blob: 20271691ba35a7e5b8a5e7e74a7e04d8ee280e77 [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.LongDeque;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.types.Type;
/**
* Parser for DML statements
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class ParserDML extends ParserDQL {
ParserDML(Session session, Scanner t) {
super(session, t);
}
/**
* Retrieves an INSERT Statement from this parse context.
*/
StatementDMQL compileInsertStatement(RangeVariable[] outerRanges) {
read();
readThis(Tokens.INTO);
boolean[] columnCheckList;
int[] columnMap;
int colCount;
Table table = readTableName();
boolean overridingUser = false;
boolean overridingSystem = false;
boolean assignsToIdentity = false;
columnCheckList = null;
columnMap = table.getColumnMap();
colCount = table.getColumnCount();
int position = getPosition();
if (!table.isInsertable() && !table.isTriggerInsertable()
&& !session.isProcessingScript) {
throw Error.error(ErrorCode.X_42545);
}
Table baseTable = table.isTriggerInsertable() ? table
: table.getBaseTable();
switch (token.tokenType) {
case Tokens.DEFAULT : {
read();
readThis(Tokens.VALUES);
Expression insertExpression = new Expression(OpTypes.ROW,
new Expression[]{});
insertExpression = new Expression(OpTypes.TABLE,
new Expression[]{
insertExpression });
columnCheckList = table.getNewColumnCheckList();
for (int i = 0; i < table.colDefaults.length; i++) {
if (table.colDefaults[i] == null
&& table.identityColumn != i) {
if (!table.getColumn(i).isGenerated()) {
throw Error.error(ErrorCode.X_42544);
}
}
}
StatementDMQL cs = new StatementInsert(session, table,
columnMap,
insertExpression,
columnCheckList,
compileContext);
return cs;
}
case Tokens.OPENBRACKET : {
int brackets = readOpenBrackets();
if (brackets == 1) {
boolean isQuery = false;
switch (token.tokenType) {
case Tokens.WITH :
case Tokens.SELECT :
case Tokens.TABLE : {
rewind(position);
isQuery = true;
break;
}
default :
}
if (isQuery) {
break;
}
OrderedHashSet columnNames = new OrderedHashSet();
readSimpleColumnNames(columnNames, table);
readThis(Tokens.CLOSEBRACKET);
colCount = columnNames.size();
columnMap = table.getColumnIndexes(columnNames);
if (token.tokenType != Tokens.VALUES
&& token.tokenType != Tokens.OVERRIDING) {
break;
}
// fall through
} else {
rewind(position);
break;
}
}
// fall through
case Tokens.OVERRIDING : {
if (token.tokenType == Tokens.OVERRIDING) {
read();
if (token.tokenType == Tokens.USER) {
read();
overridingUser = true;
} else if (token.tokenType == Tokens.SYSTEM) {
read();
overridingSystem = true;
} else {
unexpectedToken();
}
readThis(Tokens.VALUE);
if (token.tokenType != Tokens.VALUES) {
break;
}
}
}
// fall through
case Tokens.VALUES : {
read();
columnCheckList = table.getColumnCheckList(columnMap);
Expression insertExpressions =
XreadContextuallyTypedTable(colCount);
HsqlList unresolved =
insertExpressions.resolveColumnReferences(outerRanges,
null);
ExpressionColumn.checkColumnsResolved(unresolved);
insertExpressions.resolveTypes(session, null);
setParameterTypes(insertExpressions, table, columnMap);
if (table != baseTable) {
int[] baseColumnMap = table.getBaseTableColumnMap();
int[] newColumnMap = new int[columnMap.length];
ArrayUtil.projectRow(baseColumnMap, columnMap,
newColumnMap);
columnMap = newColumnMap;
}
Expression[] rowList = insertExpressions.nodes;
for (int j = 0; j < rowList.length; j++) {
Expression[] rowArgs = rowList[j].nodes;
for (int i = 0; i < rowArgs.length; i++) {
Expression e = rowArgs[i];
ColumnSchema column =
baseTable.getColumn(columnMap[i]);
if (column.isIdentity()) {
assignsToIdentity = true;
if (e.getType() != OpTypes.DEFAULT) {
if (table.identitySequence.isAlways()) {
if (!overridingUser && !overridingSystem) {
throw Error.error(ErrorCode.X_42543);
}
}
if (overridingUser) {
rowArgs[i] =
new ExpressionColumn(OpTypes.DEFAULT);
}
}
} else if (column.hasDefault()) {}
else if (column.isGenerated()) {
if (e.getType() != OpTypes.DEFAULT) {
throw Error.error(ErrorCode.X_42541);
}
} else {
if (e.getType() == OpTypes.DEFAULT) {
throw Error.error(ErrorCode.X_42544);
}
}
if (e.isUnresolvedParam()) {
e.setAttributesAsColumn(column, true);
}
}
}
if (!assignsToIdentity
&& (overridingUser || overridingSystem)) {
unexpectedTokenRequire(Tokens.T_OVERRIDING);
}
StatementDMQL cs = new StatementInsert(session, table,
columnMap,
insertExpressions,
columnCheckList,
compileContext);
return cs;
}
case Tokens.WITH :
case Tokens.SELECT :
case Tokens.TABLE : {
break;
}
default : {
throw unexpectedToken();
}
}
columnCheckList = table.getColumnCheckList(columnMap);
if (baseTable != null && table != baseTable) {
int[] baseColumnMap = table.getBaseTableColumnMap();
int[] newColumnMap = new int[columnMap.length];
ArrayUtil.projectRow(baseColumnMap, columnMap, newColumnMap);
columnMap = newColumnMap;
}
int enforcedDefaultIndex = table.getIdentityColumnIndex();
int overrideIndex = -1;
if (enforcedDefaultIndex != -1
&& ArrayUtil.find(columnMap, enforcedDefaultIndex) > -1) {
if (table.identitySequence.isAlways()) {
if (!overridingUser && !overridingSystem) {
throw Error.error(ErrorCode.X_42543);
}
}
if (overridingUser) {
overrideIndex = enforcedDefaultIndex;
}
} else if (overridingUser || overridingSystem) {
unexpectedTokenRequire(Tokens.T_OVERRIDING);
}
Type[] types = new Type[columnMap.length];
ArrayUtil.projectRow(baseTable.getColumnTypes(), columnMap, types);
QueryExpression queryExpression = XreadQueryExpression();
queryExpression.setReturningResult();
queryExpression.resolve(session, outerRanges, types);
if (colCount != queryExpression.getColumnCount()) {
throw Error.error(ErrorCode.X_42546);
}
StatementDMQL cs = new StatementInsert(session, table, columnMap,
columnCheckList,
queryExpression,
compileContext, overrideIndex);
return cs;
}
private static void setParameterTypes(Expression tableExpression,
Table table, int[] columnMap) {
for (int i = 0; i < tableExpression.nodes.length; i++) {
Expression[] list = tableExpression.nodes[i].nodes;
for (int j = 0; j < list.length; j++) {
if (list[j].isUnresolvedParam()) {
list[j].setAttributesAsColumn(
table.getColumn(columnMap[j]), true);
}
}
}
}
/**
* Creates a DELETE-type Statement from this parse context.
*/
StatementDMQL compileDeleteStatement(RangeVariable[] outerRanges) {
Expression condition = null;
boolean truncate = false;
boolean restartIdentity = false;
int statementType;
switch (token.tokenType) {
case Tokens.TRUNCATE : {
read();
readThis(Tokens.TABLE);
truncate = true;
statementType = StatementTypes.TRUNCATE;
break;
}
case Tokens.DELETE : {
read();
readThis(Tokens.FROM);
statementType = StatementTypes.DELETE_WHERE;
break;
}
default :
throw unexpectedToken();
}
RangeVariable[] rangeVariables = {
readSimpleRangeVariable(statementType) };
Table table = rangeVariables[0].getTable();
Table baseTable = table.getBaseTable();
if (truncate) {
if (table != baseTable) {
throw Error.error(ErrorCode.X_42545);
}
if (table.isTriggerDeletable()) {
// redundant
throw Error.error(ErrorCode.X_42545);
}
switch (token.tokenType) {
case Tokens.CONTINUE : {
read();
readThis(Tokens.IDENTITY);
break;
}
case Tokens.RESTART : {
read();
readThis(Tokens.IDENTITY);
restartIdentity = true;
break;
}
}
if (table.fkMainConstraints.length > 0) {
throw Error.error(ErrorCode.X_23504);
}
}
if (!truncate && token.tokenType == Tokens.WHERE) {
read();
condition = XreadBooleanValueExpression();
HsqlList unresolved =
condition.resolveColumnReferences(outerRanges, null);
unresolved = Expression.resolveColumnSet(rangeVariables,
rangeVariables.length, unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
condition.resolveTypes(session, null);
if (condition.isUnresolvedParam()) {
condition.dataType = Type.SQL_BOOLEAN;
}
if (condition.getDataType() != Type.SQL_BOOLEAN) {
throw Error.error(ErrorCode.X_42568);
}
}
if (baseTable == null) {
//
} else if (table != baseTable) {
QuerySpecification baseSelect =
((TableDerived) table).getQueryExpression().getMainSelect();
RangeVariable[] newRangeVariables =
(RangeVariable[]) ArrayUtil.duplicateArray(
baseSelect.rangeVariables);
newRangeVariables[0] = baseSelect.rangeVariables[0].duplicate();
Expression[] newBaseExprColumns =
new Expression[baseSelect.indexLimitData];
for (int i = 0; i < baseSelect.indexLimitData; i++) {
Expression e = baseSelect.exprColumns[i].duplicate();
newBaseExprColumns[i] = e;
e.replaceRangeVariables(baseSelect.rangeVariables,
newRangeVariables);
}
Expression baseQueryCondition = baseSelect.queryCondition;
if (baseQueryCondition != null) {
baseQueryCondition = baseQueryCondition.duplicate();
baseQueryCondition.replaceRangeVariables(rangeVariables,
newRangeVariables);
}
if (condition != null) {
condition =
condition.replaceColumnReferences(rangeVariables[0],
newBaseExprColumns);
}
rangeVariables = newRangeVariables;
condition = ExpressionLogical.andExpressions(baseQueryCondition,
condition);
}
if (condition != null) {
rangeVariables[0].addJoinCondition(condition);
RangeVariableResolver resolver =
new RangeVariableResolver(rangeVariables, null,
compileContext);
resolver.processConditions(session);
rangeVariables = resolver.rangeVariables;
}
StatementDMQL cs = new StatementDML(session, table, rangeVariables,
compileContext, restartIdentity,
statementType);
return cs;
}
/**
* Creates an UPDATE-type Statement from this parse context.
*/
StatementDMQL compileUpdateStatement(RangeVariable[] outerRanges) {
read();
Expression[] updateExpressions;
int[] columnMap;
boolean[] columnCheckList;
OrderedHashSet targetSet = new OrderedHashSet();
LongDeque colIndexList = new LongDeque();
HsqlArrayList exprList = new HsqlArrayList();
RangeVariable[] rangeVariables = {
readSimpleRangeVariable(StatementTypes.UPDATE_WHERE) };
Table table = rangeVariables[0].rangeTable;
Table baseTable = table.getBaseTable();
readThis(Tokens.SET);
readSetClauseList(rangeVariables, targetSet, colIndexList, exprList);
columnMap = new int[colIndexList.size()];
colIndexList.toArray(columnMap);
Expression[] targets = new Expression[targetSet.size()];
targetSet.toArray(targets);
for (int i = 0; i < targets.length; i++) {
this.resolveOuterReferencesAndTypes(outerRanges, targets[i]);
}
columnCheckList = table.getColumnCheckList(columnMap);
updateExpressions = new Expression[exprList.size()];
exprList.toArray(updateExpressions);
Expression condition = null;
if (token.tokenType == Tokens.WHERE) {
read();
condition = XreadBooleanValueExpression();
HsqlList unresolved =
condition.resolveColumnReferences(outerRanges, null);
unresolved = Expression.resolveColumnSet(rangeVariables,
rangeVariables.length, unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
condition.resolveTypes(session, null);
if (condition.isUnresolvedParam()) {
condition.dataType = Type.SQL_BOOLEAN;
}
if (condition.getDataType() != Type.SQL_BOOLEAN) {
throw Error.error(ErrorCode.X_42568);
}
}
resolveUpdateExpressions(table, rangeVariables, columnMap,
updateExpressions, outerRanges);
if (table != baseTable) {
QuerySpecification baseSelect =
((TableDerived) table).getQueryExpression().getMainSelect();
RangeVariable[] newRangeVariables =
(RangeVariable[]) ArrayUtil.duplicateArray(
baseSelect.rangeVariables);
newRangeVariables[0] = baseSelect.rangeVariables[0].duplicate();
Expression[] newBaseExprColumns =
new Expression[baseSelect.indexLimitData];
for (int i = 0; i < baseSelect.indexLimitData; i++) {
Expression e = baseSelect.exprColumns[i].duplicate();
newBaseExprColumns[i] = e;
e.replaceRangeVariables(baseSelect.rangeVariables,
newRangeVariables);
}
Expression baseQueryCondition = baseSelect.queryCondition;
if (baseQueryCondition != null) {
baseQueryCondition = baseQueryCondition.duplicate();
baseQueryCondition.replaceRangeVariables(rangeVariables,
newRangeVariables);
}
if (condition != null) {
condition =
condition.replaceColumnReferences(rangeVariables[0],
newBaseExprColumns);
}
for (int i = 0; i < updateExpressions.length; i++) {
updateExpressions[i] =
updateExpressions[i].replaceColumnReferences(
rangeVariables[0], newBaseExprColumns);
}
rangeVariables = newRangeVariables;
condition = ExpressionLogical.andExpressions(baseQueryCondition,
condition);
}
if (condition != null) {
rangeVariables[0].addJoinCondition(condition);
RangeVariableResolver resolver =
new RangeVariableResolver(rangeVariables, null,
compileContext);
resolver.processConditions(session);
rangeVariables = resolver.rangeVariables;
}
if (baseTable != null && table != baseTable) {
int[] baseColumnMap = table.getBaseTableColumnMap();
int[] newColumnMap = new int[columnMap.length];
ArrayUtil.projectRow(baseColumnMap, columnMap, newColumnMap);
columnMap = newColumnMap;
for (int i = 0; i < columnMap.length; i++) {
if (baseTable.colGenerated[columnMap[i]]) {
throw Error.error(ErrorCode.X_42513);
}
}
}
StatementDMQL cs = new StatementDML(session, targets, table,
rangeVariables, columnMap,
updateExpressions,
columnCheckList, compileContext);
return cs;
}
void resolveUpdateExpressions(Table targetTable,
RangeVariable[] rangeVariables,
int[] columnMap,
Expression[] colExpressions,
RangeVariable[] outerRanges) {
HsqlList unresolved = null;
int enforcedDefaultIndex = -1;
if (targetTable.hasIdentityColumn()
&& targetTable.identitySequence.isAlways()) {
enforcedDefaultIndex = targetTable.getIdentityColumnIndex();
}
for (int i = 0, ix = 0; i < columnMap.length; ix++) {
Expression expr = colExpressions[ix];
Expression e;
// no generated column can be updated
if (targetTable.colGenerated[columnMap[i]]) {
throw Error.error(ErrorCode.X_42513);
}
if (expr.getType() == OpTypes.ROW) {
Expression[] elements = expr.nodes;
for (int j = 0; j < elements.length; j++, i++) {
e = elements[j];
if (enforcedDefaultIndex == columnMap[i]) {
if (e.getType() != OpTypes.DEFAULT) {
throw Error.error(ErrorCode.X_42541);
}
}
if (e.isUnresolvedParam()) {
e.setAttributesAsColumn(
targetTable.getColumn(columnMap[i]), true);
} else if (e.getType() == OpTypes.DEFAULT) {
if (targetTable.colDefaults[columnMap[i]] == null
&& targetTable.identityColumn
!= columnMap[i]) {
throw Error.error(ErrorCode.X_42544);
}
} else {
unresolved = expr.resolveColumnReferences(outerRanges,
null);
unresolved =
Expression.resolveColumnSet(rangeVariables,
rangeVariables.length,
unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
unresolved = null;
e.resolveTypes(session, null);
}
}
} else if (expr.getType() == OpTypes.ROW_SUBQUERY) {
unresolved = expr.resolveColumnReferences(outerRanges, null);
unresolved = Expression.resolveColumnSet(rangeVariables,
rangeVariables.length, unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
expr.resolveTypes(session, null);
int count = expr.subQuery.queryExpression.getColumnCount();
for (int j = 0; j < count; j++, i++) {
if (enforcedDefaultIndex == columnMap[i]) {
throw Error.error(ErrorCode.X_42541);
}
}
} else {
e = expr;
if (enforcedDefaultIndex == columnMap[i]) {
if (e.getType() != OpTypes.DEFAULT) {
throw Error.error(ErrorCode.X_42541);
}
}
if (e.isUnresolvedParam()) {
e.setAttributesAsColumn(
targetTable.getColumn(columnMap[i]), true);
} else if (e.getType() == OpTypes.DEFAULT) {
if (targetTable.colDefaults[columnMap[i]] == null
&& targetTable.identityColumn != columnMap[i]) {
throw Error.error(ErrorCode.X_42544);
}
} else {
unresolved = expr.resolveColumnReferences(outerRanges,
null);
unresolved = Expression.resolveColumnSet(rangeVariables,
rangeVariables.length, unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
e.resolveTypes(session, null);
}
i++;
}
}
}
void readSetClauseList(RangeVariable[] rangeVars, OrderedHashSet targets,
LongDeque colIndexList, HsqlArrayList expressions) {
while (true) {
int degree;
if (token.tokenType == Tokens.OPENBRACKET) {
read();
int oldCount = targets.size();
readTargetSpecificationList(targets, rangeVars, colIndexList);
degree = targets.size() - oldCount;
readThis(Tokens.CLOSEBRACKET);
} else {
Expression target = XreadTargetSpecification(rangeVars,
colIndexList);
if (!targets.add(target)) {
ColumnSchema col = target.getColumn();
throw Error.error(ErrorCode.X_42579, col.getName().name);
}
degree = 1;
}
readThis(Tokens.EQUALS);
int position = getPosition();
int brackets = readOpenBrackets();
if (token.tokenType == Tokens.SELECT) {
rewind(position);
SubQuery sq = XreadSubqueryBody(false, OpTypes.ROW_SUBQUERY);
if (degree != sq.queryExpression.getColumnCount()) {
throw Error.error(ErrorCode.X_42546);
}
Expression e = new Expression(OpTypes.ROW_SUBQUERY, sq);
expressions.add(e);
if (token.tokenType == Tokens.COMMA) {
read();
continue;
}
break;
}
if (brackets > 0) {
rewind(position);
}
if (degree > 1) {
readThis(Tokens.OPENBRACKET);
Expression e = readRow();
readThis(Tokens.CLOSEBRACKET);
int rowDegree = e.getType() == OpTypes.ROW ? e.nodes.length
: 1;
if (degree != rowDegree) {
throw Error.error(ErrorCode.X_42546);
}
expressions.add(e);
} else {
Expression e = XreadValueExpressionWithContext();
expressions.add(e);
}
if (token.tokenType == Tokens.COMMA) {
read();
continue;
}
break;
}
}
/**
* Retrieves a MERGE Statement from this parse context.
*/
StatementDMQL compileMergeStatement(RangeVariable[] outerRanges) {
boolean[] insertColumnCheckList;
int[] insertColumnMap = null;
int[] updateColumnMap = null;
int[] baseUpdateColumnMap;
Table table;
RangeVariable targetRange;
RangeVariable sourceRange;
Expression mergeCondition;
Expression[] targets = null;
HsqlArrayList updateList = new HsqlArrayList();
Expression[] updateExpressions = Expression.emptyArray;
HsqlArrayList insertList = new HsqlArrayList();
Expression insertExpression = null;
read();
readThis(Tokens.INTO);
targetRange = readSimpleRangeVariable(StatementTypes.MERGE);
table = targetRange.rangeTable;
readThis(Tokens.USING);
sourceRange = readTableOrSubquery();
// parse ON search conditions
readThis(Tokens.ON);
mergeCondition = XreadBooleanValueExpression();
if (mergeCondition.getDataType() != Type.SQL_BOOLEAN) {
throw Error.error(ErrorCode.X_42568);
}
RangeVariable[] fullRangeVars = new RangeVariable[] {
sourceRange, targetRange
};
RangeVariable[] sourceRangeVars = new RangeVariable[]{ sourceRange };
RangeVariable[] targetRangeVars = new RangeVariable[]{ targetRange };
// parse WHEN clause(s) and convert lists to arrays
insertColumnMap = table.getColumnMap();
insertColumnCheckList = table.getNewColumnCheckList();
OrderedHashSet updateTargetSet = new OrderedHashSet();
OrderedHashSet insertColNames = new OrderedHashSet();
LongDeque updateColIndexList = new LongDeque();
readMergeWhen(updateColIndexList, insertColNames, updateTargetSet,
insertList, updateList, targetRangeVars, sourceRange);
if (insertList.size() > 0) {
int colCount = insertColNames.size();
if (colCount != 0) {
insertColumnMap = table.getColumnIndexes(insertColNames);
insertColumnCheckList =
table.getColumnCheckList(insertColumnMap);
}
insertExpression = (Expression) insertList.get(0);
setParameterTypes(insertExpression, table, insertColumnMap);
}
if (updateList.size() > 0) {
targets = new Expression[updateTargetSet.size()];
updateTargetSet.toArray(targets);
for (int i = 0; i < targets.length; i++) {
this.resolveOuterReferencesAndTypes(outerRanges, targets[i]);
}
updateExpressions = new Expression[updateList.size()];
updateList.toArray(updateExpressions);
updateColumnMap = new int[updateColIndexList.size()];
updateColIndexList.toArray(updateColumnMap);
}
if (updateExpressions.length != 0) {
Table baseTable = table.getBaseTable();
baseUpdateColumnMap = updateColumnMap;
if (table != baseTable) {
baseUpdateColumnMap = new int[updateColumnMap.length];
ArrayUtil.projectRow(table.getBaseTableColumnMap(),
updateColumnMap, baseUpdateColumnMap);
}
resolveUpdateExpressions(table, sourceRangeVars, updateColumnMap,
updateExpressions, outerRanges);
}
HsqlList unresolved = null;
unresolved = mergeCondition.resolveColumnReferences(fullRangeVars,
null);
ExpressionColumn.checkColumnsResolved(unresolved);
mergeCondition.resolveTypes(session, null);
if (mergeCondition.isUnresolvedParam()) {
mergeCondition.dataType = Type.SQL_BOOLEAN;
}
if (mergeCondition.getDataType() != Type.SQL_BOOLEAN) {
throw Error.error(ErrorCode.X_42568);
}
RangeVariableResolver resolver =
new RangeVariableResolver(fullRangeVars, mergeCondition,
compileContext);
resolver.processConditions(session);
fullRangeVars = resolver.rangeVariables;
if (insertExpression != null) {
unresolved =
insertExpression.resolveColumnReferences(sourceRangeVars,
unresolved);
ExpressionColumn.checkColumnsResolved(unresolved);
insertExpression.resolveTypes(session, null);
}
StatementDMQL cs = new StatementDML(session, targets, fullRangeVars,
insertColumnMap, updateColumnMap,
insertColumnCheckList,
mergeCondition, insertExpression,
updateExpressions, compileContext);
return cs;
}
/**
* Parses a WHEN clause from a MERGE statement. This can be either a
* WHEN MATCHED or WHEN NOT MATCHED clause, or both, and the appropriate
* values will be updated.
*
* If the var that is to hold the data is not null, then we already
* encountered this type of clause, which is only allowed once, and at least
* one is required.
*/
private void readMergeWhen(LongDeque updateColIndexList,
OrderedHashSet insertColumnNames,
OrderedHashSet updateTargetSet,
HsqlArrayList insertExpressions,
HsqlArrayList updateExpressions,
RangeVariable[] targetRangeVars,
RangeVariable sourceRangeVar) {
Table table = targetRangeVars[0].rangeTable;
int columnCount = table.getColumnCount();
readThis(Tokens.WHEN);
if (token.tokenType == Tokens.MATCHED) {
if (updateExpressions.size() != 0) {
throw Error.error(ErrorCode.X_42547);
}
read();
readThis(Tokens.THEN);
readThis(Tokens.UPDATE);
readThis(Tokens.SET);
readSetClauseList(targetRangeVars, updateTargetSet,
updateColIndexList, updateExpressions);
} else if (token.tokenType == Tokens.NOT) {
if (insertExpressions.size() != 0) {
throw Error.error(ErrorCode.X_42548);
}
read();
readThis(Tokens.MATCHED);
readThis(Tokens.THEN);
readThis(Tokens.INSERT);
// parse INSERT statement
// optional column list
int brackets = readOpenBrackets();
if (brackets == 1) {
readSimpleColumnNames(insertColumnNames, targetRangeVars[0]);
columnCount = insertColumnNames.size();
readThis(Tokens.CLOSEBRACKET);
brackets = 0;
}
readThis(Tokens.VALUES);
Expression e = XreadContextuallyTypedTable(columnCount);
if (e.nodes.length != 1) {
throw Error.error(ErrorCode.X_21000);
}
insertExpressions.add(e);
} else {
throw unexpectedToken();
}
if (token.tokenType == Tokens.WHEN) {
readMergeWhen(updateColIndexList, insertColumnNames,
updateTargetSet, insertExpressions,
updateExpressions, targetRangeVars, sourceRangeVar);
}
}
/**
* Retrieves a CALL Statement from this parse context.
*/
// to do call argument name and type resolution
StatementDMQL compileCallStatement(RangeVariable[] outerRanges,
boolean isStrictlyProcedure) {
read();
if (isIdentifier()) {
checkValidCatalogName(token.namePrePrefix);
RoutineSchema routineSchema =
(RoutineSchema) database.schemaManager.findSchemaObject(
token.tokenString,
session.getSchemaName(token.namePrefix),
SchemaObject.PROCEDURE);
if (routineSchema != null) {
read();
HsqlArrayList list = new HsqlArrayList();
readThis(Tokens.OPENBRACKET);
if (token.tokenType == Tokens.CLOSEBRACKET) {
read();
} else {
while (true) {
Expression e = XreadValueExpression();
list.add(e);
if (token.tokenType == Tokens.COMMA) {
read();
} else {
readThis(Tokens.CLOSEBRACKET);
break;
}
}
}
Expression[] arguments = new Expression[list.size()];
list.toArray(arguments);
Routine routine =
routineSchema.getSpecificRoutine(arguments.length);
compileContext.addProcedureCall(routine);
HsqlList unresolved = null;
for (int i = 0; i < arguments.length; i++) {
Expression e = arguments[i];
if (e.isUnresolvedParam()) {
e.setAttributesAsColumn(
routine.getParameter(i),
routine.getParameter(i).isWriteable());
} else {
int paramMode =
routine.getParameter(i).getParameterMode();
unresolved =
arguments[i].resolveColumnReferences(outerRanges,
unresolved);
if (paramMode
!= SchemaObject.ParameterModes.PARAM_IN) {
if (e.getType() != OpTypes.VARIABLE) {
throw Error.error(ErrorCode.X_42603);
}
}
}
}
ExpressionColumn.checkColumnsResolved(unresolved);
for (int i = 0; i < arguments.length; i++) {
arguments[i].resolveTypes(session, null);
if (!routine.getParameter(
i).getDataType().canBeAssignedFrom(
arguments[i].getDataType())) {
throw Error.error(ErrorCode.X_42561);
}
}
StatementDMQL cs = new StatementProcedure(session, routine,
arguments, compileContext);
return cs;
}
}
if (isStrictlyProcedure) {
throw Error.error(ErrorCode.X_42501, token.tokenString);
}
Expression expression = this.XreadValueExpression();
HsqlList unresolved = expression.resolveColumnReferences(outerRanges,
null);
ExpressionColumn.checkColumnsResolved(unresolved);
expression.resolveTypes(session, null);
// expression.paramMode = PARAM_OUT;
StatementDMQL cs = new StatementProcedure(session, expression,
compileContext);
return cs;
}
void resolveOuterReferencesAndTypes(RangeVariable[] rangeVars,
Expression e) {
HsqlList unresolved = e.resolveColumnReferences(rangeVars,
rangeVars.length, null, false);
unresolved = Expression.resolveColumnSet(rangeVars, rangeVars.length,
unresolved, null);
ExpressionColumn.checkColumnsResolved(unresolved);
e.resolveTypes(session, null);
}
}