| /* 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.HsqlNameManager.HsqlName; |
| import org.hsqldb.error.Error; |
| import org.hsqldb.error.ErrorCode; |
| import org.hsqldb.lib.ArrayUtil; |
| import org.hsqldb.lib.HsqlArrayList; |
| import org.hsqldb.lib.LongDeque; |
| import org.hsqldb.lib.OrderedHashSet; |
| import org.hsqldb.lib.OrderedIntHashSet; |
| import org.hsqldb.result.ResultProperties; |
| import org.hsqldb.types.Type; |
| import org.hsqldb.types.Types; |
| |
| /** |
| * Parser for SQL stored procedures and functions - PSM |
| * |
| * @author Fred Toussi (fredt@users dot sourceforge.net) |
| * @version 1.9.0 |
| * @since 1.9.0 |
| */ |
| public class ParserRoutine extends ParserDML { |
| |
| ParserRoutine(Session session, Scanner t) { |
| super(session, t); |
| } |
| |
| /** |
| * Reads a DEFAULT clause expression. |
| */ |
| /* |
| for datetime, the default must have the same fields |
| */ |
| Expression readDefaultClause(Type dataType) { |
| |
| Expression e = null; |
| boolean minus = false; |
| |
| if (token.tokenType == Tokens.NULL) { |
| read(); |
| |
| return new ExpressionValue(null, dataType); |
| } |
| |
| if (dataType.isDateTimeType() || dataType.isIntervalType()) { |
| switch (token.tokenType) { |
| |
| case Tokens.DATE : |
| case Tokens.TIME : |
| case Tokens.TIMESTAMP : |
| case Tokens.INTERVAL : { |
| e = readDateTimeIntervalLiteral(); |
| |
| if (e.dataType.typeCode != dataType.typeCode) { |
| |
| // error message |
| throw unexpectedToken(); |
| } |
| |
| Object defaultValue = e.getValue(session, dataType); |
| |
| return new ExpressionValue(defaultValue, dataType); |
| } |
| case Tokens.X_VALUE : |
| break; |
| |
| default : |
| e = XreadDateTimeValueFunctionOrNull(); |
| break; |
| } |
| } else if (dataType.isNumberType()) { |
| if (token.tokenType == Tokens.MINUS) { |
| read(); |
| |
| minus = true; |
| } |
| } else if (dataType.isCharacterType()) { |
| switch (token.tokenType) { |
| |
| case Tokens.USER : |
| case Tokens.CURRENT_USER : |
| case Tokens.CURRENT_ROLE : |
| case Tokens.SESSION_USER : |
| case Tokens.SYSTEM_USER : |
| case Tokens.CURRENT_CATALOG : |
| case Tokens.CURRENT_SCHEMA : |
| case Tokens.CURRENT_PATH : |
| FunctionSQL function = |
| FunctionSQL.newSQLFunction(token.tokenString, |
| compileContext); |
| |
| e = readSQLFunction(function); |
| break; |
| |
| default : |
| } |
| } else if (dataType.isBooleanType()) { |
| switch (token.tokenType) { |
| |
| case Tokens.TRUE : |
| read(); |
| |
| return Expression.EXPR_TRUE; |
| |
| case Tokens.FALSE : |
| read(); |
| |
| return Expression.EXPR_FALSE; |
| } |
| } else if (dataType.isArrayType()) { |
| e = readCollection(OpTypes.ARRAY); |
| |
| if (e.nodes.length > 0) { |
| throw Error.error(ErrorCode.X_42562); |
| } |
| |
| resolveOuterReferencesAndTypes(RangeVariable.emptyArray, e); |
| |
| return e; |
| } |
| |
| if (e != null) { |
| e.resolveTypes(session, null); |
| |
| if (dataType.typeComparisonGroup |
| != e.getDataType().typeComparisonGroup) { |
| throw Error.error(ErrorCode.X_42562); |
| } |
| |
| return e; |
| } |
| |
| if (token.tokenType == Tokens.X_VALUE) { |
| Object value = dataType.convertToType(session, token.tokenValue, |
| token.dataType); |
| |
| read(); |
| |
| if (minus) { |
| value = dataType.negate(value); |
| } |
| |
| return new ExpressionValue(value, dataType); |
| } else { |
| throw unexpectedToken(); |
| } |
| } |
| |
| Statement compileSelectSingleRowStatement(RangeVariable[] rangeVars) { |
| |
| OrderedHashSet variableNames = new OrderedHashSet(); |
| QuerySpecification select = XreadSelect(); |
| Type[] targetTypes; |
| LongDeque colIndexList = new LongDeque(); |
| |
| readThis(Tokens.INTO); |
| readTargetSpecificationList(variableNames, rangeVars, colIndexList); |
| XreadTableExpression(select); |
| select.setReturningResult(); |
| |
| int[] columnMap = new int[colIndexList.size()]; |
| |
| colIndexList.toArray(columnMap); |
| |
| Expression[] variables = new Expression[variableNames.size()]; |
| |
| variableNames.toArray(variables); |
| |
| targetTypes = new Type[variables.length]; |
| |
| for (int i = 0; i < variables.length; i++) { |
| if (variables[i].getColumn().getParameterMode() |
| == SchemaObject.ParameterModes.PARAM_IN) { |
| |
| // todo - use more specific error message |
| throw Error.error(ErrorCode.X_0U000); |
| } |
| |
| targetTypes[i] = variables[i].getDataType(); |
| } |
| |
| select.resolve(session, rangeVars, targetTypes); |
| |
| if (select.getColumnCount() != variables.length) { |
| throw Error.error(ErrorCode.X_42564, Tokens.T_INTO); |
| } |
| |
| Statement statement = new StatementSet(session, variables, select, |
| columnMap, compileContext); |
| |
| return statement; |
| } |
| |
| /** |
| * Creates SET Statement for PSM or session variables from this parse context. |
| */ |
| Statement compileSetStatement(RangeVariable rangeVars[]) { |
| |
| read(); |
| |
| OrderedHashSet targetSet = new OrderedHashSet(); |
| HsqlArrayList exprList = new HsqlArrayList(); |
| LongDeque colIndexList = new LongDeque(); |
| |
| readSetClauseList(rangeVars, targetSet, colIndexList, exprList); |
| |
| if (exprList.size() > 1) { |
| throw Error.error(ErrorCode.X_42602); |
| } |
| |
| Expression expression = (Expression) exprList.get(0); |
| |
| if (expression.getDegree() != targetSet.size()) { |
| throw Error.error(ErrorCode.X_42546, Tokens.T_SET); |
| } |
| |
| int[] 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(rangeVars, targets[i]); |
| } |
| |
| resolveOuterReferencesAndTypes(rangeVars, expression); |
| |
| for (int i = 0; i < targets.length; i++) { |
| if (targets[i].getColumn().getParameterMode() |
| == SchemaObject.ParameterModes.PARAM_IN) { |
| |
| // todo - use more specific error message |
| throw Error.error(ErrorCode.X_0U000); |
| } |
| |
| if (!targets[i].getDataType().canBeAssignedFrom( |
| expression.getNodeDataType(i))) { |
| throw Error.error(ErrorCode.X_42561); |
| } |
| } |
| |
| StatementSet cs = new StatementSet(session, targets, expression, |
| columnMap, compileContext); |
| |
| return cs; |
| } |
| |
| /** |
| * Creates SET Statement for a trigger row from this parse context. |
| */ |
| StatementDMQL compileTriggerSetStatement(Table table, |
| RangeVariable[] rangeVars) { |
| |
| read(); |
| |
| Expression[] updateExpressions; |
| int[] columnMap; |
| OrderedHashSet targetSet = new OrderedHashSet(); |
| HsqlArrayList exprList = new HsqlArrayList(); |
| RangeVariable[] targetRangeVars = new RangeVariable[]{ |
| rangeVars[TriggerDef.NEW_ROW] }; |
| LongDeque colIndexList = new LongDeque(); |
| |
| readSetClauseList(targetRangeVars, 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(RangeVariable.emptyArray, |
| targets[i]); |
| } |
| |
| updateExpressions = new Expression[exprList.size()]; |
| |
| exprList.toArray(updateExpressions); |
| resolveUpdateExpressions(table, rangeVars, columnMap, |
| updateExpressions, RangeVariable.emptyArray); |
| |
| StatementDMQL cs = new StatementSet(session, targets, table, |
| rangeVars, columnMap, |
| updateExpressions, compileContext); |
| |
| return cs; |
| } |
| |
| // SQL-invoked routine |
| StatementSchema compileCreateProcedureOrFunction() { |
| |
| int routineType; |
| boolean isAggregate = false; |
| |
| if (token.tokenType == Tokens.AGGREGATE) { |
| isAggregate = true; |
| |
| read(); |
| |
| if (token.tokenType == Tokens.PROCEDURE) { |
| throw super.unexpectedToken(); |
| } |
| } |
| |
| routineType = token.tokenType == Tokens.PROCEDURE |
| ? SchemaObject.PROCEDURE |
| : SchemaObject.FUNCTION; |
| |
| HsqlName name; |
| |
| read(); |
| |
| name = readNewSchemaObjectName(routineType, false); |
| |
| Routine routine = new Routine(routineType); |
| |
| routine.setName(name); |
| routine.setAggregate(isAggregate); |
| readThis(Tokens.OPENBRACKET); |
| |
| if (token.tokenType == Tokens.CLOSEBRACKET) { |
| read(); |
| } else { |
| while (true) { |
| ColumnSchema newcolumn = readRoutineParameter(routine, true); |
| |
| routine.addParameter(newcolumn); |
| |
| if (token.tokenType == Tokens.COMMA) { |
| read(); |
| } else { |
| readThis(Tokens.CLOSEBRACKET); |
| |
| break; |
| } |
| } |
| } |
| |
| if (routineType != SchemaObject.PROCEDURE) { |
| readThis(Tokens.RETURNS); |
| |
| if (token.tokenType == Tokens.TABLE) { |
| read(); |
| |
| TableDerived table = |
| new TableDerived(database, name, TableBase.FUNCTION_TABLE); |
| |
| readThis(Tokens.OPENBRACKET); |
| |
| for (int i = 0; ; i++) { |
| ColumnSchema newcolumn = readRoutineParameter(routine, |
| false); |
| |
| if (newcolumn.getName() == null) { |
| throw super.unexpectedToken(); |
| } |
| |
| table.addColumn(newcolumn); |
| |
| if (token.tokenType == Tokens.COMMA) { |
| read(); |
| } else { |
| readThis(Tokens.CLOSEBRACKET); |
| |
| break; |
| } |
| } |
| |
| table.createPrimaryKey(); |
| routine.setReturnTable(table); |
| } else { |
| Type type = readTypeDefinition(true); |
| |
| routine.setReturnType(type); |
| } |
| } |
| |
| readRoutineCharacteristics(routine); |
| |
| if (token.tokenType == Tokens.EXTERNAL) { |
| if (routine.getLanguage() != Routine.LANGUAGE_JAVA) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.NAME); |
| checkIsValue(Types.SQL_CHAR); |
| routine.setMethodURL((String) token.tokenValue); |
| read(); |
| |
| if (token.tokenType == Tokens.PARAMETER) { |
| read(); |
| readThis(Tokens.STYLE); |
| readThis(Tokens.JAVA); |
| } |
| } else { |
| startRecording(); |
| |
| Statement statement = compileSQLProcedureStatementOrNull(routine, |
| null); |
| |
| if (statement == null) { |
| throw unexpectedToken(); |
| } |
| |
| Token[] tokenisedStatement = getRecordedStatement(); |
| String sql = Token.getSQL(tokenisedStatement); |
| |
| statement.setSQL(sql); |
| routine.setProcedure(statement); |
| } |
| |
| Object[] args = new Object[]{ routine }; |
| String sql = getLastPart(); |
| StatementSchema cs = new StatementSchema(sql, |
| StatementTypes.CREATE_ROUTINE, args); |
| |
| return cs; |
| } |
| |
| private void readRoutineCharacteristics(Routine routine) { |
| |
| OrderedIntHashSet set = new OrderedIntHashSet(); |
| boolean end = false; |
| |
| while (!end) { |
| switch (token.tokenType) { |
| |
| case Tokens.LANGUAGE : { |
| if (!set.add(Tokens.LANGUAGE)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| |
| if (token.tokenType == Tokens.JAVA) { |
| read(); |
| routine.setLanguage(Routine.LANGUAGE_JAVA); |
| } else if (token.tokenType == Tokens.SQL) { |
| read(); |
| routine.setLanguage(Routine.LANGUAGE_SQL); |
| } else { |
| throw unexpectedToken(); |
| } |
| |
| break; |
| } |
| case Tokens.PARAMETER : { |
| if (!set.add(Tokens.PARAMETER)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.STYLE); |
| |
| if (token.tokenType == Tokens.JAVA) { |
| read(); |
| routine.setParameterStyle(Routine.PARAM_STYLE_JAVA); |
| } else { |
| readThis(Tokens.SQL); |
| routine.setParameterStyle(Routine.PARAM_STYLE_SQL); |
| } |
| |
| break; |
| } |
| case Tokens.SPECIFIC : { |
| if (!set.add(Tokens.SPECIFIC)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| |
| HsqlName name = |
| readNewSchemaObjectName(SchemaObject.SPECIFIC_ROUTINE, |
| false); |
| |
| routine.setSpecificName(name); |
| |
| break; |
| } |
| case Tokens.DETERMINISTIC : { |
| if (!set.add(Tokens.DETERMINISTIC)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| routine.setDeterministic(true); |
| |
| break; |
| } |
| case Tokens.NOT : { |
| if (!set.add(Tokens.DETERMINISTIC)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.DETERMINISTIC); |
| routine.setDeterministic(false); |
| |
| break; |
| } |
| case Tokens.MODIFIES : { |
| if (!set.add(Tokens.SQL)) { |
| throw unexpectedToken(); |
| } |
| |
| if (routine.getType() == SchemaObject.FUNCTION) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SQL); |
| readThis(Tokens.DATA); |
| routine.setDataImpact(Routine.MODIFIES_SQL); |
| |
| break; |
| } |
| case Tokens.NO : { |
| if (!set.add(Tokens.SQL)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SQL); |
| routine.setDataImpact(Routine.NO_SQL); |
| |
| break; |
| } |
| case Tokens.READS : { |
| if (!set.add(Tokens.SQL)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SQL); |
| readThis(Tokens.DATA); |
| routine.setDataImpact(Routine.READS_SQL); |
| |
| break; |
| } |
| case Tokens.CONTAINS : { |
| if (!set.add(Tokens.SQL)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SQL); |
| routine.setDataImpact(Routine.CONTAINS_SQL); |
| |
| break; |
| } |
| case Tokens.RETURNS : { |
| if (!set.add(Tokens.NULL) || routine.isProcedure()) { |
| throw unexpectedToken(); |
| } |
| |
| if (routine.isAggregate()) { |
| throw Error.error(ErrorCode.X_42604, |
| token.tokenString); |
| } |
| |
| read(); |
| readThis(Tokens.NULL); |
| readThis(Tokens.ON); |
| readThis(Tokens.NULL); |
| readThis(Tokens.INPUT); |
| routine.setNullInputOutput(true); |
| |
| break; |
| } |
| case Tokens.CALLED : { |
| if (!set.add(Tokens.NULL) || routine.isProcedure()) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.ON); |
| readThis(Tokens.NULL); |
| readThis(Tokens.INPUT); |
| routine.setNullInputOutput(false); |
| |
| break; |
| } |
| case Tokens.DYNAMIC : { |
| if (!set.add(Tokens.RESULT) || routine.isFunction()) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.RESULT); |
| readThis(Tokens.SETS); |
| readBigint(); |
| |
| break; |
| } |
| case Tokens.NEW : { |
| if (routine.getType() == SchemaObject.FUNCTION |
| || !set.add(Tokens.SAVEPOINT)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SAVEPOINT); |
| readThis(Tokens.LEVEL); |
| routine.setNewSavepointLevel(true); |
| |
| break; |
| } |
| case Tokens.OLD : { |
| if (routine.getType() == SchemaObject.FUNCTION |
| || !set.add(Tokens.SAVEPOINT)) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| readThis(Tokens.SAVEPOINT); |
| readThis(Tokens.LEVEL); |
| routine.setNewSavepointLevel(false); |
| |
| throw super.unsupportedFeature(Tokens.T_OLD); |
| |
| // break; |
| } |
| default : |
| end = true; |
| break; |
| } |
| } |
| } |
| |
| /* |
| <SQL control statement> ::= |
| <call statement> |
| | <return statement> |
| |
| <compound statement> |
| <case statement> |
| <if statement> |
| <iterate statement> |
| <leave statement> |
| <loop statement> |
| <while statement> |
| <repeat statement> |
| <for statement> |
| <assignment statement> SET (,,,) = (,,,) or SET a = b |
| |
| |
| */ |
| private Object[] readLocalDeclarationList(Routine routine, |
| StatementCompound context) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| |
| while (token.tokenType == Tokens.DECLARE) { |
| Object var = readLocalVariableDeclarationOrNull(); |
| |
| if (var == null) { |
| var = compileLocalHandlerDeclarationOrNull(routine, context); |
| } |
| |
| if (var instanceof ColumnSchema[]) { |
| list.addAll((Object[]) var); |
| } else { |
| list.add(var); |
| } |
| } |
| |
| Object[] declarations = new Object[list.size()]; |
| |
| list.toArray(declarations); |
| |
| return declarations; |
| } |
| |
| ColumnSchema[] readLocalVariableDeclarationOrNull() { |
| |
| int position = super.getPosition(); |
| Type type; |
| HsqlName[] names = HsqlName.emptyArray; |
| |
| try { |
| readThis(Tokens.DECLARE); |
| |
| if (isReservedKey()) { |
| rewind(position); |
| |
| return null; |
| } |
| |
| while (true) { |
| names = (HsqlName[]) ArrayUtil.resizeArray(names, |
| names.length + 1); |
| names[names.length - 1] = |
| super.readNewSchemaObjectName(SchemaObject.VARIABLE, |
| false); |
| |
| if (token.tokenType == Tokens.COMMA) { |
| read(); |
| } else { |
| break; |
| } |
| } |
| |
| type = readTypeDefinition(true); |
| } catch (Exception e) { |
| |
| // may be cursor |
| rewind(position); |
| |
| return null; |
| } |
| |
| Expression def = null; |
| |
| if (token.tokenType == Tokens.DEFAULT) { |
| read(); |
| |
| def = readDefaultClause(type); |
| } |
| |
| ColumnSchema[] variable = new ColumnSchema[names.length]; |
| |
| for (int i = 0; i < names.length; i++) { |
| variable[i] = new ColumnSchema(names[i], type, true, false, def); |
| |
| variable[i].setParameterMode( |
| SchemaObject.ParameterModes.PARAM_INOUT); |
| } |
| |
| readThis(Tokens.SEMICOLON); |
| |
| return variable; |
| } |
| |
| private StatementHandler compileLocalHandlerDeclarationOrNull( |
| Routine routine, StatementCompound context) { |
| |
| int handlerType; |
| |
| readThis(Tokens.DECLARE); |
| |
| switch (token.tokenType) { |
| |
| case Tokens.CONTINUE : |
| read(); |
| |
| handlerType = StatementHandler.CONTINUE; |
| break; |
| |
| case Tokens.EXIT : |
| read(); |
| |
| handlerType = StatementHandler.EXIT; |
| break; |
| |
| case Tokens.UNDO : |
| read(); |
| |
| handlerType = StatementHandler.UNDO; |
| break; |
| |
| default : |
| throw unexpectedToken(); |
| } |
| |
| readThis(Tokens.HANDLER); |
| readThis(Tokens.FOR); |
| |
| StatementHandler handler = new StatementHandler(handlerType); |
| boolean end = false; |
| boolean start = true; |
| |
| while (!end) { |
| int conditionType = StatementHandler.NONE; |
| |
| switch (token.tokenType) { |
| |
| case Tokens.COMMA : |
| if (start) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| |
| start = true; |
| break; |
| |
| case Tokens.SQLSTATE : |
| conditionType = StatementHandler.SQL_STATE; |
| |
| // fall through |
| case Tokens.SQLEXCEPTION : |
| if (conditionType == StatementHandler.NONE) { |
| conditionType = StatementHandler.SQL_EXCEPTION; |
| } |
| |
| // fall through |
| case Tokens.SQLWARNING : |
| if (conditionType == StatementHandler.NONE) { |
| conditionType = StatementHandler.SQL_WARNING; |
| } |
| |
| // fall through |
| case Tokens.NOT : |
| if (conditionType == StatementHandler.NONE) { |
| conditionType = StatementHandler.SQL_NOT_FOUND; |
| } |
| |
| if (!start) { |
| throw unexpectedToken(); |
| } |
| |
| start = false; |
| |
| read(); |
| |
| if (conditionType == StatementHandler.SQL_NOT_FOUND) { |
| readThis(Tokens.FOUND); |
| } else if (conditionType == StatementHandler.SQL_STATE) { |
| String sqlState = parseSQLStateValue(); |
| |
| handler.addConditionState(sqlState); |
| |
| break; |
| } |
| |
| handler.addConditionType(conditionType); |
| break; |
| |
| default : |
| if (start) { |
| throw unexpectedToken(); |
| } |
| |
| end = true; |
| break; |
| } |
| } |
| |
| if (token.tokenType == Tokens.SEMICOLON) { |
| read(); |
| } else { |
| Statement e = compileSQLProcedureStatementOrNull(routine, context); |
| |
| if (e == null) { |
| throw unexpectedToken(); |
| } |
| |
| readThis(Tokens.SEMICOLON); |
| handler.addStatement(e); |
| } |
| |
| return handler; |
| } |
| |
| String parseSQLStateValue() { |
| |
| readIfThis(Tokens.VALUE); |
| checkIsValue(Types.SQL_CHAR); |
| |
| String sqlState = token.tokenString; |
| |
| if (token.tokenString.length() != 5) { |
| throw Error.error(ErrorCode.X_42607); |
| } |
| |
| read(); |
| |
| return sqlState; |
| } |
| |
| private Statement compileCompoundStatement(Routine routine, |
| StatementCompound context, HsqlName label) { |
| |
| final boolean atomic = true; |
| |
| readThis(Tokens.BEGIN); |
| readThis(Tokens.ATOMIC); |
| |
| StatementCompound statement = |
| new StatementCompound(StatementTypes.BEGIN_END, label); |
| |
| statement.setAtomic(atomic); |
| statement.setRoot(routine); |
| statement.setParent(context); |
| |
| Object[] declarations = readLocalDeclarationList(routine, context); |
| |
| statement.setLocalDeclarations(declarations); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| statement); |
| |
| statement.setStatements(statements); |
| readThis(Tokens.END); |
| |
| if (isSimpleName() && !isReservedKey()) { |
| if (label == null) { |
| throw unexpectedToken(); |
| } |
| |
| if (!label.name.equals(token.tokenString)) { |
| throw Error.error(ErrorCode.X_42508, token.tokenString); |
| } |
| |
| read(); |
| } |
| |
| return statement; |
| } |
| |
| private Statement[] compileSQLProcedureStatementList(Routine routine, |
| StatementCompound context) { |
| |
| Statement e; |
| HsqlArrayList list = new HsqlArrayList(); |
| |
| while (true) { |
| e = compileSQLProcedureStatementOrNull(routine, context); |
| |
| if (e == null) { |
| break; |
| } |
| |
| readThis(Tokens.SEMICOLON); |
| list.add(e); |
| } |
| |
| if (list.size() == 0) { |
| throw unexpectedToken(); |
| } |
| |
| Statement[] statements = new Statement[list.size()]; |
| |
| list.toArray(statements); |
| |
| return statements; |
| } |
| |
| Statement compileSQLProcedureStatementOrNull(Routine routine, |
| StatementCompound context) { |
| |
| Statement cs = null; |
| HsqlName label = null; |
| RangeVariable[] rangeVariables = context == null |
| ? routine.getParameterRangeVariables() |
| : context.getRangeVariables(); |
| |
| if (!routine.isTrigger() && isSimpleName() && !isReservedKey()) { |
| label = readNewSchemaObjectName(SchemaObject.LABEL, false); |
| |
| readThis(Tokens.COLON); |
| } |
| |
| compileContext.reset(); |
| |
| switch (token.tokenType) { |
| |
| // data |
| case Tokens.SELECT : { |
| if (routine.dataImpact == Routine.CONTAINS_SQL) { |
| throw Error.error(ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileSelectSingleRowStatement(rangeVariables); |
| |
| break; |
| } |
| |
| // data change |
| case Tokens.INSERT : |
| if (routine.dataImpact != Routine.MODIFIES_SQL) { |
| throw Error.error(ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileInsertStatement(rangeVariables); |
| break; |
| |
| case Tokens.UPDATE : |
| if (routine.dataImpact != Routine.MODIFIES_SQL) { |
| throw Error.error(ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileUpdateStatement(rangeVariables); |
| break; |
| |
| case Tokens.DELETE : |
| case Tokens.TRUNCATE : |
| if (routine.dataImpact != Routine.MODIFIES_SQL) { |
| throw Error.error(ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileDeleteStatement(rangeVariables); |
| break; |
| |
| case Tokens.MERGE : |
| if (routine.dataImpact != Routine.MODIFIES_SQL) { |
| throw Error.error(ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileMergeStatement(rangeVariables); |
| break; |
| |
| case Tokens.SET : |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| if (routine.isTrigger()) { |
| if (routine.triggerOperation |
| == StatementTypes.DELETE_WHERE) { |
| throw unexpectedToken(); |
| } |
| |
| if (routine.triggerType != TriggerDef.BEFORE) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileTriggerSetStatement(routine.triggerTable, |
| rangeVariables); |
| } else { |
| cs = compileSetStatement(rangeVariables); |
| } |
| break; |
| |
| // control |
| case Tokens.CALL : { |
| if (label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileCallStatement(rangeVariables, true); |
| |
| Routine proc = ((StatementProcedure) cs).procedure; |
| |
| if (proc != null) { |
| switch (routine.dataImpact) { |
| |
| case Routine.CONTAINS_SQL : { |
| if (proc.dataImpact == Routine.READS_SQL |
| || proc.dataImpact |
| == Routine.MODIFIES_SQL) { |
| throw Error.error( |
| ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| } |
| case Routine.READS_SQL : { |
| if (routine.dataImpact == Routine.MODIFIES_SQL) { |
| throw Error.error( |
| ErrorCode.X_42608, |
| routine.getDataImpactString()); |
| } |
| } |
| } |
| } |
| |
| break; |
| } |
| case Tokens.RETURN : { |
| if (routine.isTrigger() || label != null) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| |
| cs = compileReturnValue(routine, context); |
| |
| break; |
| } |
| case Tokens.BEGIN : { |
| cs = compileCompoundStatement(routine, context, label); |
| |
| break; |
| } |
| case Tokens.WHILE : { |
| if (routine.isTrigger()) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileWhile(routine, context, label); |
| |
| break; |
| } |
| case Tokens.REPEAT : { |
| if (routine.isTrigger()) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileRepeat(routine, context, label); |
| |
| break; |
| } |
| case Tokens.LOOP : { |
| if (routine.isTrigger()) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileLoop(routine, context, label); |
| |
| break; |
| } |
| case Tokens.FOR : { |
| if (routine.isTrigger()) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileFor(routine, context, label); |
| |
| break; |
| } |
| case Tokens.ITERATE : { |
| if (routine.isTrigger() || label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileIterate(); |
| |
| break; |
| } |
| case Tokens.LEAVE : { |
| if (routine.isTrigger() || label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileLeave(routine, context); |
| |
| break; |
| } |
| case Tokens.IF : { |
| cs = compileIf(routine, context); |
| |
| break; |
| } |
| case Tokens.CASE : { |
| cs = compileCase(routine, context); |
| |
| break; |
| } |
| case Tokens.SIGNAL : { |
| if (routine.isTrigger() || label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileSignal(routine, context, label); |
| |
| break; |
| } |
| case Tokens.RESIGNAL : { |
| if (routine.isTrigger() || label != null) { |
| throw unexpectedToken(); |
| } |
| |
| cs = compileResignal(routine, context, label); |
| |
| break; |
| } |
| default : |
| return null; |
| } |
| |
| cs.setRoot(routine); |
| cs.setParent(context); |
| |
| return cs; |
| } |
| |
| private Statement compileReturnValue(Routine routine, |
| StatementCompound context) { |
| |
| Expression e = XreadValueExpressionOrNull(); |
| |
| if (e == null) { |
| checkIsValue(); |
| |
| if (token.tokenValue == null) { |
| e = new ExpressionValue(null, null); |
| } |
| } |
| |
| resolveOuterReferencesAndTypes(routine, context, e); |
| |
| if (routine.isProcedure()) { |
| throw Error.error(ErrorCode.X_42602); |
| } |
| |
| return new StatementExpression(session, compileContext, |
| StatementTypes.RETURN, e); |
| } |
| |
| private Statement compileIterate() { |
| |
| readThis(Tokens.ITERATE); |
| |
| HsqlName label = readNewSchemaObjectName(SchemaObject.LABEL, false); |
| |
| return new StatementSimple(StatementTypes.ITERATE, label); |
| } |
| |
| private Statement compileLeave(Routine routine, |
| StatementCompound context) { |
| |
| readThis(Tokens.LEAVE); |
| |
| HsqlName label = readNewSchemaObjectName(SchemaObject.LABEL, false); |
| |
| return new StatementSimple(StatementTypes.LEAVE, label); |
| } |
| |
| private Statement compileWhile(Routine routine, StatementCompound context, |
| HsqlName label) { |
| |
| readThis(Tokens.WHILE); |
| |
| Expression e = XreadBooleanValueExpression(); |
| |
| resolveOuterReferencesAndTypes(routine, context, e); |
| |
| StatementExpression condition = new StatementExpression(session, |
| compileContext, StatementTypes.CONDITION, e); |
| |
| readThis(Tokens.DO); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| context); |
| |
| readThis(Tokens.END); |
| readThis(Tokens.WHILE); |
| |
| if (isSimpleName() && !isReservedKey()) { |
| if (label == null) { |
| throw unexpectedToken(); |
| } |
| |
| if (!label.name.equals(token.tokenString)) { |
| throw Error.error(ErrorCode.X_42508, token.tokenString); |
| } |
| |
| read(); |
| } |
| |
| StatementCompound statement = |
| new StatementCompound(StatementTypes.WHILE, label); |
| |
| statement.setStatements(statements); |
| statement.setCondition(condition); |
| |
| return statement; |
| } |
| |
| private Statement compileRepeat(Routine routine, |
| StatementCompound context, |
| HsqlName label) { |
| |
| readThis(Tokens.REPEAT); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| context); |
| |
| readThis(Tokens.UNTIL); |
| |
| Expression e = XreadBooleanValueExpression(); |
| |
| resolveOuterReferencesAndTypes(routine, context, e); |
| |
| StatementExpression condition = new StatementExpression(session, |
| compileContext, StatementTypes.CONDITION, e); |
| |
| readThis(Tokens.END); |
| readThis(Tokens.REPEAT); |
| |
| if (isSimpleName() && !isReservedKey()) { |
| if (label == null) { |
| throw unexpectedToken(); |
| } |
| |
| if (!label.name.equals(token.tokenString)) { |
| throw Error.error(ErrorCode.X_42508, token.tokenString); |
| } |
| |
| read(); |
| } |
| |
| StatementCompound statement = |
| new StatementCompound(StatementTypes.REPEAT, label); |
| |
| statement.setStatements(statements); |
| statement.setCondition(condition); |
| |
| return statement; |
| } |
| |
| private Statement compileLoop(Routine routine, StatementCompound context, |
| HsqlName label) { |
| |
| readThis(Tokens.LOOP); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| context); |
| |
| readThis(Tokens.END); |
| readThis(Tokens.LOOP); |
| |
| if (isSimpleName() && !isReservedKey()) { |
| if (label == null) { |
| throw unexpectedToken(); |
| } |
| |
| if (!label.name.equals(token.tokenString)) { |
| throw Error.error(ErrorCode.X_42508, token.tokenString); |
| } |
| |
| read(); |
| } |
| |
| StatementCompound result = new StatementCompound(StatementTypes.LOOP, |
| label); |
| |
| result.setStatements(statements); |
| |
| return result; |
| } |
| |
| private Statement compileFor(Routine routine, StatementCompound context, |
| HsqlName label) { |
| |
| readThis(Tokens.FOR); |
| |
| Statement cursorStatement = |
| compileCursorSpecification(ResultProperties.defaultPropsValue); |
| |
| readThis(Tokens.DO); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| context); |
| |
| readThis(Tokens.END); |
| readThis(Tokens.FOR); |
| |
| if (isSimpleName() && !isReservedKey()) { |
| if (label == null) { |
| throw unexpectedToken(); |
| } |
| |
| if (!label.name.equals(token.tokenString)) { |
| throw Error.error(ErrorCode.X_42508, token.tokenString); |
| } |
| |
| read(); |
| } |
| |
| StatementCompound result = new StatementCompound(StatementTypes.FOR, |
| label); |
| |
| result.setLoopStatement(cursorStatement); |
| result.setStatements(statements); |
| |
| return result; |
| } |
| |
| private Statement compileIf(Routine routine, StatementCompound context) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| |
| readThis(Tokens.IF); |
| |
| Expression e = XreadBooleanValueExpression(); |
| |
| resolveOuterReferencesAndTypes(routine, context, e); |
| |
| Statement statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, e); |
| |
| list.add(statement); |
| readThis(Tokens.THEN); |
| |
| Statement[] statements = compileSQLProcedureStatementList(routine, |
| context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| |
| while (token.tokenType == Tokens.ELSEIF) { |
| read(); |
| |
| e = XreadBooleanValueExpression(); |
| |
| resolveOuterReferencesAndTypes(routine, context, e); |
| |
| statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, e); |
| |
| list.add(statement); |
| readThis(Tokens.THEN); |
| |
| statements = compileSQLProcedureStatementList(routine, context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| } |
| |
| if (token.tokenType == Tokens.ELSE) { |
| read(); |
| |
| e = Expression.EXPR_TRUE; |
| statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, e); |
| |
| list.add(statement); |
| |
| statements = compileSQLProcedureStatementList(routine, context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| } |
| |
| readThis(Tokens.END); |
| readThis(Tokens.IF); |
| |
| statements = new Statement[list.size()]; |
| |
| list.toArray(statements); |
| |
| StatementCompound result = new StatementCompound(StatementTypes.IF, |
| null); |
| |
| result.setStatements(statements); |
| |
| return result; |
| } |
| |
| private Statement compileCase(Routine routine, StatementCompound context) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| Expression condition = null; |
| Statement statement; |
| Statement[] statements; |
| |
| readThis(Tokens.CASE); |
| |
| if (token.tokenType == Tokens.WHEN) { |
| list = readCaseWhen(routine, context); |
| } else { |
| list = readSimpleCaseWhen(routine, context); |
| } |
| |
| if (token.tokenType == Tokens.ELSE) { |
| read(); |
| |
| condition = Expression.EXPR_TRUE; |
| statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, |
| condition); |
| |
| list.add(statement); |
| |
| statements = compileSQLProcedureStatementList(routine, context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| } |
| |
| readThis(Tokens.END); |
| readThis(Tokens.CASE); |
| |
| statements = new Statement[list.size()]; |
| |
| list.toArray(statements); |
| |
| StatementCompound result = new StatementCompound(StatementTypes.IF, |
| null); |
| |
| result.setStatements(statements); |
| |
| return result; |
| } |
| |
| private HsqlArrayList readSimpleCaseWhen(Routine routine, |
| StatementCompound context) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| Expression condition = null; |
| Statement statement; |
| Statement[] statements; |
| Expression predicand = XreadRowValuePredicand(); |
| |
| do { |
| readThis(Tokens.WHEN); |
| |
| do { |
| Expression newCondition = XreadPredicateRightPart(predicand); |
| |
| if (predicand == newCondition) { |
| newCondition = |
| new ExpressionLogical(predicand, |
| XreadRowValuePredicand()); |
| } |
| |
| resolveOuterReferencesAndTypes(routine, context, newCondition); |
| |
| if (condition == null) { |
| condition = newCondition; |
| } else { |
| condition = new ExpressionLogical(OpTypes.OR, condition, |
| newCondition); |
| } |
| |
| if (token.tokenType == Tokens.COMMA) { |
| read(); |
| } else { |
| break; |
| } |
| } while (true); |
| |
| statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, |
| condition); |
| |
| list.add(statement); |
| readThis(Tokens.THEN); |
| |
| statements = compileSQLProcedureStatementList(routine, context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| |
| if (token.tokenType != Tokens.WHEN) { |
| break; |
| } |
| } while (true); |
| |
| return list; |
| } |
| |
| private HsqlArrayList readCaseWhen(Routine routine, |
| StatementCompound context) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| Expression condition = null; |
| Statement statement; |
| Statement[] statements; |
| |
| do { |
| readThis(Tokens.WHEN); |
| |
| condition = XreadBooleanValueExpression(); |
| |
| resolveOuterReferencesAndTypes(routine, context, condition); |
| |
| statement = new StatementExpression(session, compileContext, |
| StatementTypes.CONDITION, |
| condition); |
| |
| list.add(statement); |
| readThis(Tokens.THEN); |
| |
| statements = compileSQLProcedureStatementList(routine, context); |
| |
| for (int i = 0; i < statements.length; i++) { |
| list.add(statements[i]); |
| } |
| |
| if (token.tokenType != Tokens.WHEN) { |
| break; |
| } |
| } while (true); |
| |
| return list; |
| } |
| |
| private Statement compileSignal(Routine routine, |
| StatementCompound context, |
| HsqlName label) { |
| |
| readThis(Tokens.SIGNAL); |
| readThis(Tokens.SQLSTATE); |
| |
| String sqlState = parseSQLStateValue(); |
| StatementSimple cs = new StatementSimple(StatementTypes.SIGNAL, |
| sqlState); |
| |
| return cs; |
| } |
| |
| private Statement compileResignal(Routine routine, |
| StatementCompound context, |
| HsqlName label) { |
| |
| String sqlState = null; |
| |
| readThis(Tokens.RESIGNAL); |
| |
| if (readIfThis(Tokens.SQLSTATE)) { |
| sqlState = parseSQLStateValue(); |
| } |
| |
| StatementSimple cs = new StatementSimple(StatementTypes.RESIGNAL, |
| sqlState); |
| |
| return cs; |
| } |
| |
| private ColumnSchema readRoutineParameter(Routine routine, |
| boolean isParam) { |
| |
| HsqlName hsqlName = null; |
| byte parameterMode = SchemaObject.ParameterModes.PARAM_IN; |
| |
| if (isParam) { |
| switch (token.tokenType) { |
| |
| case Tokens.IN : |
| read(); |
| break; |
| |
| case Tokens.OUT : |
| if (routine.getType() != SchemaObject.PROCEDURE) { |
| throw unexpectedToken(); |
| } |
| |
| read(); |
| |
| parameterMode = SchemaObject.ParameterModes.PARAM_OUT; |
| break; |
| |
| case Tokens.INOUT : |
| if (routine.getType() != SchemaObject.PROCEDURE) { |
| if (!routine.isAggregate()) { |
| throw unexpectedToken(); |
| } |
| } |
| |
| read(); |
| |
| parameterMode = SchemaObject.ParameterModes.PARAM_INOUT; |
| break; |
| |
| default : |
| } |
| } |
| |
| if (!isReservedKey()) { |
| hsqlName = readNewDependentSchemaObjectName(routine.getName(), |
| SchemaObject.PARAMETER); |
| } |
| |
| Type typeObject = readTypeDefinition(true); |
| ColumnSchema column = new ColumnSchema(hsqlName, typeObject, true, |
| false, null); |
| |
| if (isParam) { |
| column.setParameterMode(parameterMode); |
| } |
| |
| return column; |
| } |
| |
| void resolveOuterReferencesAndTypes(Routine routine, |
| StatementCompound context, |
| Expression e) { |
| |
| RangeVariable[] rangeVars = routine.getParameterRangeVariables(); |
| |
| if (context != null) { |
| rangeVars = context.getRangeVariables(); |
| } |
| |
| resolveOuterReferencesAndTypes(rangeVars, e); |
| } |
| } |