1 /* Copyright (c) 2001-2016, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 package org.hsqldb;
33 
34 import org.hsqldb.HsqlNameManager.HsqlName;
35 import org.hsqldb.HsqlNameManager.SimpleName;
36 import org.hsqldb.error.Error;
37 import org.hsqldb.error.ErrorCode;
38 import org.hsqldb.lib.ArrayUtil;
39 import org.hsqldb.lib.HsqlArrayList;
40 import org.hsqldb.lib.HsqlList;
41 import org.hsqldb.lib.LongDeque;
42 import org.hsqldb.lib.OrderedHashSet;
43 import org.hsqldb.lib.OrderedIntHashSet;
44 import org.hsqldb.result.ResultProperties;
45 import org.hsqldb.types.ArrayType;
46 import org.hsqldb.types.RowType;
47 import org.hsqldb.types.Type;
48 import org.hsqldb.types.Types;
49 
50 /**
51  * Parser for SQL stored procedures and functions - PSM
52  *
53  * @author Fred Toussi (fredt@users dot sourceforge.net)
54  * @version 2.3.4
55  * @since 1.9.0
56  */
57 public class ParserRoutine extends ParserTable {
58 
ParserRoutine(Session session, Scanner t)59     ParserRoutine(Session session, Scanner t) {
60         super(session, t);
61     }
62 
compileOpenCursorStatement(StatementCompound context)63     Statement compileOpenCursorStatement(StatementCompound context) {
64 
65         readThis(Tokens.OPEN);
66         checkIsSimpleName();
67 
68         String tokenString = token.tokenString;
69 
70         read();
71 
72         for (int i = 0; i < context.cursors.length; i++) {
73             if (context.cursors[i].getCursorName().name.equals(tokenString)) {
74                 return context.cursors[i];
75             }
76         }
77 
78         throw Error.parseError(ErrorCode.X_34000, null,
79                                scanner.getLineNumber());
80     }
81 
compileSelectSingleRowStatement(RangeGroup[] rangeGroups)82     Statement compileSelectSingleRowStatement(RangeGroup[] rangeGroups) {
83 
84         OrderedHashSet     variableNames = new OrderedHashSet();
85         Type[]             targetTypes;
86         LongDeque          colIndexList = new LongDeque();
87         QuerySpecification select;
88 
89         compileContext.setOuterRanges(rangeGroups);
90 
91         select = XreadSelect();
92 
93         readThis(Tokens.INTO);
94 
95         RangeVariable[] ranges = rangeGroups[0].getRangeVariables();
96 
97         readTargetSpecificationList(variableNames, ranges, colIndexList);
98         XreadTableExpression(select);
99         select.setReturningResult();
100 
101         int[] columnMap = new int[colIndexList.size()];
102 
103         colIndexList.toArray(columnMap);
104 
105         Expression[] variables = new Expression[variableNames.size()];
106 
107         variableNames.toArray(variables);
108 
109         targetTypes = new Type[variables.length];
110 
111         for (int i = 0; i < variables.length; i++) {
112             if (variables[i].getColumn().getParameterMode()
113                     == SchemaObject.ParameterModes.PARAM_IN) {
114 
115                 // todo - use more specific error message
116                 throw Error.parseError(ErrorCode.X_0U000, null,
117                                        scanner.getLineNumber());
118             }
119 
120             targetTypes[i] = variables[i].getDataType();
121         }
122 
123         select.setReturningResult();
124         select.resolve(session, rangeGroups, targetTypes);
125 
126         if (select.getColumnCount() != variables.length) {
127             throw Error.error(ErrorCode.X_42564, Tokens.T_INTO);
128         }
129 
130         Statement statement = new StatementSet(session, variables, select,
131                                                columnMap, compileContext);
132 
133         return statement;
134     }
135 
136     /**
137      * Creates GET DIAGNOSTICS.
138      */
compileGetStatement(RangeGroup[] rangeGroups)139     Statement compileGetStatement(RangeGroup[] rangeGroups) {
140 
141         read();
142         readThis(Tokens.DIAGNOSTICS);
143 
144         OrderedHashSet  targetSet    = new OrderedHashSet();
145         HsqlArrayList   exprList     = new HsqlArrayList();
146         LongDeque       colIndexList = new LongDeque();
147         RangeVariable[] rangeVars    = rangeGroups[0].getRangeVariables();
148 
149         readGetClauseList(rangeVars, targetSet, colIndexList, exprList);
150 
151         if (exprList.size() > 1) {
152             throw Error.parseError(ErrorCode.X_42602, null,
153                                    scanner.getLineNumber());
154         }
155 
156         Expression expression = (Expression) exprList.get(0);
157 
158         if (expression.getDegree() != targetSet.size()) {
159             throw Error.error(ErrorCode.X_42546, Tokens.T_SET);
160         }
161 
162         int[] columnMap = new int[colIndexList.size()];
163 
164         colIndexList.toArray(columnMap);
165 
166         Expression[] targets = new Expression[targetSet.size()];
167 
168         targetSet.toArray(targets);
169 
170         for (int i = 0; i < targets.length; i++) {
171             resolveOuterReferencesAndTypes(rangeGroups, targets[i]);
172         }
173 
174         resolveOuterReferencesAndTypes(rangeGroups, expression);
175 
176         for (int i = 0; i < targets.length; i++) {
177             if (targets[i].getColumn().getParameterMode()
178                     == SchemaObject.ParameterModes.PARAM_IN) {
179 
180                 // todo - use more specific error message
181                 throw Error.parseError(ErrorCode.X_0U000, null,
182                                        scanner.getLineNumber());
183             }
184 
185             if (!targets[i].getDataType().canBeAssignedFrom(
186                     expression.getNodeDataType(i))) {
187                 throw Error.parseError(ErrorCode.X_42561, null,
188                                        scanner.getLineNumber());
189             }
190         }
191 
192         StatementSet cs = new StatementSet(session, targets, expression,
193                                            columnMap, compileContext);
194 
195         return cs;
196     }
197 
198     /**
199      * Creates SET Statement for PSM or session variables from this parse context.
200      */
compileSetStatement(RangeGroup[] rangeGroups, RangeVariable[] rangeVars)201     StatementSet compileSetStatement(RangeGroup[] rangeGroups,
202                                      RangeVariable[] rangeVars) {
203 
204         OrderedHashSet targetSet    = new OrderedHashSet();
205         HsqlArrayList  exprList     = new HsqlArrayList();
206         LongDeque      colIndexList = new LongDeque();
207 
208         readSetClauseList(rangeVars, targetSet, colIndexList, exprList);
209 
210         if (exprList.size() > 1) {
211             throw Error.parseError(ErrorCode.X_42602, null,
212                                    scanner.getLineNumber());
213         }
214 
215         Expression expression = (Expression) exprList.get(0);
216 
217         if (expression.getDegree() != targetSet.size()) {
218             throw Error.error(ErrorCode.X_42546, Tokens.T_SET);
219         }
220 
221         int[] columnMap = new int[colIndexList.size()];
222 
223         colIndexList.toArray(columnMap);
224 
225         Expression[] targets = new Expression[targetSet.size()];
226 
227         targetSet.toArray(targets);
228 
229         for (int i = 0; i < targets.length; i++) {
230             resolveOuterReferencesAndTypes(rangeGroups, targets[i]);
231         }
232 
233         resolveOuterReferencesAndTypes(rangeGroups, expression);
234 
235         for (int i = 0; i < targets.length; i++) {
236             ColumnSchema col = targets[i].getColumn();
237 
238             if (col.getParameterMode()
239                     == SchemaObject.ParameterModes.PARAM_IN) {
240 
241                 // todo - use more specific error message
242                 throw Error.error(ErrorCode.X_0U000,
243                                   col.getName().statementName);
244             }
245 
246             if (!targets[i].getDataType().canBeAssignedFrom(
247                     expression.getNodeDataType(i))) {
248                 throw Error.parseError(ErrorCode.X_42561, null,
249                                        scanner.getLineNumber());
250             }
251         }
252 
253         StatementSet cs = new StatementSet(session, targets, expression,
254                                            columnMap, compileContext);
255 
256         return cs;
257     }
258 
259     /**
260      * Creates SET Statement for a trigger row from this parse context.
261      */
compileTriggerSetStatement(Table table, RangeGroup[] rangeGroups)262     StatementDMQL compileTriggerSetStatement(Table table,
263             RangeGroup[] rangeGroups) {
264 
265         Expression[]   updateExpressions;
266         int[]          columnMap;
267         OrderedHashSet targetSet = new OrderedHashSet();
268         HsqlArrayList  exprList  = new HsqlArrayList();
269         RangeVariable[] targetRangeVars = new RangeVariable[]{
270             rangeGroups[0].getRangeVariables()[TriggerDef.NEW_ROW] };
271         LongDeque colIndexList = new LongDeque();
272 
273         readSetClauseList(targetRangeVars, targetSet, colIndexList, exprList);
274 
275         columnMap = new int[colIndexList.size()];
276 
277         colIndexList.toArray(columnMap);
278 
279         Expression[] targets = new Expression[targetSet.size()];
280 
281         targetSet.toArray(targets);
282 
283         for (int i = 0; i < targets.length; i++) {
284             resolveOuterReferencesAndTypes(RangeGroup.emptyArray, targets[i]);
285         }
286 
287         updateExpressions = new Expression[exprList.size()];
288 
289         exprList.toArray(updateExpressions);
290         resolveUpdateExpressions(table, RangeGroup.emptyGroup, columnMap,
291                                  updateExpressions, rangeGroups);
292 
293         StatementDMQL cs = new StatementSet(session, targets, table,
294                                             rangeGroups[0].getRangeVariables(),
295                                             columnMap, updateExpressions,
296                                             compileContext);
297 
298         return cs;
299     }
300 
compileAlterSpecificRoutine()301     StatementSchema compileAlterSpecificRoutine() {
302 
303         boolean restrict = false;
304 
305         readThis(Tokens.SPECIFIC);
306         readThis(Tokens.ROUTINE);
307 
308         Routine routine =
309             (Routine) readSchemaObjectName(SchemaObject.SPECIFIC_ROUTINE);
310 
311         routine = routine.duplicate();
312 
313         readRoutineCharacteristics(routine);
314 
315         restrict = readIfThis(Tokens.RESTRICT);
316 
317         if (restrict) {
318             OrderedHashSet set = database.schemaManager.getReferencesTo(
319                 routine.getSpecificName());
320 
321             if (!set.isEmpty()) {
322                 throw Error.parseError(ErrorCode.X_42502, null,
323                                        scanner.getLineNumber());
324             }
325         }
326 
327         if (token.tokenType == Tokens.BODY) {
328             read();
329         } else if (token.tokenType == Tokens.NAME) {
330             read();
331         }
332 
333         readRoutineBody(routine);
334         routine.resetAlteredRoutineSettings();
335         routine.resolve(session);
336 
337         Object[] args = new Object[]{ routine };
338         String   sql  = getLastPart();
339         StatementSchema cs = new StatementSchema(sql,
340             StatementTypes.ALTER_ROUTINE, args, null,
341             database.schemaManager.getCatalogNameArray());
342 
343         return cs;
344     }
345 
346     // SQL-invoked routine
compileCreateProcedureOrFunction(boolean orReplace)347     StatementSchema compileCreateProcedureOrFunction(boolean orReplace) {
348 
349         Routine  routine = readCreateProcedureOrFunction();
350         Object[] args    = new Object[]{ routine };
351         String   sql     = getLastPart();
352         StatementSchema cs = new StatementSchema(sql,
353             StatementTypes.CREATE_ROUTINE, args, null,
354             database.schemaManager.getCatalogNameArray());
355 
356         return cs;
357     }
358 
readCreateProcedureOrFunction()359     Routine readCreateProcedureOrFunction() {
360 
361         Routine routine = readProcedureOrFunctionDeclaration();
362 
363         readRoutineBody(routine);
364 
365         return routine;
366     }
367 
readProcedureOrFunctionDeclaration()368     Routine readProcedureOrFunctionDeclaration() {
369 
370         int     routineType;
371         boolean isAggregate = false;
372 
373         if (token.tokenType == Tokens.AGGREGATE) {
374             isAggregate = true;
375 
376             read();
377 
378             if (token.tokenType == Tokens.PROCEDURE) {
379                 throw unexpectedToken();
380             }
381         }
382 
383         routineType = token.tokenType == Tokens.PROCEDURE
384                       ? SchemaObject.PROCEDURE
385                       : SchemaObject.FUNCTION;
386 
387         HsqlName name;
388 
389         read();
390 
391         name = readNewSchemaObjectName(routineType, true);
392 
393         name.setSchemaIfNull(session.getCurrentSchemaHsqlName());
394 
395         Routine routine = new Routine(routineType);
396 
397         routine.setName(name);
398         routine.setAggregate(isAggregate);
399         readRoutineArguments(routine);
400 
401         if (routineType != SchemaObject.PROCEDURE) {
402             readThis(Tokens.RETURNS);
403 
404             if (token.tokenType == Tokens.TABLE) {
405                 read();
406 
407                 TableDerived table =
408                     new TableDerived(database, SqlInvariants.MODULE_HSQLNAME,
409                                      TableBase.FUNCTION_TABLE);
410 
411                 readTableDefinition(routine, table);
412                 routine.setReturnTable(table);
413             } else {
414                 Type type = readTypeDefinition(false, true);
415 
416                 routine.setReturnType(type);
417             }
418         }
419 
420         readRoutineCharacteristics(routine);
421 
422         return routine;
423     }
424 
readRoutineArguments(Routine routine)425     void readRoutineArguments(Routine routine) {
426 
427         readThis(Tokens.OPENBRACKET);
428 
429         if (token.tokenType == Tokens.CLOSEBRACKET) {
430             read();
431         } else {
432             while (true) {
433                 ColumnSchema newcolumn = readRoutineParameter(routine, true);
434 
435                 routine.addParameter(newcolumn);
436 
437                 if (token.tokenType == Tokens.COMMA) {
438                     read();
439                 } else {
440                     readThis(Tokens.CLOSEBRACKET);
441 
442                     break;
443                 }
444             }
445         }
446     }
447 
readCreatePasswordCheckFunction()448     Routine readCreatePasswordCheckFunction() {
449 
450         Routine routine = new Routine(SchemaObject.FUNCTION);
451 
452         if (token.tokenType == Tokens.NONE) {
453             read();
454 
455             return null;
456         } else if (token.tokenType == Tokens.EXTERNAL) {
457             routine.setLanguage(Routine.LANGUAGE_JAVA);
458             routine.setDataImpact(Routine.NO_SQL);
459         } else {
460             routine.setLanguage(Routine.LANGUAGE_SQL);
461             routine.setDataImpact(Routine.CONTAINS_SQL);
462         }
463 
464         HsqlName hsqlName = database.nameManager.newHsqlName(Tokens.T_PASSWORD,
465             false, SchemaObject.FUNCTION);
466 
467         hsqlName.setSchemaIfNull(SqlInvariants.SYSTEM_SCHEMA_HSQLNAME);
468         routine.setName(hsqlName);
469 
470         hsqlName = database.nameManager.newHsqlName(Tokens.T_PASSWORD, false,
471                 SchemaObject.PARAMETER);
472 
473         ColumnSchema column = new ColumnSchema(hsqlName, Type.SQL_VARCHAR,
474                                                false, false, null);
475 
476         routine.addParameter(column);
477         routine.setReturnType(Type.SQL_BOOLEAN);
478         readRoutineBody(routine);
479         routine.resolve(session);
480 
481         return routine;
482     }
483 
readCreateDatabaseAuthenticationFunction()484     Routine readCreateDatabaseAuthenticationFunction() {
485 
486         Routine routine = new Routine(SchemaObject.FUNCTION);
487 
488         if (token.tokenType == Tokens.NONE) {
489             read();
490 
491             return null;
492         }
493 
494         checkIsThis(Tokens.EXTERNAL);
495         routine.setLanguage(Routine.LANGUAGE_JAVA);
496         routine.setDataImpact(Routine.NO_SQL);
497         routine.setName(
498             database.nameManager.newHsqlName(
499                 Tokens.T_AUTHENTICATION, false, SchemaObject.FUNCTION));
500 
501         for (int i = 0; i < 3; i++) {
502             ColumnSchema column = new ColumnSchema(null, Type.SQL_VARCHAR,
503                                                    false, false, null);
504 
505             routine.addParameter(column);
506         }
507 
508         routine.setReturnType(
509             new ArrayType(
510                 Type.SQL_VARCHAR_DEFAULT, ArrayType.defaultArrayCardinality));
511         readRoutineBody(routine);
512         routine.resolve(session);
513 
514         return routine;
515     }
516 
readTableDefinition(Routine routine, Table table)517     private void readTableDefinition(Routine routine, Table table) {
518 
519         readThis(Tokens.OPENBRACKET);
520 
521         for (int i = 0; ; i++) {
522             ColumnSchema newcolumn = readRoutineParameter(routine, false);
523 
524             if (newcolumn.getName() == null) {
525                 throw unexpectedToken();
526             }
527 
528             table.addColumn(newcolumn);
529 
530             if (token.tokenType == Tokens.COMMA) {
531                 read();
532             } else {
533                 readThis(Tokens.CLOSEBRACKET);
534 
535                 break;
536             }
537         }
538 
539         table.createPrimaryKey();
540     }
541 
readRoutineCharacteristics(Routine routine)542     private void readRoutineCharacteristics(Routine routine) {
543 
544         OrderedIntHashSet set = new OrderedIntHashSet();
545         boolean           end = false;
546 
547         while (!end) {
548             switch (token.tokenType) {
549 
550                 case Tokens.LANGUAGE : {
551                     if (!set.add(Tokens.LANGUAGE)) {
552                         throw unexpectedToken();
553                     }
554 
555                     read();
556 
557                     if (token.tokenType == Tokens.JAVA) {
558                         read();
559                         routine.setLanguage(Routine.LANGUAGE_JAVA);
560                     } else if (token.tokenType == Tokens.SQL) {
561                         read();
562                         routine.setLanguage(Routine.LANGUAGE_SQL);
563                     } else {
564                         throw unexpectedToken();
565                     }
566 
567                     break;
568                 }
569                 case Tokens.PARAMETER : {
570                     if (!set.add(Tokens.PARAMETER)) {
571                         throw unexpectedToken();
572                     }
573 
574                     read();
575                     readThis(Tokens.STYLE);
576 
577                     if (token.tokenType == Tokens.JAVA) {
578                         read();
579                         routine.setParameterStyle(Routine.PARAM_STYLE_JAVA);
580                     } else {
581                         readThis(Tokens.SQL);
582                         routine.setParameterStyle(Routine.PARAM_STYLE_SQL);
583                     }
584 
585                     break;
586                 }
587                 case Tokens.SPECIFIC : {
588                     if (!set.add(Tokens.SPECIFIC)) {
589                         throw unexpectedToken();
590                     }
591 
592                     read();
593 
594                     HsqlName name =
595                         readNewSchemaObjectName(SchemaObject.SPECIFIC_ROUTINE,
596                                                 false);
597 
598                     routine.setSpecificName(name);
599 
600                     break;
601                 }
602                 case Tokens.DETERMINISTIC : {
603                     if (!set.add(Tokens.DETERMINISTIC)) {
604                         throw unexpectedToken();
605                     }
606 
607                     read();
608                     routine.setDeterministic(true);
609 
610                     break;
611                 }
612                 case Tokens.NOT : {
613                     if (!set.add(Tokens.DETERMINISTIC)) {
614                         throw unexpectedToken();
615                     }
616 
617                     read();
618                     readThis(Tokens.DETERMINISTIC);
619                     routine.setDeterministic(false);
620 
621                     break;
622                 }
623                 case Tokens.MODIFIES : {
624                     if (!set.add(Tokens.SQL)) {
625                         throw unexpectedToken();
626                     }
627 
628                     if (routine.getType() == SchemaObject.FUNCTION) {
629                         throw unexpectedToken();
630                     }
631 
632                     read();
633                     readThis(Tokens.SQL);
634                     readThis(Tokens.DATA);
635                     routine.setDataImpact(Routine.MODIFIES_SQL);
636 
637                     break;
638                 }
639                 case Tokens.NO : {
640                     if (!set.add(Tokens.SQL)) {
641                         throw unexpectedToken();
642                     }
643 
644                     read();
645                     readThis(Tokens.SQL);
646                     routine.setDataImpact(Routine.NO_SQL);
647 
648                     break;
649                 }
650                 case Tokens.READS : {
651                     if (!set.add(Tokens.SQL)) {
652                         throw unexpectedToken();
653                     }
654 
655                     read();
656                     readThis(Tokens.SQL);
657                     readThis(Tokens.DATA);
658                     routine.setDataImpact(Routine.READS_SQL);
659 
660                     break;
661                 }
662                 case Tokens.CONTAINS : {
663                     if (!set.add(Tokens.SQL)) {
664                         throw unexpectedToken();
665                     }
666 
667                     read();
668                     readThis(Tokens.SQL);
669                     routine.setDataImpact(Routine.CONTAINS_SQL);
670 
671                     break;
672                 }
673                 case Tokens.RETURNS : {
674                     if (!set.add(Tokens.NULL) || routine.isProcedure()) {
675                         throw unexpectedToken();
676                     }
677 
678                     if (routine.isAggregate()) {
679                         throw Error.error(ErrorCode.X_42604,
680                                           token.tokenString);
681                     }
682 
683                     read();
684                     readThis(Tokens.NULL);
685                     readThis(Tokens.ON);
686                     readThis(Tokens.NULL);
687                     readThis(Tokens.INPUT);
688                     routine.setNullInputOutput(true);
689 
690                     break;
691                 }
692                 case Tokens.CALLED : {
693                     if (!set.add(Tokens.NULL) || routine.isProcedure()) {
694                         throw unexpectedToken();
695                     }
696 
697                     read();
698                     readThis(Tokens.ON);
699                     readThis(Tokens.NULL);
700                     readThis(Tokens.INPUT);
701                     routine.setNullInputOutput(false);
702 
703                     break;
704                 }
705                 case Tokens.DYNAMIC : {
706                     if (!set.add(Tokens.RESULT) || routine.isFunction()) {
707                         throw unexpectedToken();
708                     }
709 
710                     read();
711                     readThis(Tokens.RESULT);
712                     readThis(Tokens.SETS);
713 
714                     int results = readInteger();
715 
716                     if (results < 0 || results > 16) {
717                         throw Error.error(ErrorCode.X_42604,
718                                           String.valueOf(results));
719                     }
720 
721                     routine.setMaxDynamicResults(results);
722 
723                     break;
724                 }
725                 case Tokens.NEW : {
726                     if (routine.getType() == SchemaObject.FUNCTION
727                             || !set.add(Tokens.SAVEPOINT)) {
728                         throw unexpectedToken();
729                     }
730 
731                     read();
732                     readThis(Tokens.SAVEPOINT);
733                     readThis(Tokens.LEVEL);
734                     routine.setNewSavepointLevel(true);
735 
736                     break;
737                 }
738                 case Tokens.OLD : {
739                     if (routine.getType() == SchemaObject.FUNCTION
740                             || !set.add(Tokens.SAVEPOINT)) {
741                         throw unexpectedToken();
742                     }
743 
744                     read();
745                     readThis(Tokens.SAVEPOINT);
746                     readThis(Tokens.LEVEL);
747                     routine.setNewSavepointLevel(false);
748 
749                     throw unsupportedFeature(Tokens.T_OLD);
750 
751                     // break;
752                 }
753                 default :
754                     end = true;
755                     break;
756             }
757         }
758     }
759 
readRoutineBody(Routine routine)760     void readRoutineBody(Routine routine) {
761 
762         if (token.tokenType == Tokens.EXTERNAL) {
763             readRoutineJavaBody(routine);
764         } else {
765             readRoutineSQLBody(routine);
766         }
767     }
768 
readRoutineSQLBody(Routine routine)769     void readRoutineSQLBody(Routine routine) {
770 
771         startRecording();
772         session.sessionContext.pushRoutineTables();
773 
774         try {
775             Statement statement = compileSQLProcedureStatementOrNull(routine,
776                 null);
777 
778             if (statement == null) {
779                 throw unexpectedToken();
780             }
781 
782             Token[] tokenisedStatement = getRecordedStatement();
783             String  sql                = Token.getSQL(tokenisedStatement);
784 
785             statement.setSQL(sql);
786             routine.setProcedure(statement);
787         } finally {
788             session.sessionContext.popRoutineTables();
789         }
790     }
791 
readRoutineJavaBody(Routine routine)792     void readRoutineJavaBody(Routine routine) {
793 
794         if (routine.getLanguage() != Routine.LANGUAGE_JAVA) {
795             throw unexpectedToken();
796         }
797 
798         read();
799         readThis(Tokens.NAME);
800         checkIsQuotedString();
801         routine.setMethodURL((String) token.tokenValue);
802         read();
803 
804         if (token.tokenType == Tokens.PARAMETER) {
805             read();
806             readThis(Tokens.STYLE);
807             readThis(Tokens.JAVA);
808         }
809     }
810 
811     /*
812         <SQL control statement> ::=
813         <call statement>
814         | <return statement>
815 
816         <compound statement>
817         <case statement>
818         <if statement>
819         <iterate statement>
820         <leave statement>
821         <loop statement>
822         <while statement>
823         <repeat statement>
824        <for statement>
825        <assignment statement> SET (,,,) = (,,,) or SET a = b
826 
827 
828      */
readLocalDeclarationList(Routine routine, StatementCompound context)829     Object[] readLocalDeclarationList(Routine routine,
830                                       StatementCompound context) {
831 
832         HsqlArrayList list                = new HsqlArrayList();
833         final int     table               = 0;
834         final int     variableOrCondition = 1;
835         final int     cursor              = 2;
836         final int     handler             = 3;
837         int           objectType          = table;
838         RangeGroup[]  rangeGroups         = new RangeGroup[1];
839 
840         rangeGroups[0] = context == null ? routine
841                                          : context;
842 
843         compileContext.setOuterRanges(rangeGroups);
844 
845         while (token.tokenType == Tokens.DECLARE) {
846             Object var = null;
847 
848             if (objectType == table) {
849                 var = readLocalTableVariableDeclarationOrNull(routine);
850 
851                 if (var == null) {
852                     objectType = variableOrCondition;
853                 } else {
854                     list.add(var);
855                     readThis(Tokens.SEMICOLON);
856                 }
857             } else if (objectType == variableOrCondition) {
858                 var = readLocalVariableDeclarationOrNull();
859 
860                 if (var == null) {
861                     objectType = cursor;
862                 } else {
863                     list.addAll((Object[]) var);
864                 }
865             } else if (objectType == cursor) {
866                 var = compileDeclareCursorOrNull(rangeGroups, true);
867 
868                 if (var == null) {
869                     objectType = handler;
870                 } else {
871                     list.add(var);
872                     readThis(Tokens.SEMICOLON);
873                 }
874             } else if (objectType == handler) {
875                 var = compileLocalHandlerDeclaration(routine, context);
876 
877                 list.add(var);
878             }
879         }
880 
881         Object[] declarations = new Object[list.size()];
882 
883         list.toArray(declarations);
884 
885         return declarations;
886     }
887 
readLocalTableVariableDeclarationOrNull(Routine routine)888     Table readLocalTableVariableDeclarationOrNull(Routine routine) {
889 
890         int position = getPosition();
891 
892         readThis(Tokens.DECLARE);
893 
894         if (token.tokenType == Tokens.TABLE) {
895             read();
896 
897             HsqlName name = readNewSchemaObjectName(SchemaObject.TABLE, false);
898 
899             name.schema = SqlInvariants.MODULE_HSQLNAME;
900 
901             Table table = new Table(database, name, TableBase.TEMP_TABLE);
902 
903             table.persistenceScope = TableBase.SCOPE_ROUTINE;
904 
905             readTableDefinition(routine, table);
906             session.sessionContext.addSessionTable(table);
907 
908             return table;
909         } else {
910             rewind(position);
911 
912             return null;
913         }
914     }
915 
readLocalVariableDeclarationOrNull()916     ColumnSchema[] readLocalVariableDeclarationOrNull() {
917 
918         int           position = getPosition();
919         Type          type;
920         HsqlArrayList names = new HsqlArrayList();
921 
922         try {
923             readThis(Tokens.DECLARE);
924 
925             if (isReservedKey()) {
926                 rewind(position);
927 
928                 return null;
929             }
930 
931             while (true) {
932                 HsqlName name = readNewSchemaObjectName(SchemaObject.VARIABLE,
933                     false);
934 
935                 if (token.tokenType == Tokens.CONDITION) {
936                     rewind(position);
937 
938                     return null;
939                 }
940 
941                 names.add(name);
942 
943                 if (token.tokenType == Tokens.COMMA) {
944                     read();
945                 } else {
946                     break;
947                 }
948             }
949 
950             type = readTypeDefinition(false, true);
951         } catch (HsqlException e) {
952 
953             // may be cursor
954             rewind(position);
955 
956             return null;
957         }
958 
959         Expression def = null;
960 
961         if (token.tokenType == Tokens.DEFAULT) {
962             read();
963 
964             def = readDefaultClause(type);
965         }
966 
967         ColumnSchema[] variable = new ColumnSchema[names.size()];
968 
969         for (int i = 0; i < names.size(); i++) {
970             variable[i] = new ColumnSchema((HsqlName) names.get(i), type,
971                                            true, false, def);
972 
973             variable[i].setParameterMode(
974                 SchemaObject.ParameterModes.PARAM_INOUT);
975         }
976 
977         readThis(Tokens.SEMICOLON);
978 
979         return variable;
980     }
981 
compileLocalHandlerDeclaration(Routine routine, StatementCompound context)982     private StatementHandler compileLocalHandlerDeclaration(Routine routine,
983             StatementCompound context) {
984 
985         int handlerType;
986 
987         readThis(Tokens.DECLARE);
988 
989         switch (token.tokenType) {
990 
991             case Tokens.CONTINUE :
992                 read();
993 
994                 handlerType = StatementHandler.CONTINUE;
995                 break;
996 
997             case Tokens.EXIT :
998                 read();
999 
1000                 handlerType = StatementHandler.EXIT;
1001                 break;
1002 
1003             case Tokens.UNDO :
1004                 read();
1005 
1006                 handlerType = StatementHandler.UNDO;
1007                 break;
1008 
1009             default :
1010                 throw unexpectedToken();
1011         }
1012 
1013         readThis(Tokens.HANDLER);
1014         readThis(Tokens.FOR);
1015 
1016         StatementHandler handler = new StatementHandler(handlerType);
1017         boolean          end     = false;
1018         boolean          start   = true;
1019 
1020         while (!end) {
1021             int conditionType = StatementHandler.NONE;
1022 
1023             switch (token.tokenType) {
1024 
1025                 case Tokens.COMMA :
1026                     if (start) {
1027                         throw unexpectedToken();
1028                     }
1029 
1030                     read();
1031 
1032                     start = true;
1033                     break;
1034 
1035                 case Tokens.SQLSTATE :
1036                     conditionType = StatementHandler.SQL_STATE;
1037 
1038                 // fall through
1039                 case Tokens.SQLEXCEPTION :
1040                     if (conditionType == StatementHandler.NONE) {
1041                         conditionType = StatementHandler.SQL_EXCEPTION;
1042                     }
1043 
1044                 // fall through
1045                 case Tokens.SQLWARNING :
1046                     if (conditionType == StatementHandler.NONE) {
1047                         conditionType = StatementHandler.SQL_WARNING;
1048                     }
1049 
1050                 // fall through
1051                 case Tokens.NOT :
1052                     if (conditionType == StatementHandler.NONE) {
1053                         conditionType = StatementHandler.SQL_NOT_FOUND;
1054                     }
1055 
1056                     if (!start) {
1057                         throw unexpectedToken();
1058                     }
1059 
1060                     start = false;
1061 
1062                     read();
1063 
1064                     if (conditionType == StatementHandler.SQL_NOT_FOUND) {
1065                         readThis(Tokens.FOUND);
1066                     } else if (conditionType == StatementHandler.SQL_STATE) {
1067                         String sqlState = parseSQLStateValue();
1068 
1069                         handler.addConditionState(sqlState);
1070 
1071                         break;
1072                     }
1073 
1074                     handler.addConditionType(conditionType);
1075                     break;
1076 
1077                 default :
1078                     if (start) {
1079                         throw unexpectedToken();
1080                     }
1081 
1082                     end = true;
1083                     break;
1084             }
1085         }
1086 
1087         if (token.tokenType == Tokens.SEMICOLON) {
1088             read();
1089         } else {
1090             Statement e = compileSQLProcedureStatementOrNull(routine, context);
1091 
1092             if (e == null) {
1093                 throw unexpectedToken();
1094             }
1095 
1096             readThis(Tokens.SEMICOLON);
1097             handler.addStatement(e);
1098         }
1099 
1100         return handler;
1101     }
1102 
parseSQLStateValue()1103     String parseSQLStateValue() {
1104 
1105         readIfThis(Tokens.VALUE);
1106         checkIsQuotedString();
1107 
1108         String sqlState = token.tokenString;
1109 
1110         if (sqlState.length() != 5) {
1111             throw Error.parseError(ErrorCode.X_42607, null,
1112                                    scanner.getLineNumber());
1113         }
1114 
1115         read();
1116 
1117         return sqlState;
1118     }
1119 
1120     static String[] featureStrings = new String[]{ "H901_03" };
1121 
parseSQLFeatureValue()1122     String parseSQLFeatureValue() {
1123 
1124         if (!isUndelimitedSimpleName()) {
1125             throw Error.parseError(ErrorCode.X_42555, token.tokenString,
1126                                    scanner.getLineNumber());
1127         }
1128 
1129         String sqlFeature = token.tokenString;
1130         int    index      = ArrayUtil.find(featureStrings, sqlFeature);
1131 
1132         if (index < 0) {
1133             throw Error.parseError(ErrorCode.X_42555, token.tokenString,
1134                                    scanner.getLineNumber());
1135         }
1136 
1137         read();
1138 
1139         return sqlFeature;
1140     }
1141 
compileCompoundStatement(Routine routine, StatementCompound context, HsqlName label)1142     Statement compileCompoundStatement(Routine routine,
1143                                        StatementCompound context,
1144                                        HsqlName label) {
1145 
1146         final boolean atomic = true;
1147 
1148         readThis(Tokens.BEGIN);
1149         readThis(Tokens.ATOMIC);
1150 
1151         label = createLabelIfNull(context, label);
1152 
1153         StatementCompound statement =
1154             new StatementCompound(StatementTypes.BEGIN_END, label, context);
1155 
1156         statement.setAtomic(atomic);
1157         statement.setRoot(routine);
1158 
1159         Object[] declarations = readLocalDeclarationList(routine, context);
1160 
1161         statement.setLocalDeclarations(declarations);
1162 
1163         Statement[] statements = compileSQLProcedureStatementList(routine,
1164             statement);
1165 
1166         statement.setStatements(statements);
1167         readThis(Tokens.END);
1168 
1169         if (isSimpleName() && !isReservedKey()) {
1170             if (label == null) {
1171                 throw unexpectedToken();
1172             }
1173 
1174             if (!label.name.equals(token.tokenString)) {
1175                 throw Error.error(ErrorCode.X_42508, token.tokenString);
1176             }
1177 
1178             read();
1179         }
1180 
1181         return statement;
1182     }
1183 
createLabelIfNull(StatementCompound context, HsqlName label)1184     HsqlName createLabelIfNull(StatementCompound context, HsqlName label) {
1185 
1186         if (label != null) {
1187             return label;
1188         }
1189 
1190         String            labelString;
1191         StatementCompound parent = context;
1192         int               level  = 0;
1193 
1194         while (parent != null) {
1195             level++;
1196 
1197             parent = parent.parent;
1198         }
1199 
1200         labelString = "_" + level;
1201         label = session.database.nameManager.newHsqlName(labelString, false,
1202                 SchemaObject.LABEL);
1203 
1204         return label;
1205     }
1206 
compileSQLProcedureStatementList(Routine routine, StatementCompound context)1207     Statement[] compileSQLProcedureStatementList(Routine routine,
1208             StatementCompound context) {
1209 
1210         Statement     e;
1211         HsqlArrayList list = new HsqlArrayList();
1212 
1213         while (true) {
1214             e = compileSQLProcedureStatementOrNull(routine, context);
1215 
1216             if (e == null) {
1217                 break;
1218             }
1219 
1220             readThis(Tokens.SEMICOLON);
1221             list.add(e);
1222         }
1223 
1224         if (list.size() == 0) {
1225             throw unexpectedToken();
1226         }
1227 
1228         Statement[] statements = new Statement[list.size()];
1229 
1230         list.toArray(statements);
1231 
1232         return statements;
1233     }
1234 
compileSQLProcedureStatementOrNull(Routine routine, StatementCompound context)1235     Statement compileSQLProcedureStatementOrNull(Routine routine,
1236             StatementCompound context) {
1237 
1238         Statement    cs          = null;
1239         HsqlName     label       = null;
1240         RangeGroup   rangeGroup  = context == null ? routine
1241                                                    : context;
1242         RangeGroup[] rangeGroups = new RangeGroup[]{ rangeGroup };
1243 
1244         if (!routine.isTrigger() && isSimpleName() && !isReservedKey()) {
1245             label = readLabel();
1246         }
1247 
1248         compileContext.reset();
1249 
1250         HsqlName oldSchema = session.getCurrentSchemaHsqlName();
1251 
1252         session.setCurrentSchemaHsqlName(routine.getSchemaName());
1253 
1254         try {
1255             switch (token.tokenType) {
1256 
1257                 // data
1258                 case Tokens.OPEN : {
1259                     if (routine.dataImpact == Routine.CONTAINS_SQL) {
1260                         throw Error.error(ErrorCode.X_42602,
1261                                           routine.getDataImpactString());
1262                     }
1263 
1264                     if (label != null) {
1265                         throw unexpectedToken();
1266                     }
1267 
1268                     cs = compileOpenCursorStatement(context);
1269 
1270                     break;
1271                 }
1272                 case Tokens.SELECT : {
1273                     if (label != null) {
1274                         throw unexpectedToken();
1275                     }
1276 
1277                     cs = compileSelectSingleRowStatement(rangeGroups);
1278 
1279                     break;
1280                 }
1281 
1282                 // data change
1283                 case Tokens.INSERT :
1284                     if (label != null) {
1285                         throw unexpectedToken();
1286                     }
1287 
1288                     cs = compileInsertStatement(rangeGroups);
1289                     break;
1290 
1291                 case Tokens.UPDATE :
1292                     if (label != null) {
1293                         throw unexpectedToken();
1294                     }
1295 
1296                     cs = compileUpdateStatement(rangeGroups);
1297                     break;
1298 
1299                 case Tokens.DELETE :
1300                     if (label != null) {
1301                         throw unexpectedToken();
1302                     }
1303 
1304                     cs = compileDeleteStatement(rangeGroups);
1305                     break;
1306 
1307                 case Tokens.TRUNCATE :
1308                     if (label != null) {
1309                         throw unexpectedToken();
1310                     }
1311 
1312                     cs = compileTruncateStatement();
1313                     break;
1314 
1315                 case Tokens.MERGE :
1316                     if (label != null) {
1317                         throw unexpectedToken();
1318                     }
1319 
1320                     cs = compileMergeStatement(rangeGroups);
1321                     break;
1322 
1323                 case Tokens.SET :
1324                     if (label != null) {
1325                         throw unexpectedToken();
1326                     }
1327 
1328                     read();
1329 
1330                     if (routine.isTrigger()) {
1331                         if (routine.triggerType == TriggerDef.BEFORE
1332                                 && routine.triggerOperation
1333                                    != StatementTypes.DELETE_WHERE) {
1334                             int position = getPosition();
1335 
1336                             try {
1337                                 cs = compileTriggerSetStatement(
1338                                     routine.triggerTable, rangeGroups);
1339 
1340                                 break;
1341                             } catch (HsqlException e) {
1342                                 rewind(position);
1343 
1344                                 cs = compileSetStatement(
1345                                     rangeGroups,
1346                                     rangeGroup.getRangeVariables());
1347                             }
1348                         } else {
1349                             cs = compileSetStatement(
1350                                 rangeGroups, rangeGroup.getRangeVariables());
1351                         }
1352 
1353                         ((StatementSet) cs).checkIsNotColumnTarget();
1354                     } else {
1355                         cs = compileSetStatement(
1356                             rangeGroups, rangeGroup.getRangeVariables());
1357                     }
1358                     break;
1359 
1360                 case Tokens.GET :
1361                     if (label != null) {
1362                         throw unexpectedToken();
1363                     }
1364 
1365                     cs = compileGetStatement(rangeGroups);
1366                     break;
1367 
1368                 // control
1369                 case Tokens.CALL : {
1370                     if (label != null) {
1371                         throw unexpectedToken();
1372                     }
1373 
1374                     cs = compileCallStatement(rangeGroups, true);
1375 
1376                     Routine proc = ((StatementProcedure) cs).procedure;
1377 
1378                     if (proc != null) {
1379                         switch (routine.dataImpact) {
1380 
1381                             case Routine.CONTAINS_SQL : {
1382                                 if (proc.dataImpact == Routine.READS_SQL
1383                                         || proc.dataImpact
1384                                            == Routine.MODIFIES_SQL) {
1385                                     throw Error.error(
1386                                         ErrorCode.X_42602,
1387                                         routine.getDataImpactString());
1388                                 }
1389 
1390                                 break;
1391                             }
1392                             case Routine.READS_SQL : {
1393                                 if (proc.dataImpact == Routine.MODIFIES_SQL) {
1394                                     throw Error.error(
1395                                         ErrorCode.X_42602,
1396                                         routine.getDataImpactString());
1397                                 }
1398 
1399                                 break;
1400                             }
1401                         }
1402                     }
1403 
1404                     break;
1405                 }
1406                 case Tokens.RETURN : {
1407                     if (routine.isTrigger() || label != null) {
1408                         throw unexpectedToken();
1409                     }
1410 
1411                     read();
1412 
1413                     cs = compileReturnValue(routine, context);
1414 
1415                     break;
1416                 }
1417                 case Tokens.BEGIN : {
1418                     cs = compileCompoundStatement(routine, context, label);
1419 
1420                     break;
1421                 }
1422                 case Tokens.WHILE : {
1423                     if (routine.isTrigger()) {
1424                         throw unexpectedToken();
1425                     }
1426 
1427                     cs = compileWhile(routine, context, label);
1428 
1429                     break;
1430                 }
1431                 case Tokens.REPEAT : {
1432                     cs = compileRepeat(routine, context, label);
1433 
1434                     break;
1435                 }
1436                 case Tokens.LOOP : {
1437                     cs = compileLoop(routine, context, label);
1438 
1439                     break;
1440                 }
1441                 case Tokens.FOR : {
1442                     cs = compileFor(routine, context, label);
1443 
1444                     break;
1445                 }
1446                 case Tokens.ITERATE : {
1447                     if (label != null) {
1448                         throw unexpectedToken();
1449                     }
1450 
1451                     cs = compileIterate();
1452 
1453                     break;
1454                 }
1455                 case Tokens.LEAVE : {
1456                     if (label != null) {
1457                         throw unexpectedToken();
1458                     }
1459 
1460                     cs = compileLeave(routine, context);
1461 
1462                     break;
1463                 }
1464                 case Tokens.IF : {
1465                     cs = compileIf(routine, context);
1466 
1467                     break;
1468                 }
1469                 case Tokens.CASE : {
1470                     cs = compileCase(routine, context);
1471 
1472                     break;
1473                 }
1474                 case Tokens.SIGNAL : {
1475                     cs = compileSignal(routine, context, label);
1476 
1477                     break;
1478                 }
1479                 case Tokens.RESIGNAL : {
1480                     cs = compileResignal(routine, context, label);
1481 
1482                     break;
1483                 }
1484                 default :
1485                     return null;
1486             }
1487 
1488             cs.setRoot(routine);
1489             cs.setParent(context);
1490 
1491             return cs;
1492         } finally {
1493             session.setCurrentSchemaHsqlName(oldSchema);
1494         }
1495     }
1496 
readLabel()1497     HsqlName readLabel() {
1498 
1499         HsqlName label = readNewSchemaObjectName(SchemaObject.LABEL, false);
1500 
1501         if (token.tokenType != Tokens.COLON) {
1502             throw unexpectedToken(label.getNameString());
1503         }
1504 
1505         readThis(Tokens.COLON);
1506 
1507         return label;
1508     }
1509 
compileReturnValue(Routine routine, StatementCompound context)1510     Statement compileReturnValue(Routine routine, StatementCompound context) {
1511 
1512         RangeGroup[] rangeGroups = new RangeGroup[1];
1513 
1514         rangeGroups[0] = context == null ? routine
1515                                          : context;
1516 
1517         compileContext.setOuterRanges(rangeGroups);
1518 
1519         Expression e = XreadValueExpressionOrNull();
1520 
1521         if (e == null) {
1522             throw unexpectedToken();
1523         }
1524 
1525         resolveOuterReferencesAndTypes(routine, context, e);
1526 
1527         if (routine.isProcedure()) {
1528             throw Error.parseError(ErrorCode.X_42602, null,
1529                                    scanner.getLineNumber());
1530         }
1531 
1532         if (routine.returnsTable()) {
1533             if (e.getType() != OpTypes.TABLE_SUBQUERY) {
1534                 throw Error.parseError(ErrorCode.X_42611, null,
1535                                        scanner.getLineNumber());
1536             }
1537         }
1538 
1539         Type returnType   = new RowType(e.getNodeDataTypes());
1540         Type declaredType = routine.getReturnType();
1541 
1542         if (!declaredType.isRowType()) {
1543             declaredType = new RowType(new Type[]{ routine.getReturnType() });
1544         }
1545 
1546         if (declaredType.getDegree() != returnType.getDegree()) {
1547             throw Error.parseError(ErrorCode.X_42564, null,
1548                                    scanner.getLineNumber());
1549         }
1550 
1551         if (!declaredType.canBeAssignedFrom(returnType)) {
1552             throw Error.parseError(ErrorCode.X_42611, null,
1553                                    scanner.getLineNumber());
1554         }
1555 
1556         return new StatementExpression(session, compileContext,
1557                                        StatementTypes.RETURN, e);
1558     }
1559 
compileIterate()1560     Statement compileIterate() {
1561 
1562         readThis(Tokens.ITERATE);
1563 
1564         HsqlName label = readNewSchemaObjectName(SchemaObject.LABEL, false);
1565 
1566         return new StatementSimple(StatementTypes.ITERATE, label);
1567     }
1568 
compileLeave(Routine routine, StatementCompound context)1569     Statement compileLeave(Routine routine, StatementCompound context) {
1570 
1571         readThis(Tokens.LEAVE);
1572 
1573         HsqlName label = readNewSchemaObjectName(SchemaObject.LABEL, false);
1574 
1575         return new StatementSimple(StatementTypes.LEAVE, label);
1576     }
1577 
compileWhile(Routine routine, StatementCompound context, HsqlName label)1578     Statement compileWhile(Routine routine, StatementCompound context,
1579                            HsqlName label) {
1580 
1581         readThis(Tokens.WHILE);
1582 
1583         Expression e = XreadBooleanValueExpression();
1584 
1585         resolveOuterReferencesAndTypes(routine, context, e);
1586 
1587         StatementExpression condition = new StatementExpression(session,
1588             compileContext, StatementTypes.CONDITION, e);
1589 
1590         readThis(Tokens.DO);
1591 
1592         Statement[] statements = compileSQLProcedureStatementList(routine,
1593             context);
1594 
1595         readThis(Tokens.END);
1596         readThis(Tokens.WHILE);
1597 
1598         if (isSimpleName() && !isReservedKey()) {
1599             if (label == null) {
1600                 throw unexpectedToken();
1601             }
1602 
1603             if (!label.name.equals(token.tokenString)) {
1604                 throw Error.error(ErrorCode.X_42508, token.tokenString);
1605             }
1606 
1607             read();
1608         }
1609 
1610         StatementCompound statement =
1611             new StatementCompound(StatementTypes.WHILE, label, context);
1612 
1613         statement.setStatements(statements);
1614         statement.setCondition(condition);
1615 
1616         return statement;
1617     }
1618 
compileRepeat(Routine routine, StatementCompound context, HsqlName label)1619     Statement compileRepeat(Routine routine, StatementCompound context,
1620                             HsqlName label) {
1621 
1622         readThis(Tokens.REPEAT);
1623 
1624         Statement[] statements = compileSQLProcedureStatementList(routine,
1625             context);
1626 
1627         readThis(Tokens.UNTIL);
1628 
1629         Expression e = XreadBooleanValueExpression();
1630 
1631         resolveOuterReferencesAndTypes(routine, context, e);
1632 
1633         StatementExpression condition = new StatementExpression(session,
1634             compileContext, StatementTypes.CONDITION, e);
1635 
1636         readThis(Tokens.END);
1637         readThis(Tokens.REPEAT);
1638 
1639         if (isSimpleName() && !isReservedKey()) {
1640             if (label == null) {
1641                 throw unexpectedToken();
1642             }
1643 
1644             if (!label.name.equals(token.tokenString)) {
1645                 throw Error.error(ErrorCode.X_42508, token.tokenString);
1646             }
1647 
1648             read();
1649         }
1650 
1651         StatementCompound statement =
1652             new StatementCompound(StatementTypes.REPEAT, label, context);
1653 
1654         statement.setStatements(statements);
1655         statement.setCondition(condition);
1656 
1657         return statement;
1658     }
1659 
compileLoop(Routine routine, StatementCompound context, HsqlName label)1660     Statement compileLoop(Routine routine, StatementCompound context,
1661                           HsqlName label) {
1662 
1663         readThis(Tokens.LOOP);
1664 
1665         Statement[] statements = compileSQLProcedureStatementList(routine,
1666             context);
1667 
1668         readThis(Tokens.END);
1669         readThis(Tokens.LOOP);
1670 
1671         if (isSimpleName() && !isReservedKey()) {
1672             if (label == null) {
1673                 throw unexpectedToken();
1674             }
1675 
1676             if (!label.name.equals(token.tokenString)) {
1677                 throw Error.error(ErrorCode.X_42508, token.tokenString);
1678             }
1679 
1680             read();
1681         }
1682 
1683         StatementCompound result = new StatementCompound(StatementTypes.LOOP,
1684             label, context);
1685 
1686         result.setStatements(statements);
1687 
1688         return result;
1689     }
1690 
compileFor(Routine routine, StatementCompound context, HsqlName label)1691     Statement compileFor(Routine routine, StatementCompound context,
1692                          HsqlName label) {
1693 
1694         RangeGroup[] rangeGroups = new RangeGroup[1];
1695 
1696         rangeGroups[0] = context == null ? routine
1697                                          : context;
1698 
1699         compileContext.setOuterRanges(rangeGroups);
1700         readThis(Tokens.FOR);
1701 
1702         StatementQuery cursorStatement =
1703             compileCursorSpecification(rangeGroups,
1704                                        ResultProperties.defaultPropsValue,
1705                                        false);
1706 
1707         readThis(Tokens.DO);
1708 
1709         StatementCompound forStatement =
1710             new StatementCompound(StatementTypes.FOR, label, context);
1711 
1712         forStatement.setAtomic(true);
1713         forStatement.setRoot(routine);
1714         forStatement.setLoopStatement(null, cursorStatement);
1715 
1716         Statement[] statements = compileSQLProcedureStatementList(routine,
1717             forStatement);
1718 
1719         readThis(Tokens.END);
1720         readThis(Tokens.FOR);
1721 
1722         if (isSimpleName() && !isReservedKey()) {
1723             if (label == null) {
1724                 throw unexpectedToken();
1725             }
1726 
1727             if (!label.name.equals(token.tokenString)) {
1728                 throw Error.error(ErrorCode.X_42508, token.tokenString);
1729             }
1730 
1731             read();
1732         }
1733 
1734         forStatement.setStatements(statements);
1735 
1736         return forStatement;
1737     }
1738 
compileIf(Routine routine, StatementCompound context)1739     Statement compileIf(Routine routine, StatementCompound context) {
1740 
1741         HsqlArrayList list = new HsqlArrayList();
1742 
1743         readThis(Tokens.IF);
1744 
1745         Expression e = XreadBooleanValueExpression();
1746 
1747         resolveOuterReferencesAndTypes(routine, context, e);
1748 
1749         Statement statement = new StatementExpression(session, compileContext,
1750             StatementTypes.CONDITION, e);
1751 
1752         list.add(statement);
1753         readThis(Tokens.THEN);
1754 
1755         Statement[] statements = compileSQLProcedureStatementList(routine,
1756             context);
1757 
1758         for (int i = 0; i < statements.length; i++) {
1759             list.add(statements[i]);
1760         }
1761 
1762         while (token.tokenType == Tokens.ELSEIF) {
1763             read();
1764 
1765             e = XreadBooleanValueExpression();
1766 
1767             resolveOuterReferencesAndTypes(routine, context, e);
1768 
1769             statement = new StatementExpression(session, compileContext,
1770                                                 StatementTypes.CONDITION, e);
1771 
1772             list.add(statement);
1773             readThis(Tokens.THEN);
1774 
1775             statements = compileSQLProcedureStatementList(routine, context);
1776 
1777             for (int i = 0; i < statements.length; i++) {
1778                 list.add(statements[i]);
1779             }
1780         }
1781 
1782         if (token.tokenType == Tokens.ELSE) {
1783             read();
1784 
1785             e = Expression.EXPR_TRUE;
1786             statement = new StatementExpression(session, compileContext,
1787                                                 StatementTypes.CONDITION, e);
1788 
1789             list.add(statement);
1790 
1791             statements = compileSQLProcedureStatementList(routine, context);
1792 
1793             for (int i = 0; i < statements.length; i++) {
1794                 list.add(statements[i]);
1795             }
1796         }
1797 
1798         readThis(Tokens.END);
1799         readThis(Tokens.IF);
1800 
1801         statements = new Statement[list.size()];
1802 
1803         list.toArray(statements);
1804 
1805         StatementCompound result = new StatementCompound(StatementTypes.IF,
1806             null, context);
1807 
1808         result.setStatements(statements);
1809 
1810         return result;
1811     }
1812 
compileCase(Routine routine, StatementCompound context)1813     Statement compileCase(Routine routine, StatementCompound context) {
1814 
1815         HsqlArrayList list      = new HsqlArrayList();
1816         Expression    condition = null;
1817         Statement     statement;
1818         Statement[]   statements;
1819 
1820         readThis(Tokens.CASE);
1821 
1822         if (token.tokenType == Tokens.WHEN) {
1823             list = readCaseWhen(routine, context);
1824         } else {
1825             list = readSimpleCaseWhen(routine, context);
1826         }
1827 
1828         if (token.tokenType == Tokens.ELSE) {
1829             read();
1830 
1831             condition = Expression.EXPR_TRUE;
1832             statement = new StatementExpression(session, compileContext,
1833                                                 StatementTypes.CONDITION,
1834                                                 condition);
1835 
1836             list.add(statement);
1837 
1838             statements = compileSQLProcedureStatementList(routine, context);
1839 
1840             for (int i = 0; i < statements.length; i++) {
1841                 list.add(statements[i]);
1842             }
1843         }
1844 
1845         readThis(Tokens.END);
1846         readThis(Tokens.CASE);
1847 
1848         statements = new Statement[list.size()];
1849 
1850         list.toArray(statements);
1851 
1852         StatementCompound result = new StatementCompound(StatementTypes.IF,
1853             null, context);
1854 
1855         result.setStatements(statements);
1856 
1857         return result;
1858     }
1859 
readSimpleCaseWhen(Routine routine, StatementCompound context)1860     HsqlArrayList readSimpleCaseWhen(Routine routine,
1861                                      StatementCompound context) {
1862 
1863         HsqlArrayList list      = new HsqlArrayList();
1864         Expression    condition = null;
1865         Statement     statement;
1866         Statement[]   statements;
1867         Expression    predicand = XreadRowValuePredicand();
1868 
1869         do {
1870             readThis(Tokens.WHEN);
1871 
1872             do {
1873                 Expression newCondition = XreadPredicateRightPart(predicand);
1874 
1875                 if (predicand == newCondition) {
1876                     newCondition =
1877                         new ExpressionLogical(predicand,
1878                                               XreadRowValuePredicand());
1879                 }
1880 
1881                 resolveOuterReferencesAndTypes(routine, context, newCondition);
1882 
1883                 if (condition == null) {
1884                     condition = newCondition;
1885                 } else {
1886                     condition = new ExpressionLogical(OpTypes.OR, condition,
1887                                                       newCondition);
1888                 }
1889 
1890                 if (token.tokenType == Tokens.COMMA) {
1891                     read();
1892                 } else {
1893                     break;
1894                 }
1895             } while (true);
1896 
1897             statement = new StatementExpression(session, compileContext,
1898                                                 StatementTypes.CONDITION,
1899                                                 condition);
1900 
1901             list.add(statement);
1902             readThis(Tokens.THEN);
1903 
1904             statements = compileSQLProcedureStatementList(routine, context);
1905 
1906             for (int i = 0; i < statements.length; i++) {
1907                 list.add(statements[i]);
1908             }
1909 
1910             if (token.tokenType != Tokens.WHEN) {
1911                 break;
1912             }
1913         } while (true);
1914 
1915         return list;
1916     }
1917 
readCaseWhen(Routine routine, StatementCompound context)1918     HsqlArrayList readCaseWhen(Routine routine, StatementCompound context) {
1919 
1920         HsqlArrayList list      = new HsqlArrayList();
1921         Expression    condition = null;
1922         Statement     statement;
1923         Statement[]   statements;
1924 
1925         do {
1926             readThis(Tokens.WHEN);
1927 
1928             condition = XreadBooleanValueExpression();
1929 
1930             resolveOuterReferencesAndTypes(routine, context, condition);
1931 
1932             statement = new StatementExpression(session, compileContext,
1933                                                 StatementTypes.CONDITION,
1934                                                 condition);
1935 
1936             list.add(statement);
1937             readThis(Tokens.THEN);
1938 
1939             statements = compileSQLProcedureStatementList(routine, context);
1940 
1941             for (int i = 0; i < statements.length; i++) {
1942                 list.add(statements[i]);
1943             }
1944 
1945             if (token.tokenType != Tokens.WHEN) {
1946                 break;
1947             }
1948         } while (true);
1949 
1950         return list;
1951     }
1952 
compileSignal(Routine routine, StatementCompound context, HsqlName label)1953     Statement compileSignal(Routine routine, StatementCompound context,
1954                             HsqlName label) {
1955 
1956         String     sqlState;
1957         Expression message = null;
1958 
1959         readThis(Tokens.SIGNAL);
1960         readThis(Tokens.SQLSTATE);
1961 
1962         sqlState = parseSQLStateValue();
1963 
1964         if (readIfThis(Tokens.SET)) {
1965             readThis(Tokens.MESSAGE_TEXT);
1966             readThis(Tokens.EQUALS_OP);
1967 
1968             message = XreadSimpleValueSpecificationOrNull();
1969 
1970             if (message == null) {
1971                 throw unexpectedToken();
1972             }
1973 
1974             resolveOuterReferencesAndTypes(routine, context, message);
1975         }
1976 
1977         StatementSignal cs = new StatementSignal(StatementTypes.SIGNAL,
1978             sqlState, message);
1979 
1980         return cs;
1981     }
1982 
compileResignal(Routine routine, StatementCompound context, HsqlName label)1983     private Statement compileResignal(Routine routine,
1984                                       StatementCompound context,
1985                                       HsqlName label) {
1986 
1987         String     sqlState = null;
1988         Expression message  = null;
1989 
1990         readThis(Tokens.RESIGNAL);
1991 
1992         if (readIfThis(Tokens.SQLSTATE)) {
1993             sqlState = parseSQLStateValue();
1994 
1995             if (readIfThis(Tokens.SET)) {
1996                 readThis(Tokens.MESSAGE_TEXT);
1997                 readThis(Tokens.EQUALS_OP);
1998 
1999                 message = XreadSimpleValueSpecificationOrNull();
2000 
2001                 if (message == null) {
2002                     throw unexpectedToken();
2003                 }
2004 
2005                 resolveOuterReferencesAndTypes(routine, context, message);
2006             }
2007         }
2008 
2009         StatementSignal cs = new StatementSignal(StatementTypes.RESIGNAL,
2010             sqlState, message);
2011 
2012         return cs;
2013     }
2014 
readRoutineParameter(Routine routine, boolean isParam)2015     ColumnSchema readRoutineParameter(Routine routine, boolean isParam) {
2016 
2017         HsqlName hsqlName = null;
2018         int parameterMode = readRoutineParameterMode(routine.routineType,
2019             routine.isAggregate);
2020 
2021         if (!isReservedKey()) {
2022             hsqlName = readNewDependentSchemaObjectName(routine.getName(),
2023                     SchemaObject.PARAMETER);
2024         }
2025 
2026         Type typeObject = readTypeDefinition(false, true);
2027         ColumnSchema column = new ColumnSchema(hsqlName, typeObject, true,
2028                                                false, null);
2029 
2030         if (isParam) {
2031             column.setParameterMode((byte) parameterMode);
2032         }
2033 
2034         return column;
2035     }
2036 
readRoutineParameterMode(int routineType, boolean isAggregate)2037     int readRoutineParameterMode(int routineType, boolean isAggregate) {
2038 
2039         int parameterMode = SchemaObject.ParameterModes.PARAM_IN;
2040 
2041         switch (token.tokenType) {
2042 
2043             case Tokens.IN :
2044                 read();
2045                 break;
2046 
2047             case Tokens.OUT :
2048                 if (routineType != SchemaObject.PROCEDURE) {
2049                     throw unexpectedToken();
2050                 }
2051 
2052                 read();
2053 
2054                 parameterMode = SchemaObject.ParameterModes.PARAM_OUT;
2055                 break;
2056 
2057             case Tokens.INOUT :
2058                 if (routineType != SchemaObject.PROCEDURE) {
2059                     if (!isAggregate) {
2060                         throw unexpectedToken();
2061                     }
2062                 }
2063 
2064                 read();
2065 
2066                 parameterMode = SchemaObject.ParameterModes.PARAM_INOUT;
2067                 break;
2068 
2069             default :
2070         }
2071 
2072         return parameterMode;
2073     }
2074 
resolveOuterReferencesAndTypes(Routine routine, StatementCompound context, Expression e)2075     void resolveOuterReferencesAndTypes(Routine routine,
2076                                         StatementCompound context,
2077                                         Expression e) {
2078 
2079         RangeGroup rangeGroup = context == null ? routine
2080                                                 : context;
2081 
2082         resolveOuterReferencesAndTypes(new RangeGroup[]{ rangeGroup }, e);
2083     }
2084 
compileCreateTrigger(boolean orReplace)2085     StatementSchema compileCreateTrigger(boolean orReplace) {
2086 
2087         Table          table;
2088         Boolean        isForEachRow = null;
2089         boolean        isNowait     = false;
2090         boolean        hasQueueSize = false;
2091         int            queueSize    = 0;
2092         int            beforeOrAfterType;
2093         int            operationType;
2094         String         className;
2095         TriggerDef     td;
2096         HsqlName       name;
2097         HsqlName       otherName           = null;
2098         OrderedHashSet columns             = null;
2099         int[]          updateColumnIndexes = null;
2100 
2101         read();
2102 
2103         name = readNewSchemaObjectName(SchemaObject.TRIGGER, true);
2104 
2105         switch (token.tokenType) {
2106 
2107             case Tokens.INSTEAD :
2108                 beforeOrAfterType = TriggerDef.getTiming(Tokens.INSTEAD);
2109 
2110                 read();
2111                 readThis(Tokens.OF);
2112                 break;
2113 
2114             case Tokens.BEFORE :
2115             case Tokens.AFTER :
2116                 beforeOrAfterType = TriggerDef.getTiming(token.tokenType);
2117 
2118                 read();
2119                 break;
2120 
2121             default :
2122                 throw unexpectedToken();
2123         }
2124 
2125         switch (token.tokenType) {
2126 
2127             case Tokens.INSERT :
2128             case Tokens.DELETE :
2129                 operationType = TriggerDef.getOperationType(token.tokenType);
2130 
2131                 read();
2132                 break;
2133 
2134             case Tokens.UPDATE :
2135                 operationType = TriggerDef.getOperationType(token.tokenType);
2136 
2137                 read();
2138 
2139                 if (token.tokenType == Tokens.OF
2140                         && beforeOrAfterType != TriggerDef.INSTEAD) {
2141                     read();
2142 
2143                     columns = new OrderedHashSet();
2144 
2145                     readColumnNameList(columns, null, false);
2146                 }
2147                 break;
2148 
2149             default :
2150                 throw unexpectedToken();
2151         }
2152 
2153         readThis(Tokens.ON);
2154 
2155         table = readTableName();
2156 
2157         if (token.tokenType == Tokens.BEFORE) {
2158             read();
2159             checkIsSimpleName();
2160 
2161             otherName = readNewSchemaObjectName(SchemaObject.TRIGGER, true);
2162         }
2163 
2164         name.setSchemaIfNull(table.getSchemaName());
2165         checkSchemaUpdateAuthorisation(name.schema);
2166 
2167         if (beforeOrAfterType == TriggerDef.INSTEAD) {
2168             if (!table.isView()
2169                     || ((View) table).getCheckOption()
2170                        == SchemaObject.ViewCheckModes.CHECK_CASCADE) {
2171                 throw Error.error(ErrorCode.X_42538, name.schema.name);
2172             }
2173         } else {
2174             if (table.isView()) {
2175                 throw Error.error(ErrorCode.X_42538, name.schema.name);
2176             }
2177         }
2178 
2179         if (name.schema != table.getSchemaName()) {
2180             throw Error.error(ErrorCode.X_42505, name.schema.name);
2181         }
2182 
2183         name.parent = table.getName();
2184 
2185         database.schemaManager.checkSchemaObjectNotExists(name);
2186 
2187         if (columns != null) {
2188             updateColumnIndexes = table.getColumnIndexes(columns);
2189 
2190             for (int i = 0; i < updateColumnIndexes.length; i++) {
2191                 if (updateColumnIndexes[i] == -1) {
2192                     throw Error.error(ErrorCode.X_42544,
2193                                       (String) columns.get(i));
2194                 }
2195             }
2196         }
2197 
2198         Expression      condition    = null;
2199         SimpleName      oldTableName = null;
2200         SimpleName      newTableName = null;
2201         SimpleName      oldRowName   = null;
2202         SimpleName      newRowName   = null;
2203         Table[]         transitions  = new Table[4];
2204         RangeVariable[] rangeVars    = new RangeVariable[4];
2205         String          conditionSQL = null;
2206         RangeGroup[] rangeGroups = new RangeGroup[]{
2207             new RangeGroup.RangeGroupSimple(rangeVars, false) };
2208 
2209         if (token.tokenType == Tokens.REFERENCING) {
2210             read();
2211 
2212             if (token.tokenType != Tokens.OLD
2213                     && token.tokenType != Tokens.NEW) {
2214                 throw unexpectedToken();
2215             }
2216 
2217             while (true) {
2218                 if (token.tokenType == Tokens.OLD) {
2219                     if (operationType == StatementTypes.INSERT) {
2220                         throw unexpectedToken();
2221                     }
2222 
2223                     read();
2224 
2225                     if (token.tokenType == Tokens.TABLE) {
2226                         if (Boolean.TRUE.equals(isForEachRow)
2227                                 || oldTableName != null
2228                                 || beforeOrAfterType == TriggerDef.BEFORE) {
2229                             throw unexpectedToken();
2230                         }
2231 
2232                         read();
2233                         readIfThis(Tokens.AS);
2234                         checkIsSimpleName();
2235                         read();
2236 
2237                         oldTableName = HsqlNameManager.getSimpleName(
2238                             token.tokenString, token.isDelimitedIdentifier);
2239 
2240                         SimpleName n = oldTableName;
2241 
2242                         if (n.equals(newTableName) || n.equals(oldRowName)
2243                                 || n.equals(newRowName)) {
2244                             throw unexpectedToken();
2245                         }
2246 
2247                         isForEachRow = Boolean.FALSE;
2248 
2249                         HsqlName hsqlName = database.nameManager.newHsqlName(
2250                             table.getSchemaName(), n.name,
2251                             isDelimitedIdentifier(), SchemaObject.TRANSITION);
2252                         Table transition = new Table(table, hsqlName);
2253                         RangeVariable range = new RangeVariable(transition,
2254                             null, null, null, compileContext);
2255 
2256                         transitions[TriggerDef.OLD_TABLE] = transition;
2257                         rangeVars[TriggerDef.OLD_TABLE]   = range;
2258                     } else {
2259                         if (Boolean.FALSE.equals(isForEachRow)
2260                                 || oldRowName != null) {
2261                             throw unexpectedToken();
2262                         }
2263 
2264                         readIfThis(Tokens.ROW);
2265                         readIfThis(Tokens.AS);
2266                         checkIsSimpleName();
2267 
2268                         oldRowName = HsqlNameManager.getSimpleName(
2269                             token.tokenString, token.isDelimitedIdentifier);
2270 
2271                         read();
2272 
2273                         SimpleName n = oldRowName;
2274 
2275                         if (n.equals(newTableName) || n.equals(oldTableName)
2276                                 || n.equals(newRowName)) {
2277                             throw unexpectedToken();
2278                         }
2279 
2280                         isForEachRow = Boolean.TRUE;
2281 
2282                         RangeVariable range =
2283                             new RangeVariable(table.columnList, oldRowName,
2284                                               false,
2285                                               RangeVariable.TRANSITION_RANGE);
2286 
2287                         range.rangePosition             = TriggerDef.OLD_ROW;
2288                         transitions[TriggerDef.OLD_ROW] = null;
2289                         rangeVars[TriggerDef.OLD_ROW]   = range;
2290                     }
2291                 } else if (token.tokenType == Tokens.NEW) {
2292                     if (operationType == StatementTypes.DELETE_WHERE) {
2293                         throw unexpectedToken();
2294                     }
2295 
2296                     read();
2297 
2298                     if (token.tokenType == Tokens.TABLE) {
2299                         if (Boolean.TRUE.equals(isForEachRow)
2300                                 || newTableName != null
2301                                 || beforeOrAfterType == TriggerDef.BEFORE) {
2302                             throw unexpectedToken();
2303                         }
2304 
2305                         read();
2306                         readIfThis(Tokens.AS);
2307                         checkIsSimpleName();
2308 
2309                         newTableName = HsqlNameManager.getSimpleName(
2310                             token.tokenString, token.isDelimitedIdentifier);
2311 
2312                         read();
2313 
2314                         isForEachRow = Boolean.FALSE;
2315 
2316                         SimpleName n = newTableName;
2317 
2318                         if (n.equals(oldTableName) || n.equals(oldRowName)
2319                                 || n.equals(newRowName)) {
2320                             throw unexpectedToken();
2321                         }
2322 
2323                         HsqlName hsqlName = database.nameManager.newHsqlName(
2324                             table.getSchemaName(), n.name,
2325                             isDelimitedIdentifier(), SchemaObject.TRANSITION);
2326                         Table transition = new Table(table, hsqlName);
2327                         RangeVariable range = new RangeVariable(transition,
2328                             null, null, null, compileContext);
2329 
2330                         transitions[TriggerDef.NEW_TABLE] = transition;
2331                         rangeVars[TriggerDef.NEW_TABLE]   = range;
2332                     } else {
2333                         if (Boolean.FALSE.equals(isForEachRow)
2334                                 || newRowName != null) {
2335                             throw unexpectedToken();
2336                         }
2337 
2338                         readIfThis(Tokens.ROW);
2339                         readIfThis(Tokens.AS);
2340                         checkIsSimpleName();
2341 
2342                         newRowName = HsqlNameManager.getSimpleName(
2343                             token.tokenString, token.isDelimitedIdentifier);
2344 
2345                         read();
2346 
2347                         SimpleName n = newRowName;
2348 
2349                         if (n.equals(oldTableName) || n.equals(newTableName)
2350                                 || n.equals(oldRowName)) {
2351                             throw unexpectedToken();
2352                         }
2353 
2354                         isForEachRow = Boolean.TRUE;
2355 
2356                         RangeVariable range =
2357                             new RangeVariable(table.columnList, newRowName,
2358                                               false,
2359                                               RangeVariable.TRANSITION_RANGE);
2360 
2361                         range.rangePosition             = TriggerDef.NEW_ROW;
2362                         transitions[TriggerDef.NEW_ROW] = null;
2363                         rangeVars[TriggerDef.NEW_ROW]   = range;
2364                     }
2365                 } else {
2366                     break;
2367                 }
2368             }
2369         }
2370 
2371         if (Boolean.TRUE.equals(isForEachRow)
2372                 && token.tokenType != Tokens.FOR) {
2373             throw unexpectedTokenRequire(Tokens.T_FOR);
2374         }
2375 
2376         if (token.tokenType == Tokens.FOR) {
2377             read();
2378             readThis(Tokens.EACH);
2379 
2380             if (token.tokenType == Tokens.ROW) {
2381                 if (Boolean.FALSE.equals(isForEachRow)) {
2382                     throw unexpectedToken();
2383                 }
2384 
2385                 isForEachRow = Boolean.TRUE;
2386             } else if (token.tokenType == Tokens.STATEMENT) {
2387                 if (Boolean.TRUE.equals(isForEachRow)
2388                         || beforeOrAfterType == TriggerDef.BEFORE) {
2389                     throw unexpectedToken();
2390                 }
2391 
2392                 isForEachRow = Boolean.FALSE;
2393             } else {
2394                 throw unexpectedToken();
2395             }
2396 
2397             read();
2398         }
2399 
2400         //
2401         if (rangeVars[TriggerDef.OLD_TABLE] != null) {}
2402 
2403         if (rangeVars[TriggerDef.NEW_TABLE] != null) {}
2404 
2405         //
2406         if (Tokens.T_QUEUE.equals(token.tokenString)) {
2407             read();
2408 
2409             queueSize    = readInteger();
2410             hasQueueSize = true;
2411         }
2412 
2413         if (Tokens.T_NOWAIT.equals(token.tokenString)) {
2414             read();
2415 
2416             isNowait = true;
2417         }
2418 
2419         if (token.tokenType == Tokens.WHEN
2420                 && beforeOrAfterType != TriggerDef.INSTEAD) {
2421             read();
2422             readThis(Tokens.OPENBRACKET);
2423 
2424             int position = getPosition();
2425 
2426             isCheckOrTriggerCondition = true;
2427             condition                 = XreadBooleanValueExpression();
2428             conditionSQL              = getLastPart(position);
2429             isCheckOrTriggerCondition = false;
2430 
2431             readThis(Tokens.CLOSEBRACKET);
2432 
2433             HsqlList unresolved = condition.resolveColumnReferences(session,
2434                 rangeGroups[0], rangeGroups, null);
2435 
2436             ExpressionColumn.checkColumnsResolved(unresolved);
2437             condition.resolveTypes(session, null);
2438 
2439             if (condition.getDataType() != Type.SQL_BOOLEAN) {
2440                 throw Error.error(ErrorCode.X_42568);
2441             }
2442         }
2443 
2444         if (isForEachRow == null) {
2445             isForEachRow = Boolean.FALSE;
2446         }
2447 
2448         if (token.tokenType == Tokens.CALL) {
2449             int position = getPosition();
2450 
2451             try {
2452                 read();
2453                 checkIsSimpleName();
2454                 checkIsDelimitedIdentifier();
2455 
2456                 className = token.tokenString;
2457 
2458                 read();
2459 
2460                 if (token.tokenType == Tokens.OPENBRACKET) {
2461                     throw unexpectedToken();
2462                 }
2463 
2464                 td = new TriggerDef(name, beforeOrAfterType, operationType,
2465                                     isForEachRow.booleanValue(), table,
2466                                     transitions, rangeVars, condition,
2467                                     conditionSQL, updateColumnIndexes,
2468                                     className, isNowait, queueSize);
2469 
2470                 String     sql            = getLastPart();
2471                 Object[]   args           = new Object[] {
2472                     td, otherName
2473                 };
2474                 HsqlName[] writeLockNames = new HsqlName[] {
2475                     database.getCatalogName(), table.getName()
2476                 };
2477 
2478                 return new StatementSchema(sql, StatementTypes.CREATE_TRIGGER,
2479                                            args, null, writeLockNames);
2480             } catch (HsqlException e) {
2481                 rewind(position);
2482             }
2483         }
2484 
2485         //
2486         if (hasQueueSize) {
2487             throw unexpectedToken(Tokens.T_QUEUE);
2488         }
2489 
2490         if (isNowait) {
2491             throw unexpectedToken(Tokens.T_NOWAIT);
2492         }
2493 
2494         Routine routine = compileTriggerRoutine(table, rangeVars,
2495             beforeOrAfterType, operationType);
2496 
2497         td = new TriggerDefSQL(name, beforeOrAfterType, operationType,
2498                                isForEachRow.booleanValue(), table,
2499                                transitions, rangeVars, condition,
2500                                conditionSQL, updateColumnIndexes, routine);
2501 
2502         String   sql  = getLastPart();
2503         Object[] args = new Object[] {
2504             td, otherName
2505         };
2506 
2507         return new StatementSchema(sql, StatementTypes.CREATE_TRIGGER, args,
2508                                    null, new HsqlName[] {
2509             database.getCatalogName(), table.getName()
2510         });
2511     }
2512 
compileTriggerRoutine(Table table, RangeVariable[] ranges, int beforeOrAfter, int operation)2513     Routine compileTriggerRoutine(Table table, RangeVariable[] ranges,
2514                                   int beforeOrAfter, int operation) {
2515 
2516         int impact = (beforeOrAfter == TriggerDef.BEFORE) ? Routine.READS_SQL
2517                                                           : Routine
2518                                                               .MODIFIES_SQL;
2519         Routine routine = new Routine(table, ranges, impact, beforeOrAfter,
2520                                       operation);
2521 
2522         session.sessionContext.pushRoutineTables();
2523 
2524         try {
2525             startRecording();
2526 
2527             StatementCompound parent =
2528                 new StatementCompound(StatementTypes.BEGIN_END, null, null);
2529 
2530             parent.rangeVariables = ranges;
2531 
2532             Statement statement = compileSQLProcedureStatementOrNull(routine,
2533                 null);
2534 
2535             if (statement == null) {
2536                 throw unexpectedToken();
2537             }
2538 
2539             Token[] tokenisedStatement = getRecordedStatement();
2540             String  sql                = Token.getSQL(tokenisedStatement);
2541 
2542             statement.setSQL(sql);
2543             routine.setProcedure(statement);
2544             routine.resolve(session);
2545         } finally {
2546             session.sessionContext.popRoutineTables();
2547         }
2548 
2549         return routine;
2550     }
2551 
checkSchemaUpdateAuthorisation(HsqlName schema)2552     void checkSchemaUpdateAuthorisation(HsqlName schema) {
2553 
2554         if (session.isProcessingLog()) {
2555             return;
2556         }
2557 
2558         SqlInvariants.checkSchemaNameNotSystem(schema.name);
2559 
2560         if (isSchemaDefinition) {
2561             if (schema != session.getCurrentSchemaHsqlName()) {
2562                 throw Error.error(ErrorCode.X_42505);
2563             }
2564         } else {
2565             session.getGrantee().checkSchemaUpdateOrGrantRights(schema.name);
2566         }
2567 
2568         session.checkDDLWrite();
2569     }
2570 }
2571