EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.command]

COVERAGE SUMMARY FOR SOURCE FILE [Parser.java]

nameclass, %method, %block, %line, %
Parser.java100% (2/2)98%  (160/163)92%  (13962/15168)92%  (3570.4/3869)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Parser100% (1/1)98%  (158/161)92%  (13947/15153)92%  (3568.4/3867)
parseReleaseSavepoint (): Prepared 0%   (0/1)0%   (0/15)0%   (0/4)
parseReplace (): Replace 0%   (0/1)0%   (0/90)0%   (0/24)
parseTableName (String): Table 0%   (0/1)0%   (0/11)0%   (0/4)
readLong (): long 100% (1/1)56%  (24/43)62%  (8/13)
readPositiveInt (): int 100% (1/1)58%  (7/12)75%  (3/4)
readColumnIdentifier (): String 100% (1/1)61%  (11/18)80%  (4/5)
parseSetBinaryCollation (): Set 100% (1/1)69%  (20/29)80%  (4.8/6)
checkSchema (Schema): void 100% (1/1)70%  (7/10)67%  (2/3)
readString (): String 100% (1/1)71%  (17/24)80%  (4/5)
readCase (): Expression 100% (1/1)72%  (107/149)69%  (27/39)
readInt (): int 100% (1/1)72%  (31/43)69%  (9/13)
parseAlterSequence (): AlterSequence 100% (1/1)72%  (112/155)72%  (26/36)
parseCreateUser (): CreateUser 100% (1/1)75%  (52/69)76%  (13/17)
equalsToken (String, String): boolean 100% (1/1)76%  (19/25)86%  (6/7)
readCondition (): Expression 100% (1/1)78%  (412/526)78%  (91.3/117)
prepare (String): Prepared 100% (1/1)80%  (12/15)80%  (4/5)
parseSetCollation (): Set 100% (1/1)80%  (57/71)79%  (15/19)
parse (String): Prepared 100% (1/1)81%  (25/31)89%  (8/9)
parseWith (): Query 100% (1/1)84%  (149/177)86%  (37.9/44)
parseSet (): Prepared 100% (1/1)84%  (621/735)83%  (165/198)
initialize (String): void 100% (1/1)85%  (434/508)86%  (112.8/131)
getTokenType (String): int 100% (1/1)86%  (18/21)83%  (5/6)
readTableColumn (TableFilter): Column 100% (1/1)86%  (76/88)88%  (21/24)
parseExplain (): Explain 100% (1/1)87%  (77/89)84%  (16/19)
isKeyword (String, boolean): boolean 100% (1/1)88%  (14/16)67%  (2/3)
readTerm (): Expression 100% (1/1)88%  (685/778)89%  (171/193)
parseAlterView (): AlterView 100% (1/1)88%  (30/34)89%  (8/9)
parseShow (): Prepared 100% (1/1)89%  (147/166)95%  (35.3/37)
parseEndOfQuery (Query): void 100% (1/1)89%  (257/290)89%  (68/76)
readJoin (TableFilter, Select, boolean, boolean): TableFilter 100% (1/1)89%  (351/395)91%  (85/93)
parseColumnWithType (String): Column 100% (1/1)89%  (348/391)90%  (80/89)
readSimpleTableFilter (): TableFilter 100% (1/1)89%  (34/38)88%  (7/8)
parseCreateConstant (): CreateConstant 100% (1/1)90%  (37/41)92%  (11/12)
readTermObjectDot (String): Expression 100% (1/1)91%  (125/137)91%  (29.9/33)
readIdentifierWithSchema (String): String 100% (1/1)91%  (73/80)95%  (19/20)
parseExecute (): Prepared 100% (1/1)91%  (42/46)92%  (12/13)
parseMerge (): Merge 100% (1/1)91%  (95/104)89%  (25/28)
readBooleanSetting (): boolean 100% (1/1)92%  (33/36)89%  (8/9)
readDecimal (int, int): void 100% (1/1)92%  (121/132)94%  (32/34)
parseComment (): Prepared 100% (1/1)92%  (194/211)95%  (52/55)
parseCreateAggregate (boolean): CreateAggregate 100% (1/1)92%  (47/51)92%  (11/12)
parseAlterUser (): AlterUser 100% (1/1)93%  (118/127)91%  (30/33)
readJavaFunction (Schema, String): JavaFunction 100% (1/1)93%  (53/57)94%  (15/16)
parsePrepared (): Prepared 100% (1/1)93%  (439/472)92%  (110/119)
parseSelectSimple (): Select 100% (1/1)93%  (135/145)93%  (39/42)
readTableFilter (boolean): TableFilter 100% (1/1)93%  (248/266)93%  (57/61)
parseValuesTable (): TableFilter 100% (1/1)93%  (250/268)95%  (56/59)
parseAlter (): Prepared 100% (1/1)93%  (42/45)92%  (12/13)
parseCreateTable (boolean, boolean, boolean): CreateTable 100% (1/1)94%  (484/513)95%  (121/128)
parseReferences (AlterTableAddConstraint, Schema, String): void 100% (1/1)95%  (76/80)95%  (18/19)
addRoleOrRight (GrantRevoke): boolean 100% (1/1)95%  (77/81)93%  (25/27)
parseCreateSequence (): CreateSequence 100% (1/1)95%  (155/163)95%  (37/39)
parseCreate (): Prepared 100% (1/1)95%  (349/367)96%  (94/98)
findFunctionAlias (String, String): FunctionAlias 100% (1/1)96%  (44/46)90%  (9/10)
parseInsert (): Insert 100% (1/1)96%  (222/231)95%  (59/62)
parseColumnForTable (String, boolean): Column 100% (1/1)96%  (260/270)96%  (68/71)
readFunction (Schema, String): Expression 100% (1/1)97%  (408/422)97%  (114.9/118)
getSaveTokenType (String, boolean): int 100% (1/1)97%  (220/226)95%  (59/62)
parseDrop (): Prepared 100% (1/1)98%  (410/419)97%  (108/111)
parseCreateTrigger (boolean): CreateTrigger 100% (1/1)98%  (178/181)98%  (51/52)
parseIndexColumnList (): IndexColumn [] 100% (1/1)98%  (63/64)100% (14.9/15)
readWildcardOrSequenceValue (String, String): Expression 100% (1/1)99%  (69/70)99%  (16.8/17)
parseCreateFunctionAlias (boolean): CreateFunctionAlias 100% (1/1)99%  (71/72)100% (15/15)
parseAlterTable (): Prepared 100% (1/1)99%  (555/562)98%  (140.8/143)
parseAlterTableAddConstraintIf (String, Schema): DefineCommand 100% (1/1)99%  (310/313)99%  (80/81)
read (): void 100% (1/1)99%  (456/459)99%  (120/121)
Parser (Session): void 100% (1/1)100% (16/16)100% (5/5)
addExpected (String): void 100% (1/1)100% (9/9)100% (3/3)
checkLiterals (boolean): void 100% (1/1)100% (19/19)100% (5/5)
checkRunOver (int, int, int): void 100% (1/1)100% (10/10)100% (4/4)
findSequence (String, String): Sequence 100% (1/1)100% (46/46)100% (10/10)
getAggregateType (String): int 100% (1/1)100% (9/9)100% (3/3)
getCompareType (int): int 100% (1/1)100% (18/18)100% (9/9)
getDualTable (boolean): Table 100% (1/1)100% (17/17)100% (3/3)
getKeywordOrIdentifier (String, String, int): int 100% (1/1)100% (8/8)100% (3/3)
getNested (TableFilter): TableFilter 100% (1/1)100% (32/32)100% (4/4)
getSchema (): Schema 100% (1/1)100% (5/5)100% (1/1)
getSchema (String): Schema 100% (1/1)100% (40/40)100% (9/9)
getSession (): Session 100% (1/1)100% (3/3)100% (1/1)
getSpecialType (String): int 100% (1/1)100% (94/94)100% (35/35)
getSyntaxError (): DbException 100% (1/1)100% (44/44)100% (8/8)
isKeyword (String): boolean 100% (1/1)100% (10/10)100% (3/3)
isSelect (): boolean 100% (1/1)100% (27/27)100% (6/6)
isToken (String): boolean 100% (1/1)100% (22/22)100% (5/5)
parse (String, boolean): Prepared 100% (1/1)100% (35/35)100% (12/12)
parseAction (): int 100% (1/1)100% (31/31)100% (11/11)
parseAlterIndex (): AlterIndexRename 100% (1/1)100% (37/37)100% (10/10)
parseAlterSchema (): AlterSchemaRename 100% (1/1)100% (36/36)100% (10/10)
parseAlterTableAddColumn (Table): AlterTableAlterColumn 100% (1/1)100% (92/92)100% (25/25)
parseAlterTableAlterColumnType (Table, String, Column): AlterTableAlterColumn 100% (1/1)100% (28/28)100% (7/7)
parseAnalyze (): Prepared 100% (1/1)100% (16/16)100% (4/4)
parseAutoIncrement (Column): void 100% (1/1)100% (27/27)100% (8/8)
parseBackup (): Prepared 100% (1/1)100% (15/15)100% (4/4)
parseBegin (): TransactionCommand 100% (1/1)100% (17/17)100% (4/4)
parseCall (): Call 100% (1/1)100% (15/15)100% (4/4)
parseCascadeOrRestrict (): Integer 100% (1/1)100% (16/16)100% (5/5)
parseCheckpoint (): TransactionCommand 100% (1/1)100% (21/21)100% (4/4)
parseColumn (Table): Column 100% (1/1)100% (19/19)100% (4/4)
parseColumnList (): String [] 100% (1/1)100% (19/19)100% (5/5)
parseColumnList (Table): Column [] 100% (1/1)100% (35/35)100% (9/9)
parseCommit (): TransactionCommand 100% (1/1)100% (30/30)100% (7/7)
parseCreateLinkedTable (boolean, boolean, boolean): CreateLinkedTable 100% (1/1)100% (106/106)100% (31/31)
parseCreateRole (): CreateRole 100% (1/1)100% (16/16)100% (4/4)
parseCreateSchema (): CreateSchema 100% (1/1)100% (31/31)100% (7/7)
parseCreateUserDataType (): CreateUserDataType 100% (1/1)100% (44/44)100% (12/12)
parseCreateView (boolean, boolean): CreateView 100% (1/1)100% (79/79)100% (25/25)
parseDeallocate (): DeallocateProcedure 100% (1/1)100% (18/18)100% (5/5)
parseDelete (): Delete 100% (1/1)100% (66/66)100% (17/17)
parseDropAggregate (): DropAggregate 100% (1/1)100% (23/23)100% (6/6)
parseDropUserDataType (): DropUserDataType 100% (1/1)100% (23/23)100% (6/6)
parseExpression (String): Expression 100% (1/1)100% (11/11)100% (4/4)
parseGrantRevoke (int): GrantRevoke 100% (1/1)100% (62/62)100% (17/17)
parseHelp (): Prepared 100% (1/1)100% (56/56)100% (14/14)
parseIsolationClause (): void 100% (1/1)100% (47/47)100% (9/9)
parseJoinTableFilter (TableFilter, Select): void 100% (1/1)100% (64/64)100% (21/21)
parsePrepare (): Prepared 100% (1/1)100% (74/74)100% (19/19)
parseRollback (): TransactionCommand 100% (1/1)100% (49/49)100% (11/11)
parseRunScript (): RunScriptCommand 100% (1/1)100% (48/48)100% (12/12)
parseSavepoint (): TransactionCommand 100% (1/1)100% (13/13)100% (3/3)
parseScript (): ScriptCommand 100% (1/1)100% (151/151)100% (42/42)
parseSelect (): Query 100% (1/1)100% (34/34)100% (8/8)
parseSelectSimpleFromPart (Select): void 100% (1/1)100% (13/13)100% (4/4)
parseSelectSimpleSelectPart (Select): void 100% (1/1)100% (117/117)100% (27/27)
parseSelectSub (): Query 100% (1/1)100% (17/17)100% (6/6)
parseSelectUnion (): Query 100% (1/1)100% (12/12)100% (3/3)
parseSelectUnionExtension (Query, int, boolean): Query 100% (1/1)100% (91/91)100% (23/23)
parseSetJavaObjectSerializer (): Set 100% (1/1)100% (15/15)100% (4/4)
parseShutdown (): TransactionCommand 100% (1/1)100% (34/34)100% (9/9)
parseSimpleOrderList (): ArrayList 100% (1/1)100% (34/34)100% (10/10)
parseTruncate (): Prepared 100% (1/1)100% (17/17)100% (5/5)
parseUpdate (): Update 100% (1/1)100% (164/164)100% (42/42)
parseUse (): Prepared 100% (1/1)100% (15/15)100% (4/4)
parseValues (): Select 100% (1/1)100% (33/33)100% (9/9)
prepare (Session, String, ArrayList): Prepared 100% (1/1)100% (32/32)100% (7/7)
prepareCommand (String): Command 100% (1/1)100% (56/56)100% (14/14)
quoteIdentifier (String): String 100% (1/1)100% (58/58)100% (12/12)
read (String): void 100% (1/1)100% (18/18)100% (5/5)
readAggregate (int): Expression 100% (1/1)100% (116/116)100% (26/26)
readAliasIdentifier (): String 100% (1/1)100% (3/3)100% (1/1)
readAnd (): Expression 100% (1/1)100% (18/18)100% (4/4)
readCommentIf (): String 100% (1/1)100% (13/13)100% (4/4)
readConcat (): Expression 100% (1/1)100% (97/97)100% (18/18)
readExpression (): Expression 100% (1/1)100% (18/18)100% (4/4)
readFactor (): Expression 100% (1/1)100% (44/44)100% (8/8)
readFromAlias (String): String 100% (1/1)100% (29/29)100% (6/6)
readFunctionWithoutParameters (String): Function 100% (1/1)100% (16/16)100% (5/5)
readHexDecimal (int, int): void 100% (1/1)100% (49/49)100% (10/10)
readIdentifierWithSchema (): String 100% (1/1)100% (6/6)100% (1/1)
readIf (String): boolean 100% (1/1)100% (18/18)100% (5/5)
readIfEqualOrTo (): void 100% (1/1)100% (9/9)100% (3/3)
readIfExists (boolean): boolean 100% (1/1)100% (11/11)100% (4/4)
readIfMore (): boolean 100% (1/1)100% (17/17)100% (4/4)
readIfNoExists (): boolean 100% (1/1)100% (14/14)100% (5/5)
readJavaAggregate (UserAggregate): JavaAggregate 100% (1/1)100% (35/35)100% (9/9)
readSequence (): Sequence 100% (1/1)100% (27/27)100% (7/7)
readSum (): Expression 100% (1/1)100% (31/31)100% (6/6)
readTableOrView (): Table 100% (1/1)100% (6/6)100% (1/1)
readTableOrView (String): Table 100% (1/1)100% (66/66)100% (13/13)
readUniqueIdentifier (): String 100% (1/1)100% (3/3)100% (1/1)
setRightsChecked (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setSQL (Prepared, String, int): void 100% (1/1)100% (25/25)100% (5/5)
     
class Parser$1100% (1/1)100% (2/2)100% (15/15)100% (3/3)
Parser$1 (Parser, Select): void 100% (1/1)100% (9/9)100% (1/1)
accept (TableFilter): void 100% (1/1)100% (6/6)100% (2/2)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 *
6 * Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
7 * Support for the operator "&&" as an alias for SPATIAL_INTERSECTS
8 */
9package org.h2.command;
10 
11import java.math.BigDecimal;
12import java.math.BigInteger;
13import java.nio.charset.Charset;
14import java.text.Collator;
15import java.util.ArrayList;
16import java.util.HashSet;
17 
18import org.h2.api.ErrorCode;
19import org.h2.api.Trigger;
20import org.h2.command.ddl.AlterIndexRename;
21import org.h2.command.ddl.AlterSchemaRename;
22import org.h2.command.ddl.AlterTableAddConstraint;
23import org.h2.command.ddl.AlterTableAlterColumn;
24import org.h2.command.ddl.AlterTableDropConstraint;
25import org.h2.command.ddl.AlterTableRename;
26import org.h2.command.ddl.AlterTableRenameColumn;
27import org.h2.command.ddl.AlterUser;
28import org.h2.command.ddl.AlterView;
29import org.h2.command.ddl.Analyze;
30import org.h2.command.ddl.CreateAggregate;
31import org.h2.command.ddl.CreateConstant;
32import org.h2.command.ddl.CreateFunctionAlias;
33import org.h2.command.ddl.CreateIndex;
34import org.h2.command.ddl.CreateLinkedTable;
35import org.h2.command.ddl.CreateRole;
36import org.h2.command.ddl.CreateSchema;
37import org.h2.command.ddl.CreateSequence;
38import org.h2.command.ddl.CreateTable;
39import org.h2.command.ddl.CreateTableData;
40import org.h2.command.ddl.CreateTrigger;
41import org.h2.command.ddl.CreateUser;
42import org.h2.command.ddl.CreateUserDataType;
43import org.h2.command.ddl.CreateView;
44import org.h2.command.ddl.DeallocateProcedure;
45import org.h2.command.ddl.DefineCommand;
46import org.h2.command.ddl.DropAggregate;
47import org.h2.command.ddl.DropConstant;
48import org.h2.command.ddl.DropDatabase;
49import org.h2.command.ddl.DropFunctionAlias;
50import org.h2.command.ddl.DropIndex;
51import org.h2.command.ddl.DropRole;
52import org.h2.command.ddl.DropSchema;
53import org.h2.command.ddl.DropSequence;
54import org.h2.command.ddl.DropTable;
55import org.h2.command.ddl.DropTrigger;
56import org.h2.command.ddl.DropUser;
57import org.h2.command.ddl.DropUserDataType;
58import org.h2.command.ddl.DropView;
59import org.h2.command.ddl.GrantRevoke;
60import org.h2.command.ddl.PrepareProcedure;
61import org.h2.command.ddl.SetComment;
62import org.h2.command.ddl.TruncateTable;
63import org.h2.command.dml.AlterSequence;
64import org.h2.command.dml.AlterTableSet;
65import org.h2.command.dml.BackupCommand;
66import org.h2.command.dml.Call;
67import org.h2.command.dml.Delete;
68import org.h2.command.dml.ExecuteProcedure;
69import org.h2.command.dml.Explain;
70import org.h2.command.dml.Insert;
71import org.h2.command.dml.Merge;
72import org.h2.command.dml.NoOperation;
73import org.h2.command.dml.Query;
74import org.h2.command.dml.Replace;
75import org.h2.command.dml.RunScriptCommand;
76import org.h2.command.dml.ScriptCommand;
77import org.h2.command.dml.Select;
78import org.h2.command.dml.SelectOrderBy;
79import org.h2.command.dml.SelectUnion;
80import org.h2.command.dml.Set;
81import org.h2.command.dml.SetTypes;
82import org.h2.command.dml.TransactionCommand;
83import org.h2.command.dml.Update;
84import org.h2.constraint.ConstraintReferential;
85import org.h2.engine.Constants;
86import org.h2.engine.Database;
87import org.h2.engine.DbObject;
88import org.h2.engine.FunctionAlias;
89import org.h2.engine.Procedure;
90import org.h2.engine.Right;
91import org.h2.engine.Session;
92import org.h2.engine.SysProperties;
93import org.h2.engine.User;
94import org.h2.engine.UserAggregate;
95import org.h2.engine.UserDataType;
96import org.h2.expression.Aggregate;
97import org.h2.expression.Alias;
98import org.h2.expression.CompareLike;
99import org.h2.expression.Comparison;
100import org.h2.expression.ConditionAndOr;
101import org.h2.expression.ConditionExists;
102import org.h2.expression.ConditionIn;
103import org.h2.expression.ConditionInSelect;
104import org.h2.expression.ConditionNot;
105import org.h2.expression.Expression;
106import org.h2.expression.ExpressionColumn;
107import org.h2.expression.ExpressionList;
108import org.h2.expression.Function;
109import org.h2.expression.FunctionCall;
110import org.h2.expression.JavaAggregate;
111import org.h2.expression.JavaFunction;
112import org.h2.expression.Operation;
113import org.h2.expression.Parameter;
114import org.h2.expression.Rownum;
115import org.h2.expression.SequenceValue;
116import org.h2.expression.Subquery;
117import org.h2.expression.TableFunction;
118import org.h2.expression.ValueExpression;
119import org.h2.expression.Variable;
120import org.h2.expression.Wildcard;
121import org.h2.index.Index;
122import org.h2.message.DbException;
123import org.h2.result.SortOrder;
124import org.h2.schema.Schema;
125import org.h2.schema.Sequence;
126import org.h2.table.Column;
127import org.h2.table.FunctionTable;
128import org.h2.table.IndexColumn;
129import org.h2.table.RangeTable;
130import org.h2.table.Table;
131import org.h2.table.TableFilter;
132import org.h2.table.TableView;
133import org.h2.table.TableFilter.TableFilterVisitor;
134import org.h2.util.MathUtils;
135import org.h2.util.New;
136import org.h2.util.StatementBuilder;
137import org.h2.util.StringUtils;
138import org.h2.value.CompareMode;
139import org.h2.value.DataType;
140import org.h2.value.Value;
141import org.h2.value.ValueBoolean;
142import org.h2.value.ValueBytes;
143import org.h2.value.ValueDate;
144import org.h2.value.ValueDecimal;
145import org.h2.value.ValueInt;
146import org.h2.value.ValueLong;
147import org.h2.value.ValueNull;
148import org.h2.value.ValueString;
149import org.h2.value.ValueTime;
150import org.h2.value.ValueTimestamp;
151 
152/**
153 * The parser is used to convert a SQL statement string to an command object.
154 *
155 * @author Thomas Mueller
156 * @author Noel Grandin
157 * @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
158 */
159public class Parser {
160 
161    // used during the tokenizer phase
162    private static final int CHAR_END = 1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
163    private static final int CHAR_NAME = 4, CHAR_SPECIAL_1 = 5,
164            CHAR_SPECIAL_2 = 6;
165    private static final int CHAR_STRING = 7, CHAR_DOT = 8,
166            CHAR_DOLLAR_QUOTED_STRING = 9;
167 
168    // this are token types
169    private static final int KEYWORD = 1, IDENTIFIER = 2, PARAMETER = 3,
170            END = 4, VALUE = 5;
171    private static final int EQUAL = 6, BIGGER_EQUAL = 7, BIGGER = 8;
172    private static final int SMALLER = 9, SMALLER_EQUAL = 10, NOT_EQUAL = 11,
173            AT = 12;
174    private static final int MINUS = 13, PLUS = 14, STRING_CONCAT = 15;
175    private static final int OPEN = 16, CLOSE = 17, NULL = 18, TRUE = 19,
176            FALSE = 20;
177    private static final int CURRENT_TIMESTAMP = 21, CURRENT_DATE = 22,
178            CURRENT_TIME = 23, ROWNUM = 24;
179    private static final int SPATIAL_INTERSECTS = 25;
180 
181    private final Database database;
182    private final Session session;
183    /**
184     * @see org.h2.engine.DbSettings#databaseToUpper
185     */
186    private final boolean identifiersToUpper;
187 
188    /** indicates character-type for each char in sqlCommand */
189    private int[] characterTypes;
190    private int currentTokenType;
191    private String currentToken;
192    private boolean currentTokenQuoted;
193    private Value currentValue;
194    private String originalSQL;
195    /** copy of originalSQL, with comments blanked out */
196    private String sqlCommand;
197    /** cached array if chars from sqlCommand */
198    private char[] sqlCommandChars;
199    /** index into sqlCommand of previous token */
200    private int lastParseIndex;
201    /** index into sqlCommand of current token */
202    private int parseIndex;
203    private CreateView createView;
204    private Prepared currentPrepared;
205    private Select currentSelect;
206    private ArrayList<Parameter> parameters;
207    private String schemaName;
208    private ArrayList<String> expectedList;
209    private boolean rightsChecked;
210    private boolean recompileAlways;
211    private ArrayList<Parameter> indexedParameterList;
212 
213    public Parser(Session session) {
214        this.database = session.getDatabase();
215        this.identifiersToUpper = database.getSettings().databaseToUpper;
216        this.session = session;
217    }
218 
219    /**
220     * Parse the statement and prepare it for execution.
221     *
222     * @param sql the SQL statement to parse
223     * @return the prepared object
224     */
225    public Prepared prepare(String sql) {
226        Prepared p = parse(sql);
227        p.prepare();
228        if (currentTokenType != END) {
229            throw getSyntaxError();
230        }
231        return p;
232    }
233 
234    /**
235     * Parse a statement or a list of statements, and prepare it for execution.
236     *
237     * @param sql the SQL statement to parse
238     * @return the command object
239     */
240    public Command prepareCommand(String sql) {
241        try {
242            Prepared p = parse(sql);
243            boolean hasMore = isToken(";");
244            if (!hasMore && currentTokenType != END) {
245                throw getSyntaxError();
246            }
247            p.prepare();
248            Command c = new CommandContainer(this, sql, p);
249            if (hasMore) {
250                String remaining = originalSQL.substring(parseIndex);
251                if (remaining.trim().length() != 0) {
252                    CommandList list = new CommandList(this, sql, c, remaining);
253                    // list.addCommand(c);
254                    // do {
255                    // c = parseCommand();
256                    // list.addCommand(c);
257                    // } while (currentToken.equals(";"));
258                    c = list;
259                }
260            }
261            return c;
262        } catch (DbException e) {
263            throw e.addSQL(originalSQL);
264        }
265    }
266 
267    /**
268     * Parse the statement, but don't prepare it for execution.
269     *
270     * @param sql the SQL statement to parse
271     * @return the prepared object
272     */
273    Prepared parse(String sql) {
274        Prepared p;
275        try {
276            // first, try the fast variant
277            p = parse(sql, false);
278        } catch (DbException e) {
279            if (e.getErrorCode() == ErrorCode.SYNTAX_ERROR_1) {
280                // now, get the detailed exception
281                p = parse(sql, true);
282            } else {
283                throw e.addSQL(sql);
284            }
285        }
286        p.setPrepareAlways(recompileAlways);
287        p.setParameterList(parameters);
288        return p;
289    }
290 
291    private Prepared parse(String sql, boolean withExpectedList) {
292        initialize(sql);
293        if (withExpectedList) {
294            expectedList = New.arrayList();
295        } else {
296            expectedList = null;
297        }
298        parameters = New.arrayList();
299        currentSelect = null;
300        currentPrepared = null;
301        createView = null;
302        recompileAlways = false;
303        indexedParameterList = null;
304        read();
305        return parsePrepared();
306    }
307 
308    private Prepared parsePrepared() {
309        int start = lastParseIndex;
310        Prepared c = null;
311        String token = currentToken;
312        if (token.length() == 0) {
313            c = new NoOperation(session);
314        } else {
315            char first = token.charAt(0);
316            switch (first) {
317            case '?':
318                // read the ? as a parameter
319                readTerm();
320                // this is an 'out' parameter - set a dummy value
321                parameters.get(0).setValue(ValueNull.INSTANCE);
322                read("=");
323                read("CALL");
324                c = parseCall();
325                break;
326            case '(':
327                c = parseSelect();
328                break;
329            case 'a':
330            case 'A':
331                if (readIf("ALTER")) {
332                    c = parseAlter();
333                } else if (readIf("ANALYZE")) {
334                    c = parseAnalyze();
335                }
336                break;
337            case 'b':
338            case 'B':
339                if (readIf("BACKUP")) {
340                    c = parseBackup();
341                } else if (readIf("BEGIN")) {
342                    c = parseBegin();
343                }
344                break;
345            case 'c':
346            case 'C':
347                if (readIf("COMMIT")) {
348                    c = parseCommit();
349                } else if (readIf("CREATE")) {
350                    c = parseCreate();
351                } else if (readIf("CALL")) {
352                    c = parseCall();
353                } else if (readIf("CHECKPOINT")) {
354                    c = parseCheckpoint();
355                } else if (readIf("COMMENT")) {
356                    c = parseComment();
357                }
358                break;
359            case 'd':
360            case 'D':
361                if (readIf("DELETE")) {
362                    c = parseDelete();
363                } else if (readIf("DROP")) {
364                    c = parseDrop();
365                } else if (readIf("DECLARE")) {
366                    // support for DECLARE GLOBAL TEMPORARY TABLE...
367                    c = parseCreate();
368                } else if (readIf("DEALLOCATE")) {
369                    c = parseDeallocate();
370                }
371                break;
372            case 'e':
373            case 'E':
374                if (readIf("EXPLAIN")) {
375                    c = parseExplain();
376                } else if (readIf("EXECUTE")) {
377                    c = parseExecute();
378                }
379                break;
380            case 'f':
381            case 'F':
382                if (isToken("FROM")) {
383                    c = parseSelect();
384                }
385                break;
386            case 'g':
387            case 'G':
388                if (readIf("GRANT")) {
389                    c = parseGrantRevoke(CommandInterface.GRANT);
390                }
391                break;
392            case 'h':
393            case 'H':
394                if (readIf("HELP")) {
395                    c = parseHelp();
396                }
397                break;
398            case 'i':
399            case 'I':
400                if (readIf("INSERT")) {
401                    c = parseInsert();
402                }
403                break;
404            case 'm':
405            case 'M':
406                if (readIf("MERGE")) {
407                    c = parseMerge();
408                }
409                break;
410            case 'p':
411            case 'P':
412                if (readIf("PREPARE")) {
413                    c = parsePrepare();
414                }
415                break;
416            case 'r':
417            case 'R':
418                if (readIf("ROLLBACK")) {
419                    c = parseRollback();
420                } else if (readIf("REVOKE")) {
421                    c = parseGrantRevoke(CommandInterface.REVOKE);
422                } else if (readIf("RUNSCRIPT")) {
423                    c = parseRunScript();
424                } else if (readIf("RELEASE")) {
425                    c = parseReleaseSavepoint();
426                } else if (readIf("REPLACE")) {
427                    c = parseReplace();
428                }
429                break;
430            case 's':
431            case 'S':
432                if (isToken("SELECT")) {
433                    c = parseSelect();
434                } else if (readIf("SET")) {
435                    c = parseSet();
436                } else if (readIf("SAVEPOINT")) {
437                    c = parseSavepoint();
438                } else if (readIf("SCRIPT")) {
439                    c = parseScript();
440                } else if (readIf("SHUTDOWN")) {
441                    c = parseShutdown();
442                } else if (readIf("SHOW")) {
443                    c = parseShow();
444                }
445                break;
446            case 't':
447            case 'T':
448                if (readIf("TRUNCATE")) {
449                    c = parseTruncate();
450                }
451                break;
452            case 'u':
453            case 'U':
454                if (readIf("UPDATE")) {
455                    c = parseUpdate();
456                } else if (readIf("USE")) {
457                    c = parseUse();
458                }
459                break;
460            case 'v':
461            case 'V':
462                if (readIf("VALUES")) {
463                    c = parseValues();
464                }
465                break;
466            case 'w':
467            case 'W':
468                if (readIf("WITH")) {
469                    c = parseWith();
470                }
471                break;
472            case ';':
473                c = new NoOperation(session);
474                break;
475            default:
476                throw getSyntaxError();
477            }
478            if (indexedParameterList != null) {
479                for (int i = 0, size = indexedParameterList.size();
480                        i < size; i++) {
481                    if (indexedParameterList.get(i) == null) {
482                        indexedParameterList.set(i, new Parameter(i));
483                    }
484                }
485                parameters = indexedParameterList;
486            }
487            if (readIf("{")) {
488                do {
489                    int index = (int) readLong() - 1;
490                    if (index < 0 || index >= parameters.size()) {
491                        throw getSyntaxError();
492                    }
493                    Parameter p = parameters.get(index);
494                    if (p == null) {
495                        throw getSyntaxError();
496                    }
497                    read(":");
498                    Expression expr = readExpression();
499                    expr = expr.optimize(session);
500                    p.setValue(expr.getValue(session));
501                } while (readIf(","));
502                read("}");
503                for (Parameter p : parameters) {
504                    p.checkSet();
505                }
506                parameters.clear();
507            }
508        }
509        if (c == null) {
510            throw getSyntaxError();
511        }
512        setSQL(c, null, start);
513        return c;
514    }
515 
516    private DbException getSyntaxError() {
517        if (expectedList == null || expectedList.size() == 0) {
518            return DbException.getSyntaxError(sqlCommand, parseIndex);
519        }
520        StatementBuilder buff = new StatementBuilder();
521        for (String e : expectedList) {
522            buff.appendExceptFirst(", ");
523            buff.append(e);
524        }
525        return DbException.getSyntaxError(sqlCommand, parseIndex,
526                buff.toString());
527    }
528 
529    private Prepared parseBackup() {
530        BackupCommand command = new BackupCommand(session);
531        read("TO");
532        command.setFileName(readExpression());
533        return command;
534    }
535 
536    private Prepared parseAnalyze() {
537        Analyze command = new Analyze(session);
538        if (readIf("SAMPLE_SIZE")) {
539            command.setTop(readPositiveInt());
540        }
541        return command;
542    }
543 
544    private TransactionCommand parseBegin() {
545        TransactionCommand command;
546        if (!readIf("WORK")) {
547            readIf("TRANSACTION");
548        }
549        command = new TransactionCommand(session, CommandInterface.BEGIN);
550        return command;
551    }
552 
553    private TransactionCommand parseCommit() {
554        TransactionCommand command;
555        if (readIf("TRANSACTION")) {
556            command = new TransactionCommand(session,
557                    CommandInterface.COMMIT_TRANSACTION);
558            command.setTransactionName(readUniqueIdentifier());
559            return command;
560        }
561        command = new TransactionCommand(session,
562                CommandInterface.COMMIT);
563        readIf("WORK");
564        return command;
565    }
566 
567    private TransactionCommand parseShutdown() {
568        int type = CommandInterface.SHUTDOWN;
569        if (readIf("IMMEDIATELY")) {
570            type = CommandInterface.SHUTDOWN_IMMEDIATELY;
571        } else if (readIf("COMPACT")) {
572            type = CommandInterface.SHUTDOWN_COMPACT;
573        } else if (readIf("DEFRAG")) {
574            type = CommandInterface.SHUTDOWN_DEFRAG;
575        } else {
576            readIf("SCRIPT");
577        }
578        return new TransactionCommand(session, type);
579    }
580 
581    private TransactionCommand parseRollback() {
582        TransactionCommand command;
583        if (readIf("TRANSACTION")) {
584            command = new TransactionCommand(session,
585                    CommandInterface.ROLLBACK_TRANSACTION);
586            command.setTransactionName(readUniqueIdentifier());
587            return command;
588        }
589        if (readIf("TO")) {
590            read("SAVEPOINT");
591            command = new TransactionCommand(session,
592                    CommandInterface.ROLLBACK_TO_SAVEPOINT);
593            command.setSavepointName(readUniqueIdentifier());
594        } else {
595            readIf("WORK");
596            command = new TransactionCommand(session,
597                    CommandInterface.ROLLBACK);
598        }
599        return command;
600    }
601 
602    private Prepared parsePrepare() {
603        if (readIf("COMMIT")) {
604            TransactionCommand command = new TransactionCommand(session,
605                    CommandInterface.PREPARE_COMMIT);
606            command.setTransactionName(readUniqueIdentifier());
607            return command;
608        }
609        String procedureName = readAliasIdentifier();
610        if (readIf("(")) {
611            ArrayList<Column> list = New.arrayList();
612            for (int i = 0;; i++) {
613                Column column = parseColumnForTable("C" + i, true);
614                list.add(column);
615                if (readIf(")")) {
616                    break;
617                }
618                read(",");
619            }
620        }
621        read("AS");
622        Prepared prep = parsePrepared();
623        PrepareProcedure command = new PrepareProcedure(session);
624        command.setProcedureName(procedureName);
625        command.setPrepared(prep);
626        return command;
627    }
628 
629    private TransactionCommand parseSavepoint() {
630        TransactionCommand command = new TransactionCommand(session,
631                CommandInterface.SAVEPOINT);
632        command.setSavepointName(readUniqueIdentifier());
633        return command;
634    }
635 
636    private Prepared parseReleaseSavepoint() {
637        Prepared command = new NoOperation(session);
638        readIf("SAVEPOINT");
639        readUniqueIdentifier();
640        return command;
641    }
642 
643    private Schema getSchema(String schemaName) {
644        if (schemaName == null) {
645            return null;
646        }
647        Schema schema = database.findSchema(schemaName);
648        if (schema == null) {
649            if (equalsToken("SESSION", schemaName)) {
650                // for local temporary tables
651                schema = database.getSchema(session.getCurrentSchemaName());
652            } else if (database.getMode().sysDummy1 &&
653                    "SYSIBM".equals(schemaName)) {
654                // IBM DB2 and Apache Derby compatibility: SYSIBM.SYSDUMMY1
655            } else {
656                throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schemaName);
657            }
658        }
659        return schema;
660    }
661 
662    private Schema getSchema() {
663        return getSchema(schemaName);
664    }
665 
666    private Column readTableColumn(TableFilter filter) {
667        String tableAlias = null;
668        String columnName = readColumnIdentifier();
669        if (readIf(".")) {
670            tableAlias = columnName;
671            columnName = readColumnIdentifier();
672            if (readIf(".")) {
673                String schema = tableAlias;
674                tableAlias = columnName;
675                columnName = readColumnIdentifier();
676                if (readIf(".")) {
677                    String catalogName = schema;
678                    schema = tableAlias;
679                    tableAlias = columnName;
680                    columnName = readColumnIdentifier();
681                    if (!equalsToken(catalogName, database.getShortName())) {
682                        throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1,
683                                catalogName);
684                    }
685                }
686                if (!equalsToken(schema, filter.getTable().getSchema()
687                        .getName())) {
688                    throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schema);
689                }
690            }
691            if (!equalsToken(tableAlias, filter.getTableAlias())) {
692                throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1,
693                        tableAlias);
694            }
695        }
696        if (database.getSettings().rowId) {
697            if (Column.ROWID.equals(columnName)) {
698                return filter.getRowIdColumn();
699            }
700        }
701        return filter.getTable().getColumn(columnName);
702    }
703 
704    private Update parseUpdate() {
705        Update command = new Update(session);
706        currentPrepared = command;
707        int start = lastParseIndex;
708        TableFilter filter = readSimpleTableFilter();
709        command.setTableFilter(filter);
710        read("SET");
711        if (readIf("(")) {
712            ArrayList<Column> columns = New.arrayList();
713            do {
714                Column column = readTableColumn(filter);
715                columns.add(column);
716            } while (readIf(","));
717            read(")");
718            read("=");
719            Expression expression = readExpression();
720            if (columns.size() == 1) {
721                // the expression is parsed as a simple value
722                command.setAssignment(columns.get(0), expression);
723            } else {
724                for (int i = 0, size = columns.size(); i < size; i++) {
725                    Column column = columns.get(i);
726                    Function f = Function.getFunction(database, "ARRAY_GET");
727                    f.setParameter(0, expression);
728                    f.setParameter(1, ValueExpression.get(ValueInt.get(i + 1)));
729                    f.doneWithParameters();
730                    command.setAssignment(column, f);
731                }
732            }
733        } else {
734            do {
735                Column column = readTableColumn(filter);
736                read("=");
737                Expression expression;
738                if (readIf("DEFAULT")) {
739                    expression = ValueExpression.getDefault();
740                } else {
741                    expression = readExpression();
742                }
743                command.setAssignment(column, expression);
744            } while (readIf(","));
745        }
746        if (readIf("WHERE")) {
747            Expression condition = readExpression();
748            command.setCondition(condition);
749        }
750        if (readIf("ORDER")) {
751            // for MySQL compatibility
752            // (this syntax is supported, but ignored)
753            read("BY");
754            parseSimpleOrderList();
755        }
756        if (readIf("LIMIT")) {
757            Expression limit = readTerm().optimize(session);
758            command.setLimit(limit);
759        }
760        setSQL(command, "UPDATE", start);
761        return command;
762    }
763 
764    private TableFilter readSimpleTableFilter() {
765        Table table = readTableOrView();
766        String alias = null;
767        if (readIf("AS")) {
768            alias = readAliasIdentifier();
769        } else if (currentTokenType == IDENTIFIER) {
770            if (!equalsToken("SET", currentToken)) {
771                // SET is not a keyword (PostgreSQL supports it as a table name)
772                alias = readAliasIdentifier();
773            }
774        }
775        return new TableFilter(session, table, alias, rightsChecked,
776                currentSelect);
777    }
778 
779    private Delete parseDelete() {
780        Delete command = new Delete(session);
781        Expression limit = null;
782        if (readIf("TOP")) {
783            limit = readTerm().optimize(session);
784        }
785        currentPrepared = command;
786        int start = lastParseIndex;
787        readIf("FROM");
788        TableFilter filter = readSimpleTableFilter();
789        command.setTableFilter(filter);
790        if (readIf("WHERE")) {
791            Expression condition = readExpression();
792            command.setCondition(condition);
793        }
794        if (readIf("LIMIT") && limit == null) {
795            limit = readTerm().optimize(session);
796        }
797        command.setLimit(limit);
798        setSQL(command, "DELETE", start);
799        return command;
800    }
801 
802    private IndexColumn[] parseIndexColumnList() {
803        ArrayList<IndexColumn> columns = New.arrayList();
804        do {
805            IndexColumn column = new IndexColumn();
806            column.columnName = readColumnIdentifier();
807            columns.add(column);
808            if (readIf("ASC")) {
809                // ignore
810            } else if (readIf("DESC")) {
811                column.sortType = SortOrder.DESCENDING;
812            }
813            if (readIf("NULLS")) {
814                if (readIf("FIRST")) {
815                    column.sortType |= SortOrder.NULLS_FIRST;
816                } else {
817                    read("LAST");
818                    column.sortType |= SortOrder.NULLS_LAST;
819                }
820            }
821        } while (readIf(","));
822        read(")");
823        return columns.toArray(new IndexColumn[columns.size()]);
824    }
825 
826    private String[] parseColumnList() {
827        ArrayList<String> columns = New.arrayList();
828        do {
829            String columnName = readColumnIdentifier();
830            columns.add(columnName);
831        } while (readIfMore());
832        return columns.toArray(new String[columns.size()]);
833    }
834 
835    private Column[] parseColumnList(Table table) {
836        ArrayList<Column> columns = New.arrayList();
837        HashSet<Column> set = New.hashSet();
838        if (!readIf(")")) {
839            do {
840                Column column = parseColumn(table);
841                if (!set.add(column)) {
842                    throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1,
843                            column.getSQL());
844                }
845                columns.add(column);
846            } while (readIfMore());
847        }
848        return columns.toArray(new Column[columns.size()]);
849    }
850 
851    private Column parseColumn(Table table) {
852        String id = readColumnIdentifier();
853        if (database.getSettings().rowId && Column.ROWID.equals(id)) {
854            return table.getRowIdColumn();
855        }
856        return table.getColumn(id);
857    }
858 
859    private boolean readIfMore() {
860        if (readIf(",")) {
861            return !readIf(")");
862        }
863        read(")");
864        return false;
865    }
866 
867    private Prepared parseHelp() {
868        StringBuilder buff = new StringBuilder(
869                "SELECT * FROM INFORMATION_SCHEMA.HELP");
870        int i = 0;
871        ArrayList<Value> paramValues = New.arrayList();
872        while (currentTokenType != END) {
873            String s = currentToken;
874            read();
875            if (i == 0) {
876                buff.append(" WHERE ");
877            } else {
878                buff.append(" AND ");
879            }
880            i++;
881            buff.append("UPPER(TOPIC) LIKE ?");
882            paramValues.add(ValueString.get("%" + s + "%"));
883        }
884        return prepare(session, buff.toString(), paramValues);
885    }
886 
887    private Prepared parseShow() {
888        ArrayList<Value> paramValues = New.arrayList();
889        StringBuilder buff = new StringBuilder("SELECT ");
890        if (readIf("CLIENT_ENCODING")) {
891            // for PostgreSQL compatibility
892            buff.append("'UNICODE' AS CLIENT_ENCODING FROM DUAL");
893        } else if (readIf("DEFAULT_TRANSACTION_ISOLATION")) {
894            // for PostgreSQL compatibility
895            buff.append("'read committed' AS DEFAULT_TRANSACTION_ISOLATION " +
896                    "FROM DUAL");
897        } else if (readIf("TRANSACTION")) {
898            // for PostgreSQL compatibility
899            read("ISOLATION");
900            read("LEVEL");
901            buff.append("'read committed' AS TRANSACTION_ISOLATION " +
902                    "FROM DUAL");
903        } else if (readIf("DATESTYLE")) {
904            // for PostgreSQL compatibility
905            buff.append("'ISO' AS DATESTYLE FROM DUAL");
906        } else if (readIf("SERVER_VERSION")) {
907            // for PostgreSQL compatibility
908            buff.append("'8.1.4' AS SERVER_VERSION FROM DUAL");
909        } else if (readIf("SERVER_ENCODING")) {
910            // for PostgreSQL compatibility
911            buff.append("'UTF8' AS SERVER_ENCODING FROM DUAL");
912        } else if (readIf("TABLES")) {
913            // for MySQL compatibility
914            String schema = Constants.SCHEMA_MAIN;
915            if (readIf("FROM")) {
916                schema = readUniqueIdentifier();
917            }
918            buff.append("TABLE_NAME, TABLE_SCHEMA FROM "
919                    + "INFORMATION_SCHEMA.TABLES "
920                    + "WHERE TABLE_SCHEMA=? ORDER BY TABLE_NAME");
921            paramValues.add(ValueString.get(schema));
922        } else if (readIf("COLUMNS")) {
923            // for MySQL compatibility
924            read("FROM");
925            String tableName = readIdentifierWithSchema();
926            String schemaName = getSchema().getName();
927            paramValues.add(ValueString.get(tableName));
928            if (readIf("FROM")) {
929                schemaName = readUniqueIdentifier();
930            }
931            buff.append("C.COLUMN_NAME FIELD, "
932                    + "C.TYPE_NAME || '(' || C.NUMERIC_PRECISION || ')' TYPE, "
933                    + "C.IS_NULLABLE \"NULL\", "
934                    + "CASE (SELECT MAX(I.INDEX_TYPE_NAME) FROM "
935                    + "INFORMATION_SCHEMA.INDEXES I "
936                    + "WHERE I.TABLE_SCHEMA=C.TABLE_SCHEMA "
937                    + "AND I.TABLE_NAME=C.TABLE_NAME "
938                    + "AND I.COLUMN_NAME=C.COLUMN_NAME)"
939                    + "WHEN 'PRIMARY KEY' THEN 'PRI' "
940                    + "WHEN 'UNIQUE INDEX' THEN 'UNI' ELSE '' END KEY, "
941                    + "IFNULL(COLUMN_DEFAULT, 'NULL') DEFAULT "
942                    + "FROM INFORMATION_SCHEMA.COLUMNS C "
943                    + "WHERE C.TABLE_NAME=? AND C.TABLE_SCHEMA=? "
944                    + "ORDER BY C.ORDINAL_POSITION");
945            paramValues.add(ValueString.get(schemaName));
946        } else if (readIf("DATABASES") || readIf("SCHEMAS")) {
947            // for MySQL compatibility
948            buff.append("SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA");
949        }
950        boolean b = session.getAllowLiterals();
951        try {
952            // need to temporarily enable it, in case we are in
953            // ALLOW_LITERALS_NUMBERS mode
954            session.setAllowLiterals(true);
955            return prepare(session, buff.toString(), paramValues);
956        } finally {
957            session.setAllowLiterals(b);
958        }
959    }
960 
961    private static Prepared prepare(Session s, String sql,
962            ArrayList<Value> paramValues) {
963        Prepared prep = s.prepare(sql);
964        ArrayList<Parameter> params = prep.getParameters();
965        if (params != null) {
966            for (int i = 0, size = params.size(); i < size; i++) {
967                Parameter p = params.get(i);
968                p.setValue(paramValues.get(i));
969            }
970        }
971        return prep;
972    }
973 
974    private boolean isSelect() {
975        int start = lastParseIndex;
976        while (readIf("(")) {
977            // need to read ahead, it could be a nested union:
978            // ((select 1) union (select 1))
979        }
980        boolean select = isToken("SELECT") || isToken("FROM");
981        parseIndex = start;
982        read();
983        return select;
984    }
985 
986    private Merge parseMerge() {
987        Merge command = new Merge(session);
988        currentPrepared = command;
989        read("INTO");
990        Table table = readTableOrView();
991        command.setTable(table);
992        if (readIf("(")) {
993            if (isSelect()) {
994                command.setQuery(parseSelect());
995                read(")");
996                return command;
997            }
998            Column[] columns = parseColumnList(table);
999            command.setColumns(columns);
1000        }
1001        if (readIf("KEY")) {
1002            read("(");
1003            Column[] keys = parseColumnList(table);
1004            command.setKeys(keys);
1005        }
1006        if (readIf("VALUES")) {
1007            do {
1008                ArrayList<Expression> values = New.arrayList();
1009                read("(");
1010                if (!readIf(")")) {
1011                    do {
1012                        if (readIf("DEFAULT")) {
1013                            values.add(null);
1014                        } else {
1015                            values.add(readExpression());
1016                        }
1017                    } while (readIfMore());
1018                }
1019                command.addRow(values.toArray(new Expression[values.size()]));
1020            } while (readIf(","));
1021        } else {
1022            command.setQuery(parseSelect());
1023        }
1024        return command;
1025    }
1026 
1027    private Insert parseInsert() {
1028        Insert command = new Insert(session);
1029        currentPrepared = command;
1030        read("INTO");
1031        Table table = readTableOrView();
1032        command.setTable(table);
1033        Column[] columns = null;
1034        if (readIf("(")) {
1035            if (isSelect()) {
1036                command.setQuery(parseSelect());
1037                read(")");
1038                return command;
1039            }
1040            columns = parseColumnList(table);
1041            command.setColumns(columns);
1042        }
1043        if (readIf("DIRECT")) {
1044            command.setInsertFromSelect(true);
1045        }
1046        if (readIf("SORTED")) {
1047            command.setSortedInsertMode(true);
1048        }
1049        if (readIf("DEFAULT")) {
1050            read("VALUES");
1051            Expression[] expr = {};
1052            command.addRow(expr);
1053        } else if (readIf("VALUES")) {
1054            read("(");
1055            do {
1056                ArrayList<Expression> values = New.arrayList();
1057                if (!readIf(")")) {
1058                    do {
1059                        if (readIf("DEFAULT")) {
1060                            values.add(null);
1061                        } else {
1062                            values.add(readExpression());
1063                        }
1064                    } while (readIfMore());
1065                }
1066                command.addRow(values.toArray(new Expression[values.size()]));
1067                // the following condition will allow (..),; and (..);
1068            } while (readIf(",") && readIf("("));
1069        } else if (readIf("SET")) {
1070            if (columns != null) {
1071                throw getSyntaxError();
1072            }
1073            ArrayList<Column> columnList = New.arrayList();
1074            ArrayList<Expression> values = New.arrayList();
1075            do {
1076                columnList.add(parseColumn(table));
1077                read("=");
1078                Expression expression;
1079                if (readIf("DEFAULT")) {
1080                    expression = ValueExpression.getDefault();
1081                } else {
1082                    expression = readExpression();
1083                }
1084                values.add(expression);
1085            } while (readIf(","));
1086            command.setColumns(columnList.toArray(new Column[columnList.size()]));
1087            command.addRow(values.toArray(new Expression[values.size()]));
1088        } else {
1089            command.setQuery(parseSelect());
1090        }
1091        if (database.getMode().onDuplicateKeyUpdate) {
1092            if (readIf("ON")) {
1093                read("DUPLICATE");
1094                read("KEY");
1095                read("UPDATE");
1096                do {
1097                    Column column = parseColumn(table);
1098                    read("=");
1099                    Expression expression;
1100                    if (readIf("DEFAULT")) {
1101                        expression = ValueExpression.getDefault();
1102                    } else {
1103                        expression = readExpression();
1104                    }
1105                    command.addAssignmentForDuplicate(column, expression);
1106                } while (readIf(","));
1107            }
1108        }
1109        if (database.getMode().isolationLevelInSelectOrInsertStatement) {
1110            parseIsolationClause();
1111        }
1112        return command;
1113    }
1114 
1115    /**
1116     * MySQL compatibility. REPLACE is similar to MERGE.
1117     */
1118    private Replace parseReplace() {
1119        Replace command = new Replace(session);
1120        currentPrepared = command;
1121        read("INTO");
1122        Table table = readTableOrView();
1123        command.setTable(table);
1124        if (readIf("(")) {
1125            if (isSelect()) {
1126                command.setQuery(parseSelect());
1127                read(")");
1128                return command;
1129            }
1130            Column[] columns = parseColumnList(table);
1131            command.setColumns(columns);
1132        }
1133        if (readIf("VALUES")) {
1134            do {
1135                ArrayList<Expression> values = New.arrayList();
1136                read("(");
1137                if (!readIf(")")) {
1138                    do {
1139                        if (readIf("DEFAULT")) {
1140                            values.add(null);
1141                        } else {
1142                            values.add(readExpression());
1143                        }
1144                    } while (readIfMore());
1145                }
1146                command.addRow(values.toArray(new Expression[values.size()]));
1147            } while (readIf(","));
1148        } else {
1149            command.setQuery(parseSelect());
1150        }
1151        return command;
1152    }
1153 
1154    private TableFilter readTableFilter(boolean fromOuter) {
1155        Table table;
1156        String alias = null;
1157        if (readIf("(")) {
1158            if (isSelect()) {
1159                Query query = parseSelectUnion();
1160                read(")");
1161                query.setParameterList(New.arrayList(parameters));
1162                query.init();
1163                Session s;
1164                if (createView != null) {
1165                    s = database.getSystemSession();
1166                } else {
1167                    s = session;
1168                }
1169                alias = session.getNextSystemIdentifier(sqlCommand);
1170                table = TableView.createTempView(s, session.getUser(), alias,
1171                        query, currentSelect);
1172            } else {
1173                TableFilter top;
1174                if (database.getSettings().nestedJoins) {
1175                    top = readTableFilter(false);
1176                    top = readJoin(top, currentSelect, false, false);
1177                    top = getNested(top);
1178                } else {
1179                    top = readTableFilter(fromOuter);
1180                    top = readJoin(top, currentSelect, false, fromOuter);
1181                }
1182                read(")");
1183                alias = readFromAlias(null);
1184                if (alias != null) {
1185                    top.setAlias(alias);
1186                }
1187                return top;
1188            }
1189        } else if (readIf("VALUES")) {
1190            table = parseValuesTable().getTable();
1191        } else {
1192            String tableName = readIdentifierWithSchema(null);
1193            Schema schema = getSchema();
1194            boolean foundLeftBracket = readIf("(");
1195            if (foundLeftBracket && readIf("INDEX")) {
1196                // Sybase compatibility with
1197                // "select * from test (index table1_index)"
1198                readIdentifierWithSchema(null);
1199                read(")");
1200                foundLeftBracket = false;
1201            }
1202            if (foundLeftBracket) {
1203                Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
1204                if (equalsToken(tableName, RangeTable.NAME)
1205                        || equalsToken(tableName, RangeTable.ALIAS)) {
1206                    Expression min = readExpression();
1207                    read(",");
1208                    Expression max = readExpression();
1209                    if (readIf(",")) {
1210                        Expression step = readExpression();
1211                        read(")");
1212                        table = new RangeTable(mainSchema, min, max, step,
1213                                false);
1214                    } else {
1215                        read(")");
1216                        table = new RangeTable(mainSchema, min, max, false);
1217                    }
1218                } else {
1219                    Expression expr = readFunction(schema, tableName);
1220                    if (!(expr instanceof FunctionCall)) {
1221                        throw getSyntaxError();
1222                    }
1223                    FunctionCall call = (FunctionCall) expr;
1224                    if (!call.isDeterministic()) {
1225                        recompileAlways = true;
1226                    }
1227                    table = new FunctionTable(mainSchema, session, expr, call);
1228                }
1229            } else if (equalsToken("DUAL", tableName)) {
1230                table = getDualTable(false);
1231            } else if (database.getMode().sysDummy1 &&
1232                    equalsToken("SYSDUMMY1", tableName)) {
1233                table = getDualTable(false);
1234            } else {
1235                table = readTableOrView(tableName);
1236            }
1237        }
1238        alias = readFromAlias(alias);
1239        return new TableFilter(session, table, alias, rightsChecked,
1240                currentSelect);
1241    }
1242 
1243    private String readFromAlias(String alias) {
1244        if (readIf("AS")) {
1245            alias = readAliasIdentifier();
1246        } else if (currentTokenType == IDENTIFIER) {
1247            // left and right are not keywords (because they are functions as
1248            // well)
1249            if (!isToken("LEFT") && !isToken("RIGHT") && !isToken("FULL")) {
1250                alias = readAliasIdentifier();
1251            }
1252        }
1253        return alias;
1254    }
1255 
1256    private Prepared parseTruncate() {
1257        read("TABLE");
1258        Table table = readTableOrView();
1259        TruncateTable command = new TruncateTable(session);
1260        command.setTable(table);
1261        return command;
1262    }
1263 
1264    private boolean readIfExists(boolean ifExists) {
1265        if (readIf("IF")) {
1266            read("EXISTS");
1267            ifExists = true;
1268        }
1269        return ifExists;
1270    }
1271 
1272    private Prepared parseComment() {
1273        int type = 0;
1274        read("ON");
1275        boolean column = false;
1276        if (readIf("TABLE") || readIf("VIEW")) {
1277            type = DbObject.TABLE_OR_VIEW;
1278        } else if (readIf("COLUMN")) {
1279            column = true;
1280            type = DbObject.TABLE_OR_VIEW;
1281        } else if (readIf("CONSTANT")) {
1282            type = DbObject.CONSTANT;
1283        } else if (readIf("CONSTRAINT")) {
1284            type = DbObject.CONSTRAINT;
1285        } else if (readIf("ALIAS")) {
1286            type = DbObject.FUNCTION_ALIAS;
1287        } else if (readIf("INDEX")) {
1288            type = DbObject.INDEX;
1289        } else if (readIf("ROLE")) {
1290            type = DbObject.ROLE;
1291        } else if (readIf("SCHEMA")) {
1292            type = DbObject.SCHEMA;
1293        } else if (readIf("SEQUENCE")) {
1294            type = DbObject.SEQUENCE;
1295        } else if (readIf("TRIGGER")) {
1296            type = DbObject.TRIGGER;
1297        } else if (readIf("USER")) {
1298            type = DbObject.USER;
1299        } else if (readIf("DOMAIN")) {
1300            type = DbObject.USER_DATATYPE;
1301        } else {
1302            throw getSyntaxError();
1303        }
1304        SetComment command = new SetComment(session);
1305        String objectName;
1306        if (column) {
1307            // can't use readIdentifierWithSchema() because
1308            // it would not read schema.table.column correctly
1309            // if the db name is equal to the schema name
1310            ArrayList<String> list = New.arrayList();
1311            do {
1312                list.add(readUniqueIdentifier());
1313            } while (readIf("."));
1314            schemaName = session.getCurrentSchemaName();
1315            if (list.size() == 4) {
1316                if (!equalsToken(database.getShortName(), list.get(0))) {
1317                    throw DbException.getSyntaxError(sqlCommand, parseIndex,
1318                            "database name");
1319                }
1320                list.remove(0);
1321            }
1322            if (list.size() == 3) {
1323                schemaName = list.get(0);
1324                list.remove(0);
1325            }
1326            if (list.size() != 2) {
1327                throw DbException.getSyntaxError(sqlCommand, parseIndex,
1328                        "table.column");
1329            }
1330            objectName = list.get(0);
1331            command.setColumn(true);
1332            command.setColumnName(list.get(1));
1333        } else {
1334            objectName = readIdentifierWithSchema();
1335        }
1336        command.setSchemaName(schemaName);
1337        command.setObjectName(objectName);
1338        command.setObjectType(type);
1339        read("IS");
1340        command.setCommentExpression(readExpression());
1341        return command;
1342    }
1343 
1344    private Prepared parseDrop() {
1345        if (readIf("TABLE")) {
1346            boolean ifExists = readIfExists(false);
1347            String tableName = readIdentifierWithSchema();
1348            DropTable command = new DropTable(session, getSchema());
1349            command.setTableName(tableName);
1350            while (readIf(",")) {
1351                tableName = readIdentifierWithSchema();
1352                DropTable next = new DropTable(session, getSchema());
1353                next.setTableName(tableName);
1354                command.addNextDropTable(next);
1355            }
1356            ifExists = readIfExists(ifExists);
1357            command.setIfExists(ifExists);
1358            if (readIf("CASCADE")) {
1359                command.setDropAction(ConstraintReferential.CASCADE);
1360                readIf("CONSTRAINTS");
1361            } else if (readIf("RESTRICT")) {
1362                command.setDropAction(ConstraintReferential.RESTRICT);
1363            } else if (readIf("IGNORE")) {
1364                command.setDropAction(ConstraintReferential.SET_DEFAULT);
1365            }
1366            return command;
1367        } else if (readIf("INDEX")) {
1368            boolean ifExists = readIfExists(false);
1369            String indexName = readIdentifierWithSchema();
1370            DropIndex command = new DropIndex(session, getSchema());
1371            command.setIndexName(indexName);
1372            ifExists = readIfExists(ifExists);
1373            command.setIfExists(ifExists);
1374            return command;
1375        } else if (readIf("USER")) {
1376            boolean ifExists = readIfExists(false);
1377            DropUser command = new DropUser(session);
1378            command.setUserName(readUniqueIdentifier());
1379            ifExists = readIfExists(ifExists);
1380            readIf("CASCADE");
1381            command.setIfExists(ifExists);
1382            return command;
1383        } else if (readIf("SEQUENCE")) {
1384            boolean ifExists = readIfExists(false);
1385            String sequenceName = readIdentifierWithSchema();
1386            DropSequence command = new DropSequence(session, getSchema());
1387            command.setSequenceName(sequenceName);
1388            ifExists = readIfExists(ifExists);
1389            command.setIfExists(ifExists);
1390            return command;
1391        } else if (readIf("CONSTANT")) {
1392            boolean ifExists = readIfExists(false);
1393            String constantName = readIdentifierWithSchema();
1394            DropConstant command = new DropConstant(session, getSchema());
1395            command.setConstantName(constantName);
1396            ifExists = readIfExists(ifExists);
1397            command.setIfExists(ifExists);
1398            return command;
1399        } else if (readIf("TRIGGER")) {
1400            boolean ifExists = readIfExists(false);
1401            String triggerName = readIdentifierWithSchema();
1402            DropTrigger command = new DropTrigger(session, getSchema());
1403            command.setTriggerName(triggerName);
1404            ifExists = readIfExists(ifExists);
1405            command.setIfExists(ifExists);
1406            return command;
1407        } else if (readIf("VIEW")) {
1408            boolean ifExists = readIfExists(false);
1409            String viewName = readIdentifierWithSchema();
1410            DropView command = new DropView(session, getSchema());
1411            command.setViewName(viewName);
1412            ifExists = readIfExists(ifExists);
1413            command.setIfExists(ifExists);
1414            Integer dropAction = parseCascadeOrRestrict();
1415            if (dropAction != null) {
1416                command.setDropAction(dropAction);
1417            }
1418            return command;
1419        } else if (readIf("ROLE")) {
1420            boolean ifExists = readIfExists(false);
1421            DropRole command = new DropRole(session);
1422            command.setRoleName(readUniqueIdentifier());
1423            ifExists = readIfExists(ifExists);
1424            command.setIfExists(ifExists);
1425            return command;
1426        } else if (readIf("ALIAS")) {
1427            boolean ifExists = readIfExists(false);
1428            String aliasName = readIdentifierWithSchema();
1429            DropFunctionAlias command = new DropFunctionAlias(session,
1430                    getSchema());
1431            command.setAliasName(aliasName);
1432            ifExists = readIfExists(ifExists);
1433            command.setIfExists(ifExists);
1434            return command;
1435        } else if (readIf("SCHEMA")) {
1436            boolean ifExists = readIfExists(false);
1437            DropSchema command = new DropSchema(session);
1438            command.setSchemaName(readUniqueIdentifier());
1439            ifExists = readIfExists(ifExists);
1440            command.setIfExists(ifExists);
1441            return command;
1442        } else if (readIf("ALL")) {
1443            read("OBJECTS");
1444            DropDatabase command = new DropDatabase(session);
1445            command.setDropAllObjects(true);
1446            if (readIf("DELETE")) {
1447                read("FILES");
1448                command.setDeleteFiles(true);
1449            }
1450            return command;
1451        } else if (readIf("DOMAIN")) {
1452            return parseDropUserDataType();
1453        } else if (readIf("TYPE")) {
1454            return parseDropUserDataType();
1455        } else if (readIf("DATATYPE")) {
1456            return parseDropUserDataType();
1457        } else if (readIf("AGGREGATE")) {
1458            return parseDropAggregate();
1459        }
1460        throw getSyntaxError();
1461    }
1462 
1463    private DropUserDataType parseDropUserDataType() {
1464        boolean ifExists = readIfExists(false);
1465        DropUserDataType command = new DropUserDataType(session);
1466        command.setTypeName(readUniqueIdentifier());
1467        ifExists = readIfExists(ifExists);
1468        command.setIfExists(ifExists);
1469        return command;
1470    }
1471 
1472    private DropAggregate parseDropAggregate() {
1473        boolean ifExists = readIfExists(false);
1474        DropAggregate command = new DropAggregate(session);
1475        command.setName(readUniqueIdentifier());
1476        ifExists = readIfExists(ifExists);
1477        command.setIfExists(ifExists);
1478        return command;
1479    }
1480 
1481    private TableFilter readJoin(TableFilter top, Select command,
1482            boolean nested, boolean fromOuter) {
1483        boolean joined = false;
1484        TableFilter last = top;
1485        boolean nestedJoins = database.getSettings().nestedJoins;
1486        while (true) {
1487            if (readIf("RIGHT")) {
1488                readIf("OUTER");
1489                read("JOIN");
1490                joined = true;
1491                // the right hand side is the 'inner' table usually
1492                TableFilter newTop = readTableFilter(fromOuter);
1493                newTop = readJoin(newTop, command, nested, true);
1494                Expression on = null;
1495                if (readIf("ON")) {
1496                    on = readExpression();
1497                }
1498                if (nestedJoins) {
1499                    top = getNested(top);
1500                    newTop.addJoin(top, true, false, on);
1501                } else {
1502                    newTop.addJoin(top, true, false, on);
1503                }
1504                top = newTop;
1505                last = newTop;
1506            } else if (readIf("LEFT")) {
1507                readIf("OUTER");
1508                read("JOIN");
1509                joined = true;
1510                TableFilter join = readTableFilter(true);
1511                if (nestedJoins) {
1512                    join = readJoin(join, command, true, true);
1513                } else {
1514                    top = readJoin(top, command, false, true);
1515                }
1516                Expression on = null;
1517                if (readIf("ON")) {
1518                    on = readExpression();
1519                }
1520                top.addJoin(join, true, false, on);
1521                last = join;
1522            } else if (readIf("FULL")) {
1523                throw getSyntaxError();
1524            } else if (readIf("INNER")) {
1525                read("JOIN");
1526                joined = true;
1527                TableFilter join = readTableFilter(fromOuter);
1528                top = readJoin(top, command, false, false);
1529                Expression on = null;
1530                if (readIf("ON")) {
1531                    on = readExpression();
1532                }
1533                if (nestedJoins) {
1534                    top.addJoin(join, false, false, on);
1535                } else {
1536                    top.addJoin(join, fromOuter, false, on);
1537                }
1538                last = join;
1539            } else if (readIf("JOIN")) {
1540                joined = true;
1541                TableFilter join = readTableFilter(fromOuter);
1542                top = readJoin(top, command, false, false);
1543                Expression on = null;
1544                if (readIf("ON")) {
1545                    on = readExpression();
1546                }
1547                if (nestedJoins) {
1548                    top.addJoin(join, false, false, on);
1549                } else {
1550                    top.addJoin(join, fromOuter, false, on);
1551                }
1552                last = join;
1553            } else if (readIf("CROSS")) {
1554                read("JOIN");
1555                joined = true;
1556                TableFilter join = readTableFilter(fromOuter);
1557                if (nestedJoins) {
1558                    top.addJoin(join, false, false, null);
1559                } else {
1560                    top.addJoin(join, fromOuter, false, null);
1561                }
1562                last = join;
1563            } else if (readIf("NATURAL")) {
1564                read("JOIN");
1565                joined = true;
1566                TableFilter join = readTableFilter(fromOuter);
1567                Column[] tableCols = last.getTable().getColumns();
1568                Column[] joinCols = join.getTable().getColumns();
1569                String tableSchema = last.getTable().getSchema().getName();
1570                String joinSchema = join.getTable().getSchema().getName();
1571                Expression on = null;
1572                for (Column tc : tableCols) {
1573                    String tableColumnName = tc.getName();
1574                    for (Column c : joinCols) {
1575                        String joinColumnName = c.getName();
1576                        if (equalsToken(tableColumnName, joinColumnName)) {
1577                            join.addNaturalJoinColumn(c);
1578                            Expression tableExpr = new ExpressionColumn(
1579                                    database, tableSchema,
1580                                    last.getTableAlias(), tableColumnName);
1581                            Expression joinExpr = new ExpressionColumn(
1582                                    database, joinSchema, join.getTableAlias(),
1583                                    joinColumnName);
1584                            Expression equal = new Comparison(session,
1585                                    Comparison.EQUAL, tableExpr, joinExpr);
1586                            if (on == null) {
1587                                on = equal;
1588                            } else {
1589                                on = new ConditionAndOr(ConditionAndOr.AND, on,
1590                                        equal);
1591                            }
1592                        }
1593                    }
1594                }
1595                if (nestedJoins) {
1596                    top.addJoin(join, false, nested, on);
1597                } else {
1598                    top.addJoin(join, fromOuter, false, on);
1599                }
1600                last = join;
1601            } else {
1602                break;
1603            }
1604        }
1605        if (nested && joined) {
1606            top = getNested(top);
1607        }
1608        return top;
1609    }
1610 
1611    private TableFilter getNested(TableFilter n) {
1612        String joinTable = Constants.PREFIX_JOIN + parseIndex;
1613        TableFilter top = new TableFilter(session, getDualTable(true),
1614                joinTable, rightsChecked, currentSelect);
1615        top.addJoin(n, false, true, null);
1616        return top;
1617    }
1618 
1619    private Prepared parseExecute() {
1620        ExecuteProcedure command = new ExecuteProcedure(session);
1621        String procedureName = readAliasIdentifier();
1622        Procedure p = session.getProcedure(procedureName);
1623        if (p == null) {
1624            throw DbException.get(ErrorCode.FUNCTION_ALIAS_NOT_FOUND_1,
1625                    procedureName);
1626        }
1627        command.setProcedure(p);
1628        if (readIf("(")) {
1629            for (int i = 0;; i++) {
1630                command.setExpression(i, readExpression());
1631                if (readIf(")")) {
1632                    break;
1633                }
1634                read(",");
1635            }
1636        }
1637        return command;
1638    }
1639 
1640    private DeallocateProcedure parseDeallocate() {
1641        readIf("PLAN");
1642        String procedureName = readAliasIdentifier();
1643        DeallocateProcedure command = new DeallocateProcedure(session);
1644        command.setProcedureName(procedureName);
1645        return command;
1646    }
1647 
1648    private Explain parseExplain() {
1649        Explain command = new Explain(session);
1650        if (readIf("ANALYZE")) {
1651            command.setExecuteCommand(true);
1652        } else {
1653            if (readIf("PLAN")) {
1654                readIf("FOR");
1655            }
1656        }
1657        if (isToken("SELECT") || isToken("FROM") || isToken("(")) {
1658            command.setCommand(parseSelect());
1659        } else if (readIf("DELETE")) {
1660            command.setCommand(parseDelete());
1661        } else if (readIf("UPDATE")) {
1662            command.setCommand(parseUpdate());
1663        } else if (readIf("INSERT")) {
1664            command.setCommand(parseInsert());
1665        } else if (readIf("MERGE")) {
1666            command.setCommand(parseMerge());
1667        } else if (readIf("WITH")) {
1668            command.setCommand(parseWith());
1669        } else {
1670            throw getSyntaxError();
1671        }
1672        return command;
1673    }
1674 
1675    private Query parseSelect() {
1676        int paramIndex = parameters.size();
1677        Query command = parseSelectUnion();
1678        ArrayList<Parameter> params = New.arrayList();
1679        for (int i = paramIndex, size = parameters.size(); i < size; i++) {
1680            params.add(parameters.get(i));
1681        }
1682        command.setParameterList(params);
1683        command.init();
1684        return command;
1685    }
1686 
1687    private Query parseSelectUnion() {
1688        int start = lastParseIndex;
1689        Query command = parseSelectSub();
1690        return parseSelectUnionExtension(command, start, false);
1691    }
1692 
1693    private Query parseSelectUnionExtension(Query command, int start,
1694            boolean unionOnly) {
1695        while (true) {
1696            if (readIf("UNION")) {
1697                SelectUnion union = new SelectUnion(session, command);
1698                if (readIf("ALL")) {
1699                    union.setUnionType(SelectUnion.UNION_ALL);
1700                } else {
1701                    readIf("DISTINCT");
1702                    union.setUnionType(SelectUnion.UNION);
1703                }
1704                union.setRight(parseSelectSub());
1705                command = union;
1706            } else if (readIf("MINUS") || readIf("EXCEPT")) {
1707                SelectUnion union = new SelectUnion(session, command);
1708                union.setUnionType(SelectUnion.EXCEPT);
1709                union.setRight(parseSelectSub());
1710                command = union;
1711            } else if (readIf("INTERSECT")) {
1712                SelectUnion union = new SelectUnion(session, command);
1713                union.setUnionType(SelectUnion.INTERSECT);
1714                union.setRight(parseSelectSub());
1715                command = union;
1716            } else {
1717                break;
1718            }
1719        }
1720        if (!unionOnly) {
1721            parseEndOfQuery(command);
1722        }
1723        setSQL(command, null, start);
1724        return command;
1725    }
1726 
1727    private void parseEndOfQuery(Query command) {
1728        if (readIf("ORDER")) {
1729            read("BY");
1730            Select oldSelect = currentSelect;
1731            if (command instanceof Select) {
1732                currentSelect = (Select) command;
1733            }
1734            ArrayList<SelectOrderBy> orderList = New.arrayList();
1735            do {
1736                boolean canBeNumber = true;
1737                if (readIf("=")) {
1738                    canBeNumber = false;
1739                }
1740                SelectOrderBy order = new SelectOrderBy();
1741                Expression expr = readExpression();
1742                if (canBeNumber && expr instanceof ValueExpression &&
1743                        expr.getType() == Value.INT) {
1744                    order.columnIndexExpr = expr;
1745                } else if (expr instanceof Parameter) {
1746                    recompileAlways = true;
1747                    order.columnIndexExpr = expr;
1748                } else {
1749                    order.expression = expr;
1750                }
1751                if (readIf("DESC")) {
1752                    order.descending = true;
1753                } else {
1754                    readIf("ASC");
1755                }
1756                if (readIf("NULLS")) {
1757                    if (readIf("FIRST")) {
1758                        order.nullsFirst = true;
1759                    } else {
1760                        read("LAST");
1761                        order.nullsLast = true;
1762                    }
1763                }
1764                orderList.add(order);
1765            } while (readIf(","));
1766            command.setOrder(orderList);
1767            currentSelect = oldSelect;
1768        }
1769        if (database.getMode().supportOffsetFetch) {
1770            // make sure aggregate functions will not work here
1771            Select temp = currentSelect;
1772            currentSelect = null;
1773 
1774            // http://sqlpro.developpez.com/SQL2008/
1775            if (readIf("OFFSET")) {
1776                command.setOffset(readExpression().optimize(session));
1777                if (!readIf("ROW")) {
1778                    read("ROWS");
1779                }
1780            }
1781            if (readIf("FETCH")) {
1782                if (!readIf("FIRST")) {
1783                    read("NEXT");
1784                }
1785                if (readIf("ROW")) {
1786                    command.setLimit(ValueExpression.get(ValueInt.get(1)));
1787                } else {
1788                    Expression limit = readExpression().optimize(session);
1789                    command.setLimit(limit);
1790                    if (!readIf("ROW")) {
1791                        read("ROWS");
1792                    }
1793                }
1794                read("ONLY");
1795            }
1796 
1797            currentSelect = temp;
1798        }
1799        if (readIf("LIMIT")) {
1800            Select temp = currentSelect;
1801            // make sure aggregate functions will not work here
1802            currentSelect = null;
1803            Expression limit = readExpression().optimize(session);
1804            command.setLimit(limit);
1805            if (readIf("OFFSET")) {
1806                Expression offset = readExpression().optimize(session);
1807                command.setOffset(offset);
1808            } else if (readIf(",")) {
1809                // MySQL: [offset, ] rowcount
1810                Expression offset = limit;
1811                limit = readExpression().optimize(session);
1812                command.setOffset(offset);
1813                command.setLimit(limit);
1814            }
1815            if (readIf("SAMPLE_SIZE")) {
1816                Expression sampleSize = readExpression().optimize(session);
1817                command.setSampleSize(sampleSize);
1818            }
1819            currentSelect = temp;
1820        }
1821        if (readIf("FOR")) {
1822            if (readIf("UPDATE")) {
1823                if (readIf("OF")) {
1824                    do {
1825                        readIdentifierWithSchema();
1826                    } while (readIf(","));
1827                } else if (readIf("NOWAIT")) {
1828                    // TODO parser: select for update nowait: should not wait
1829                }
1830                command.setForUpdate(true);
1831            } else if (readIf("READ") || readIf("FETCH")) {
1832                read("ONLY");
1833            }
1834        }
1835        if (database.getMode().isolationLevelInSelectOrInsertStatement) {
1836            parseIsolationClause();
1837        }
1838    }
1839 
1840    /**
1841     * DB2 isolation clause
1842     */
1843    private void parseIsolationClause() {
1844        if (readIf("WITH")) {
1845            if (readIf("RR") || readIf("RS")) {
1846                // concurrent-access-resolution clause
1847                if (readIf("USE")) {
1848                    read("AND");
1849                    read("KEEP");
1850                    if (readIf("SHARE") || readIf("UPDATE") ||
1851                            readIf("EXCLUSIVE")) {
1852                        // ignore
1853                    }
1854                    read("LOCKS");
1855                }
1856            } else if (readIf("CS") || readIf("UR")) {
1857                // ignore
1858            }
1859        }
1860    }
1861 
1862    private Query parseSelectSub() {
1863        if (readIf("(")) {
1864            Query command = parseSelectUnion();
1865            read(")");
1866            return command;
1867        }
1868        Select select = parseSelectSimple();
1869        return select;
1870    }
1871 
1872    private void parseSelectSimpleFromPart(Select command) {
1873        do {
1874            TableFilter filter = readTableFilter(false);
1875            parseJoinTableFilter(filter, command);
1876        } while (readIf(","));
1877    }
1878 
1879    private void parseJoinTableFilter(TableFilter top, final Select command) {
1880        top = readJoin(top, command, false, top.isJoinOuter());
1881        command.addTableFilter(top, true);
1882        boolean isOuter = false;
1883        while (true) {
1884            TableFilter n = top.getNestedJoin();
1885            if (n != null) {
1886                n.visit(new TableFilterVisitor() {
1887                    @Override
1888                    public void accept(TableFilter f) {
1889                        command.addTableFilter(f, false);
1890                    }
1891                });
1892            }
1893            TableFilter join = top.getJoin();
1894            if (join == null) {
1895                break;
1896            }
1897            isOuter = isOuter | join.isJoinOuter();
1898            if (isOuter) {
1899                command.addTableFilter(join, false);
1900            } else {
1901                // make flat so the optimizer can work better
1902                Expression on = join.getJoinCondition();
1903                if (on != null) {
1904                    command.addCondition(on);
1905                }
1906                join.removeJoinCondition();
1907                top.removeJoin();
1908                command.addTableFilter(join, true);
1909            }
1910            top = join;
1911        }
1912    }
1913 
1914    private void parseSelectSimpleSelectPart(Select command) {
1915        Select temp = currentSelect;
1916        // make sure aggregate functions will not work in TOP and LIMIT
1917        currentSelect = null;
1918        if (readIf("TOP")) {
1919            // can't read more complex expressions here because
1920            // SELECT TOP 1 +? A FROM TEST could mean
1921            // SELECT TOP (1+?) A FROM TEST or
1922            // SELECT TOP 1 (+?) AS A FROM TEST
1923            Expression limit = readTerm().optimize(session);
1924            command.setLimit(limit);
1925        } else if (readIf("LIMIT")) {
1926            Expression offset = readTerm().optimize(session);
1927            command.setOffset(offset);
1928            Expression limit = readTerm().optimize(session);
1929            command.setLimit(limit);
1930        }
1931        currentSelect = temp;
1932        if (readIf("DISTINCT")) {
1933            command.setDistinct(true);
1934        } else {
1935            readIf("ALL");
1936        }
1937        ArrayList<Expression> expressions = New.arrayList();
1938        do {
1939            if (readIf("*")) {
1940                expressions.add(new Wildcard(null, null));
1941            } else {
1942                Expression expr = readExpression();
1943                if (readIf("AS") || currentTokenType == IDENTIFIER) {
1944                    String alias = readAliasIdentifier();
1945                    boolean aliasColumnName = database.getSettings().aliasColumnName;
1946                    aliasColumnName |= database.getMode().aliasColumnName;
1947                    expr = new Alias(expr, alias, aliasColumnName);
1948                }
1949                expressions.add(expr);
1950            }
1951        } while (readIf(","));
1952        command.setExpressions(expressions);
1953    }
1954 
1955    private Select parseSelectSimple() {
1956        boolean fromFirst;
1957        if (readIf("SELECT")) {
1958            fromFirst = false;
1959        } else if (readIf("FROM")) {
1960            fromFirst = true;
1961        } else {
1962            throw getSyntaxError();
1963        }
1964        Select command = new Select(session);
1965        int start = lastParseIndex;
1966        Select oldSelect = currentSelect;
1967        currentSelect = command;
1968        currentPrepared = command;
1969        if (fromFirst) {
1970            parseSelectSimpleFromPart(command);
1971            read("SELECT");
1972            parseSelectSimpleSelectPart(command);
1973        } else {
1974            parseSelectSimpleSelectPart(command);
1975            if (!readIf("FROM")) {
1976                // select without FROM: convert to SELECT ... FROM
1977                // SYSTEM_RANGE(1,1)
1978                Table dual = getDualTable(false);
1979                TableFilter filter = new TableFilter(session, dual, null,
1980                        rightsChecked, currentSelect);
1981                command.addTableFilter(filter, true);
1982            } else {
1983                parseSelectSimpleFromPart(command);
1984            }
1985        }
1986        if (readIf("WHERE")) {
1987            Expression condition = readExpression();
1988            command.addCondition(condition);
1989        }
1990        // the group by is read for the outer select (or not a select)
1991        // so that columns that are not grouped can be used
1992        currentSelect = oldSelect;
1993        if (readIf("GROUP")) {
1994            read("BY");
1995            command.setGroupQuery();
1996            ArrayList<Expression> list = New.arrayList();
1997            do {
1998                Expression expr = readExpression();
1999                list.add(expr);
2000            } while (readIf(","));
2001            command.setGroupBy(list);
2002        }
2003        currentSelect = command;
2004        if (readIf("HAVING")) {
2005            command.setGroupQuery();
2006            Expression condition = readExpression();
2007            command.setHaving(condition);
2008        }
2009        command.setParameterList(parameters);
2010        currentSelect = oldSelect;
2011        setSQL(command, "SELECT", start);
2012        return command;
2013    }
2014 
2015    private Table getDualTable(boolean noColumns) {
2016        Schema main = database.findSchema(Constants.SCHEMA_MAIN);
2017        Expression one = ValueExpression.get(ValueLong.get(1));
2018        return new RangeTable(main, one, one, noColumns);
2019    }
2020 
2021    private void setSQL(Prepared command, String start, int startIndex) {
2022        String sql = originalSQL.substring(startIndex, lastParseIndex).trim();
2023        if (start != null) {
2024            sql = start + " " + sql;
2025        }
2026        command.setSQL(sql);
2027    }
2028 
2029    private Expression readExpression() {
2030        Expression r = readAnd();
2031        while (readIf("OR")) {
2032            r = new ConditionAndOr(ConditionAndOr.OR, r, readAnd());
2033        }
2034        return r;
2035    }
2036 
2037    private Expression readAnd() {
2038        Expression r = readCondition();
2039        while (readIf("AND")) {
2040            r = new ConditionAndOr(ConditionAndOr.AND, r, readCondition());
2041        }
2042        return r;
2043    }
2044 
2045    private Expression readCondition() {
2046        if (readIf("NOT")) {
2047            return new ConditionNot(readCondition());
2048        }
2049        if (readIf("EXISTS")) {
2050            read("(");
2051            Query query = parseSelect();
2052            // can not reduce expression because it might be a union except
2053            // query with distinct
2054            read(")");
2055            return new ConditionExists(query);
2056        }
2057        if (readIf("INTERSECTS")) {
2058            read("(");
2059            Expression r1 = readConcat();
2060            read(",");
2061            Expression r2 = readConcat();
2062            read(")");
2063            return new Comparison(session, Comparison.SPATIAL_INTERSECTS, r1,
2064                    r2);
2065        }
2066        Expression r = readConcat();
2067        while (true) {
2068            // special case: NOT NULL is not part of an expression (as in CREATE
2069            // TABLE TEST(ID INT DEFAULT 0 NOT NULL))
2070            int backup = parseIndex;
2071            boolean not = false;
2072            if (readIf("NOT")) {
2073                not = true;
2074                if (isToken("NULL")) {
2075                    // this really only works for NOT NULL!
2076                    parseIndex = backup;
2077                    currentToken = "NOT";
2078                    break;
2079                }
2080            }
2081            if (readIf("LIKE")) {
2082                Expression b = readConcat();
2083                Expression esc = null;
2084                if (readIf("ESCAPE")) {
2085                    esc = readConcat();
2086                }
2087                recompileAlways = true;
2088                r = new CompareLike(database, r, b, esc, false);
2089            } else if (readIf("REGEXP")) {
2090                Expression b = readConcat();
2091                r = new CompareLike(database, r, b, null, true);
2092            } else if (readIf("IS")) {
2093                if (readIf("NOT")) {
2094                    if (readIf("NULL")) {
2095                        r = new Comparison(session, Comparison.IS_NOT_NULL, r,
2096                                null);
2097                    } else if (readIf("DISTINCT")) {
2098                        read("FROM");
2099                        r = new Comparison(session, Comparison.EQUAL_NULL_SAFE,
2100                                r, readConcat());
2101                    } else {
2102                        r = new Comparison(session,
2103                                Comparison.NOT_EQUAL_NULL_SAFE, r, readConcat());
2104                    }
2105                } else if (readIf("NULL")) {
2106                    r = new Comparison(session, Comparison.IS_NULL, r, null);
2107                } else if (readIf("DISTINCT")) {
2108                    read("FROM");
2109                    r = new Comparison(session, Comparison.NOT_EQUAL_NULL_SAFE,
2110                            r, readConcat());
2111                } else {
2112                    r = new Comparison(session, Comparison.EQUAL_NULL_SAFE, r,
2113                            readConcat());
2114                }
2115            } else if (readIf("IN")) {
2116                read("(");
2117                if (readIf(")")) {
2118                    r = ValueExpression.get(ValueBoolean.get(false));
2119                } else {
2120                    if (isSelect()) {
2121                        Query query = parseSelect();
2122                        r = new ConditionInSelect(database, r, query, false,
2123                                Comparison.EQUAL);
2124                    } else {
2125                        ArrayList<Expression> v = New.arrayList();
2126                        Expression last;
2127                        do {
2128                            last = readExpression();
2129                            v.add(last);
2130                        } while (readIf(","));
2131                        if (v.size() == 1 && (last instanceof Subquery)) {
2132                            Subquery s = (Subquery) last;
2133                            Query q = s.getQuery();
2134                            r = new ConditionInSelect(database, r, q, false,
2135                                    Comparison.EQUAL);
2136                        } else {
2137                            r = new ConditionIn(database, r, v);
2138                        }
2139                    }
2140                    read(")");
2141                }
2142            } else if (readIf("BETWEEN")) {
2143                Expression low = readConcat();
2144                read("AND");
2145                Expression high = readConcat();
2146                Expression condLow = new Comparison(session,
2147                        Comparison.SMALLER_EQUAL, low, r);
2148                Expression condHigh = new Comparison(session,
2149                        Comparison.BIGGER_EQUAL, high, r);
2150                r = new ConditionAndOr(ConditionAndOr.AND, condLow, condHigh);
2151            } else {
2152                int compareType = getCompareType(currentTokenType);
2153                if (compareType < 0) {
2154                    break;
2155                }
2156                read();
2157                if (readIf("ALL")) {
2158                    read("(");
2159                    Query query = parseSelect();
2160                    r = new ConditionInSelect(database, r, query, true,
2161                            compareType);
2162                    read(")");
2163                } else if (readIf("ANY") || readIf("SOME")) {
2164                    read("(");
2165                    Query query = parseSelect();
2166                    r = new ConditionInSelect(database, r, query, false,
2167                            compareType);
2168                    read(")");
2169                } else {
2170                    Expression right = readConcat();
2171                    if (SysProperties.OLD_STYLE_OUTER_JOIN &&
2172                            readIf("(") && readIf("+") && readIf(")")) {
2173                        // support for a subset of old-fashioned Oracle outer
2174                        // join with (+)
2175                        if (r instanceof ExpressionColumn &&
2176                                right instanceof ExpressionColumn) {
2177                            ExpressionColumn leftCol = (ExpressionColumn) r;
2178                            ExpressionColumn rightCol = (ExpressionColumn) right;
2179                            ArrayList<TableFilter> filters = currentSelect
2180                                    .getTopFilters();
2181                            for (TableFilter f : filters) {
2182                                while (f != null) {
2183                                    leftCol.mapColumns(f, 0);
2184                                    rightCol.mapColumns(f, 0);
2185                                    f = f.getJoin();
2186                                }
2187                            }
2188                            TableFilter leftFilter = leftCol.getTableFilter();
2189                            TableFilter rightFilter = rightCol.getTableFilter();
2190                            r = new Comparison(session, compareType, r, right);
2191                            if (leftFilter != null && rightFilter != null) {
2192                                int idx = filters.indexOf(rightFilter);
2193                                if (idx >= 0) {
2194                                    filters.remove(idx);
2195                                    leftFilter.addJoin(rightFilter, true,
2196                                            false, r);
2197                                } else {
2198                                    rightFilter.mapAndAddFilter(r);
2199                                }
2200                                r = ValueExpression.get(ValueBoolean.get(true));
2201                            }
2202                        }
2203                    } else {
2204                        r = new Comparison(session, compareType, r, right);
2205                    }
2206                }
2207            }
2208            if (not) {
2209                r = new ConditionNot(r);
2210            }
2211        }
2212        return r;
2213    }
2214 
2215    private Expression readConcat() {
2216        Expression r = readSum();
2217        while (true) {
2218            if (readIf("||")) {
2219                r = new Operation(Operation.CONCAT, r, readSum());
2220            } else if (readIf("~")) {
2221                if (readIf("*")) {
2222                    Function function = Function.getFunction(database, "CAST");
2223                    function.setDataType(new Column("X",
2224                            Value.STRING_IGNORECASE));
2225                    function.setParameter(0, r);
2226                    r = function;
2227                }
2228                r = new CompareLike(database, r, readSum(), null, true);
2229            } else if (readIf("!~")) {
2230                if (readIf("*")) {
2231                    Function function = Function.getFunction(database, "CAST");
2232                    function.setDataType(new Column("X",
2233                            Value.STRING_IGNORECASE));
2234                    function.setParameter(0, r);
2235                    r = function;
2236                }
2237                r = new ConditionNot(new CompareLike(database, r, readSum(),
2238                        null, true));
2239            } else {
2240                return r;
2241            }
2242        }
2243    }
2244 
2245    private Expression readSum() {
2246        Expression r = readFactor();
2247        while (true) {
2248            if (readIf("+")) {
2249                r = new Operation(Operation.PLUS, r, readFactor());
2250            } else if (readIf("-")) {
2251                r = new Operation(Operation.MINUS, r, readFactor());
2252            } else {
2253                return r;
2254            }
2255        }
2256    }
2257 
2258    private Expression readFactor() {
2259        Expression r = readTerm();
2260        while (true) {
2261            if (readIf("*")) {
2262                r = new Operation(Operation.MULTIPLY, r, readTerm());
2263            } else if (readIf("/")) {
2264                r = new Operation(Operation.DIVIDE, r, readTerm());
2265            } else if (readIf("%")) {
2266                r = new Operation(Operation.MODULUS, r, readTerm());
2267            } else {
2268                return r;
2269            }
2270        }
2271    }
2272 
2273    private Expression readAggregate(int aggregateType) {
2274        if (currentSelect == null) {
2275            throw getSyntaxError();
2276        }
2277        currentSelect.setGroupQuery();
2278        Expression r;
2279        if (aggregateType == Aggregate.COUNT) {
2280            if (readIf("*")) {
2281                r = new Aggregate(Aggregate.COUNT_ALL, null, currentSelect,
2282                        false);
2283            } else {
2284                boolean distinct = readIf("DISTINCT");
2285                Expression on = readExpression();
2286                if (on instanceof Wildcard && !distinct) {
2287                    // PostgreSQL compatibility: count(t.*)
2288                    r = new Aggregate(Aggregate.COUNT_ALL, null, currentSelect,
2289                            false);
2290                } else {
2291                    r = new Aggregate(Aggregate.COUNT, on, currentSelect,
2292                            distinct);
2293                }
2294            }
2295        } else if (aggregateType == Aggregate.GROUP_CONCAT) {
2296            boolean distinct = readIf("DISTINCT");
2297            Aggregate agg = new Aggregate(Aggregate.GROUP_CONCAT,
2298                    readExpression(), currentSelect, distinct);
2299            if (readIf("ORDER")) {
2300                read("BY");
2301                agg.setGroupConcatOrder(parseSimpleOrderList());
2302            }
2303            if (readIf("SEPARATOR")) {
2304                agg.setGroupConcatSeparator(readExpression());
2305            }
2306            r = agg;
2307        } else {
2308            boolean distinct = readIf("DISTINCT");
2309            r = new Aggregate(aggregateType, readExpression(), currentSelect,
2310                    distinct);
2311        }
2312        read(")");
2313        return r;
2314    }
2315 
2316    private ArrayList<SelectOrderBy> parseSimpleOrderList() {
2317        ArrayList<SelectOrderBy> orderList = New.arrayList();
2318        do {
2319            SelectOrderBy order = new SelectOrderBy();
2320            Expression expr = readExpression();
2321            order.expression = expr;
2322            if (readIf("DESC")) {
2323                order.descending = true;
2324            } else {
2325                readIf("ASC");
2326            }
2327            orderList.add(order);
2328        } while (readIf(","));
2329        return orderList;
2330    }
2331 
2332    private JavaFunction readJavaFunction(Schema schema, String functionName) {
2333        FunctionAlias functionAlias = null;
2334        if (schema != null) {
2335            functionAlias = schema.findFunction(functionName);
2336        } else {
2337            functionAlias = findFunctionAlias(session.getCurrentSchemaName(),
2338                    functionName);
2339        }
2340        if (functionAlias == null) {
2341            throw DbException.get(ErrorCode.FUNCTION_NOT_FOUND_1, functionName);
2342        }
2343        Expression[] args;
2344        ArrayList<Expression> argList = New.arrayList();
2345        int numArgs = 0;
2346        while (!readIf(")")) {
2347            if (numArgs++ > 0) {
2348                read(",");
2349            }
2350            argList.add(readExpression());
2351        }
2352        args = new Expression[numArgs];
2353        argList.toArray(args);
2354        JavaFunction func = new JavaFunction(functionAlias, args);
2355        return func;
2356    }
2357 
2358    private JavaAggregate readJavaAggregate(UserAggregate aggregate) {
2359        ArrayList<Expression> params = New.arrayList();
2360        do {
2361            params.add(readExpression());
2362        } while (readIf(","));
2363        read(")");
2364        Expression[] list = new Expression[params.size()];
2365        params.toArray(list);
2366        JavaAggregate agg = new JavaAggregate(aggregate, list, currentSelect);
2367        currentSelect.setGroupQuery();
2368        return agg;
2369    }
2370 
2371    private int getAggregateType(String name) {
2372        if (!identifiersToUpper) {
2373            // if not yet converted to uppercase, do it now
2374            name = StringUtils.toUpperEnglish(name);
2375        }
2376        return Aggregate.getAggregateType(name);
2377    }
2378 
2379    private Expression readFunction(Schema schema, String name) {
2380        if (schema != null) {
2381            return readJavaFunction(schema, name);
2382        }
2383        int agg = getAggregateType(name);
2384        if (agg >= 0) {
2385            return readAggregate(agg);
2386        }
2387        Function function = Function.getFunction(database, name);
2388        if (function == null) {
2389            UserAggregate aggregate = database.findAggregate(name);
2390            if (aggregate != null) {
2391                return readJavaAggregate(aggregate);
2392            }
2393            return readJavaFunction(null, name);
2394        }
2395        switch (function.getFunctionType()) {
2396        case Function.CAST: {
2397            function.setParameter(0, readExpression());
2398            read("AS");
2399            Column type = parseColumnWithType(null);
2400            function.setDataType(type);
2401            read(")");
2402            break;
2403        }
2404        case Function.CONVERT: {
2405            if (database.getMode().swapConvertFunctionParameters) {
2406                Column type = parseColumnWithType(null);
2407                function.setDataType(type);
2408                read(",");
2409                function.setParameter(0, readExpression());
2410                read(")");
2411            } else {
2412                function.setParameter(0, readExpression());
2413                read(",");
2414                Column type = parseColumnWithType(null);
2415                function.setDataType(type);
2416                read(")");
2417            }
2418            break;
2419        }
2420        case Function.EXTRACT: {
2421            function.setParameter(0,
2422                    ValueExpression.get(ValueString.get(currentToken)));
2423            read();
2424            read("FROM");
2425            function.setParameter(1, readExpression());
2426            read(")");
2427            break;
2428        }
2429        case Function.DATE_ADD:
2430        case Function.DATE_DIFF: {
2431            if (Function.isDatePart(currentToken)) {
2432                function.setParameter(0,
2433                        ValueExpression.get(ValueString.get(currentToken)));
2434                read();
2435            } else {
2436                function.setParameter(0, readExpression());
2437            }
2438            read(",");
2439            function.setParameter(1, readExpression());
2440            read(",");
2441            function.setParameter(2, readExpression());
2442            read(")");
2443            break;
2444        }
2445        case Function.SUBSTRING: {
2446            // Different variants include:
2447            // SUBSTRING(X,1)
2448            // SUBSTRING(X,1,1)
2449            // SUBSTRING(X FROM 1 FOR 1) -- Postgres
2450            // SUBSTRING(X FROM 1) -- Postgres
2451            // SUBSTRING(X FOR 1) -- Postgres
2452            function.setParameter(0, readExpression());
2453            if (readIf("FROM")) {
2454                function.setParameter(1, readExpression());
2455                if (readIf("FOR")) {
2456                    function.setParameter(2, readExpression());
2457                }
2458            } else if (readIf("FOR")) {
2459                function.setParameter(1, ValueExpression.get(ValueInt.get(0)));
2460                function.setParameter(2, readExpression());
2461            } else {
2462                read(",");
2463                function.setParameter(1, readExpression());
2464                if (readIf(",")) {
2465                    function.setParameter(2, readExpression());
2466                }
2467            }
2468            read(")");
2469            break;
2470        }
2471        case Function.POSITION: {
2472            // can't read expression because IN would be read too early
2473            function.setParameter(0, readConcat());
2474            if (!readIf(",")) {
2475                read("IN");
2476            }
2477            function.setParameter(1, readExpression());
2478            read(")");
2479            break;
2480        }
2481        case Function.TRIM: {
2482            Expression space = null;
2483            if (readIf("LEADING")) {
2484                function = Function.getFunction(database, "LTRIM");
2485                if (!readIf("FROM")) {
2486                    space = readExpression();
2487                    read("FROM");
2488                }
2489            } else if (readIf("TRAILING")) {
2490                function = Function.getFunction(database, "RTRIM");
2491                if (!readIf("FROM")) {
2492                    space = readExpression();
2493                    read("FROM");
2494                }
2495            } else if (readIf("BOTH")) {
2496                if (!readIf("FROM")) {
2497                    space = readExpression();
2498                    read("FROM");
2499                }
2500            }
2501            Expression p0 = readExpression();
2502            if (readIf(",")) {
2503                space = readExpression();
2504            } else if (readIf("FROM")) {
2505                space = p0;
2506                p0 = readExpression();
2507            }
2508            function.setParameter(0, p0);
2509            if (space != null) {
2510                function.setParameter(1, space);
2511            }
2512            read(")");
2513            break;
2514        }
2515        case Function.TABLE:
2516        case Function.TABLE_DISTINCT: {
2517            int i = 0;
2518            ArrayList<Column> columns = New.arrayList();
2519            do {
2520                String columnName = readAliasIdentifier();
2521                Column column = parseColumnWithType(columnName);
2522                columns.add(column);
2523                read("=");
2524                function.setParameter(i, readExpression());
2525                i++;
2526            } while (readIf(","));
2527            read(")");
2528            TableFunction tf = (TableFunction) function;
2529            tf.setColumns(columns);
2530            break;
2531        }
2532        case Function.ROW_NUMBER:
2533            read(")");
2534            read("OVER");
2535            read("(");
2536            read(")");
2537            return new Rownum(currentSelect == null ? currentPrepared
2538                    : currentSelect);
2539        default:
2540            if (!readIf(")")) {
2541                int i = 0;
2542                do {
2543                    function.setParameter(i++, readExpression());
2544                } while (readIf(","));
2545                read(")");
2546            }
2547        }
2548        function.doneWithParameters();
2549        return function;
2550    }
2551 
2552    private Function readFunctionWithoutParameters(String name) {
2553        if (readIf("(")) {
2554            read(")");
2555        }
2556        Function function = Function.getFunction(database, name);
2557        function.doneWithParameters();
2558        return function;
2559    }
2560 
2561    private Expression readWildcardOrSequenceValue(String schema,
2562            String objectName) {
2563        if (readIf("*")) {
2564            return new Wildcard(schema, objectName);
2565        }
2566        if (schema == null) {
2567            schema = session.getCurrentSchemaName();
2568        }
2569        if (readIf("NEXTVAL")) {
2570            Sequence sequence = findSequence(schema, objectName);
2571            if (sequence != null) {
2572                return new SequenceValue(sequence);
2573            }
2574        } else if (readIf("CURRVAL")) {
2575            Sequence sequence = findSequence(schema, objectName);
2576            if (sequence != null) {
2577                Function function = Function.getFunction(database, "CURRVAL");
2578                function.setParameter(0, ValueExpression.get(ValueString
2579                        .get(sequence.getSchema().getName())));
2580                function.setParameter(1, ValueExpression.get(ValueString
2581                        .get(sequence.getName())));
2582                function.doneWithParameters();
2583                return function;
2584            }
2585        }
2586        return null;
2587    }
2588 
2589    private Expression readTermObjectDot(String objectName) {
2590        Expression expr = readWildcardOrSequenceValue(null, objectName);
2591        if (expr != null) {
2592            return expr;
2593        }
2594        String name = readColumnIdentifier();
2595        Schema s = database.findSchema(objectName);
2596        if ((!SysProperties.OLD_STYLE_OUTER_JOIN || s != null) && readIf("(")) {
2597            // only if the token before the dot is a valid schema name,
2598            // otherwise the old style Oracle outer join doesn't work:
2599            // t.x = t2.x(+)
2600            // this additional check is not required
2601            // if the old style outer joins are not supported
2602            return readFunction(s, name);
2603        } else if (readIf(".")) {
2604            String schema = objectName;
2605            objectName = name;
2606            expr = readWildcardOrSequenceValue(schema, objectName);
2607            if (expr != null) {
2608                return expr;
2609            }
2610            name = readColumnIdentifier();
2611            if (readIf("(")) {
2612                String databaseName = schema;
2613                if (!equalsToken(database.getShortName(), databaseName)) {
2614                    throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1,
2615                            databaseName);
2616                }
2617                schema = objectName;
2618                return readFunction(database.getSchema(schema), name);
2619            } else if (readIf(".")) {
2620                String databaseName = schema;
2621                if (!equalsToken(database.getShortName(), databaseName)) {
2622                    throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1,
2623                            databaseName);
2624                }
2625                schema = objectName;
2626                objectName = name;
2627                expr = readWildcardOrSequenceValue(schema, objectName);
2628                if (expr != null) {
2629                    return expr;
2630                }
2631                name = readColumnIdentifier();
2632                return new ExpressionColumn(database, schema, objectName, name);
2633            }
2634            return new ExpressionColumn(database, schema, objectName, name);
2635        }
2636        return new ExpressionColumn(database, null, objectName, name);
2637    }
2638 
2639    private Expression readTerm() {
2640        Expression r;
2641        switch (currentTokenType) {
2642        case AT:
2643            read();
2644            r = new Variable(session, readAliasIdentifier());
2645            if (readIf(":=")) {
2646                Expression value = readExpression();
2647                Function function = Function.getFunction(database, "SET");
2648                function.setParameter(0, r);
2649                function.setParameter(1, value);
2650                r = function;
2651            }
2652            break;
2653        case PARAMETER:
2654            // there must be no space between ? and the number
2655            boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
2656            read();
2657            Parameter p;
2658            if (indexed && currentTokenType == VALUE &&
2659                    currentValue.getType() == Value.INT) {
2660                if (indexedParameterList == null) {
2661                    if (parameters == null) {
2662                        // this can occur when parsing expressions only (for
2663                        // example check constraints)
2664                        throw getSyntaxError();
2665                    } else if (parameters.size() > 0) {
2666                        throw DbException
2667                                .get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
2668                    }
2669                    indexedParameterList = New.arrayList();
2670                }
2671                int index = currentValue.getInt() - 1;
2672                if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
2673                    throw DbException.getInvalidValueException(
2674                            "parameter index", index);
2675                }
2676                if (indexedParameterList.size() <= index) {
2677                    indexedParameterList.ensureCapacity(index + 1);
2678                    while (indexedParameterList.size() <= index) {
2679                        indexedParameterList.add(null);
2680                    }
2681                }
2682                p = indexedParameterList.get(index);
2683                if (p == null) {
2684                    p = new Parameter(index);
2685                    indexedParameterList.set(index, p);
2686                }
2687                read();
2688            } else {
2689                if (indexedParameterList != null) {
2690                    throw DbException
2691                            .get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
2692                }
2693                p = new Parameter(parameters.size());
2694            }
2695            parameters.add(p);
2696            r = p;
2697            break;
2698        case KEYWORD:
2699            if (isToken("SELECT") || isToken("FROM")) {
2700                Query query = parseSelect();
2701                r = new Subquery(query);
2702            } else {
2703                throw getSyntaxError();
2704            }
2705            break;
2706        case IDENTIFIER:
2707            String name = currentToken;
2708            if (currentTokenQuoted) {
2709                read();
2710                if (readIf("(")) {
2711                    r = readFunction(null, name);
2712                } else if (readIf(".")) {
2713                    r = readTermObjectDot(name);
2714                } else {
2715                    r = new ExpressionColumn(database, null, null, name);
2716                }
2717            } else {
2718                read();
2719                if (readIf(".")) {
2720                    r = readTermObjectDot(name);
2721                } else if (equalsToken("CASE", name)) {
2722                    // CASE must be processed before (,
2723                    // otherwise CASE(3) would be a function call, which it is
2724                    // not
2725                    r = readCase();
2726                } else if (readIf("(")) {
2727                    r = readFunction(null, name);
2728                } else if (equalsToken("CURRENT_USER", name)) {
2729                    r = readFunctionWithoutParameters("USER");
2730                } else if (equalsToken("CURRENT", name)) {
2731                    if (readIf("TIMESTAMP")) {
2732                        r = readFunctionWithoutParameters("CURRENT_TIMESTAMP");
2733                    } else if (readIf("TIME")) {
2734                        r = readFunctionWithoutParameters("CURRENT_TIME");
2735                    } else if (readIf("DATE")) {
2736                        r = readFunctionWithoutParameters("CURRENT_DATE");
2737                    } else {
2738                        r = new ExpressionColumn(database, null, null, name);
2739                    }
2740                } else if (equalsToken("NEXT", name) && readIf("VALUE")) {
2741                    read("FOR");
2742                    Sequence sequence = readSequence();
2743                    r = new SequenceValue(sequence);
2744                } else if (currentTokenType == VALUE &&
2745                        currentValue.getType() == Value.STRING) {
2746                    if (equalsToken("DATE", name) ||
2747                            equalsToken("D", name)) {
2748                        String date = currentValue.getString();
2749                        read();
2750                        r = ValueExpression.get(ValueDate.parse(date));
2751                    } else if (equalsToken("TIME", name) ||
2752                            equalsToken("T", name)) {
2753                        String time = currentValue.getString();
2754                        read();
2755                        r = ValueExpression.get(ValueTime.parse(time));
2756                    } else if (equalsToken("TIMESTAMP", name) ||
2757                            equalsToken("TS", name)) {
2758                        String timestamp = currentValue.getString();
2759                        read();
2760                        r = ValueExpression
2761                                .get(ValueTimestamp.parse(timestamp));
2762                    } else if (equalsToken("X", name)) {
2763                        read();
2764                        byte[] buffer = StringUtils
2765                                .convertHexToBytes(currentValue.getString());
2766                        r = ValueExpression.get(ValueBytes.getNoCopy(buffer));
2767                    } else if (equalsToken("E", name)) {
2768                        String text = currentValue.getString();
2769                        // the PostgreSQL ODBC driver uses
2770                        // LIKE E'PROJECT\\_DATA' instead of LIKE
2771                        // 'PROJECT\_DATA'
2772                        // N: SQL-92 "National Language" strings
2773                        text = StringUtils.replaceAll(text, "\\\\", "\\");
2774                        read();
2775                        r = ValueExpression.get(ValueString.get(text));
2776                    } else if (equalsToken("N", name)) {
2777                        // SQL-92 "National Language" strings
2778                        String text = currentValue.getString();
2779                        read();
2780                        r = ValueExpression.get(ValueString.get(text));
2781                    } else {
2782                        r = new ExpressionColumn(database, null, null, name);
2783                    }
2784                } else {
2785                    r = new ExpressionColumn(database, null, null, name);
2786                }
2787            }
2788            break;
2789        case MINUS:
2790            read();
2791            if (currentTokenType == VALUE) {
2792                r = ValueExpression.get(currentValue.negate());
2793                if (r.getType() == Value.LONG &&
2794                        r.getValue(session).getLong() == Integer.MIN_VALUE) {
2795                    // convert Integer.MIN_VALUE to type 'int'
2796                    // (Integer.MAX_VALUE+1 is of type 'long')
2797                    r = ValueExpression.get(ValueInt.get(Integer.MIN_VALUE));
2798                } else if (r.getType() == Value.DECIMAL &&
2799                        r.getValue(session).getBigDecimal()
2800                                .compareTo(ValueLong.MIN_BD) == 0) {
2801                    // convert Long.MIN_VALUE to type 'long'
2802                    // (Long.MAX_VALUE+1 is of type 'decimal')
2803                    r = ValueExpression.get(ValueLong.get(Long.MIN_VALUE));
2804                }
2805                read();
2806            } else {
2807                r = new Operation(Operation.NEGATE, readTerm(), null);
2808            }
2809            break;
2810        case PLUS:
2811            read();
2812            r = readTerm();
2813            break;
2814        case OPEN:
2815            read();
2816            if (readIf(")")) {
2817                r = new ExpressionList(new Expression[0]);
2818            } else {
2819                r = readExpression();
2820                if (readIf(",")) {
2821                    ArrayList<Expression> list = New.arrayList();
2822                    list.add(r);
2823                    while (!readIf(")")) {
2824                        r = readExpression();
2825                        list.add(r);
2826                        if (!readIf(",")) {
2827                            read(")");
2828                            break;
2829                        }
2830                    }
2831                    Expression[] array = new Expression[list.size()];
2832                    list.toArray(array);
2833                    r = new ExpressionList(array);
2834                } else {
2835                    read(")");
2836                }
2837            }
2838            break;
2839        case TRUE:
2840            read();
2841            r = ValueExpression.get(ValueBoolean.get(true));
2842            break;
2843        case FALSE:
2844            read();
2845            r = ValueExpression.get(ValueBoolean.get(false));
2846            break;
2847        case CURRENT_TIME:
2848            read();
2849            r = readFunctionWithoutParameters("CURRENT_TIME");
2850            break;
2851        case CURRENT_DATE:
2852            read();
2853            r = readFunctionWithoutParameters("CURRENT_DATE");
2854            break;
2855        case CURRENT_TIMESTAMP: {
2856            Function function = Function.getFunction(database,
2857                    "CURRENT_TIMESTAMP");
2858            read();
2859            if (readIf("(")) {
2860                if (!readIf(")")) {
2861                    function.setParameter(0, readExpression());
2862                    read(")");
2863                }
2864            }
2865            function.doneWithParameters();
2866            r = function;
2867            break;
2868        }
2869        case ROWNUM:
2870            read();
2871            if (readIf("(")) {
2872                read(")");
2873            }
2874            r = new Rownum(currentSelect == null ? currentPrepared
2875                    : currentSelect);
2876            break;
2877        case NULL:
2878            read();
2879            r = ValueExpression.getNull();
2880            break;
2881        case VALUE:
2882            r = ValueExpression.get(currentValue);
2883            read();
2884            break;
2885        default:
2886            throw getSyntaxError();
2887        }
2888        if (readIf("[")) {
2889            Function function = Function.getFunction(database, "ARRAY_GET");
2890            function.setParameter(0, r);
2891            r = readExpression();
2892            r = new Operation(Operation.PLUS, r, ValueExpression.get(ValueInt
2893                    .get(1)));
2894            function.setParameter(1, r);
2895            r = function;
2896            read("]");
2897        }
2898        if (readIf("::")) {
2899            // PostgreSQL compatibility
2900            if (isToken("PG_CATALOG")) {
2901                read("PG_CATALOG");
2902                read(".");
2903            }
2904            if (readIf("REGCLASS")) {
2905                FunctionAlias f = findFunctionAlias(Constants.SCHEMA_MAIN,
2906                        "PG_GET_OID");
2907                if (f == null) {
2908                    throw getSyntaxError();
2909                }
2910                Expression[] args = { r };
2911                JavaFunction func = new JavaFunction(f, args);
2912                r = func;
2913            } else {
2914                Column col = parseColumnWithType(null);
2915                Function function = Function.getFunction(database, "CAST");
2916                function.setDataType(col);
2917                function.setParameter(0, r);
2918                r = function;
2919            }
2920        }
2921        return r;
2922    }
2923 
2924    private Expression readCase() {
2925        if (readIf("END")) {
2926            readIf("CASE");
2927            return ValueExpression.getNull();
2928        }
2929        if (readIf("ELSE")) {
2930            Expression elsePart = readExpression().optimize(session);
2931            read("END");
2932            readIf("CASE");
2933            return elsePart;
2934        }
2935        int i;
2936        Function function;
2937        if (readIf("WHEN")) {
2938            function = Function.getFunction(database, "CASE");
2939            function.setParameter(0, null);
2940            i = 1;
2941            do {
2942                function.setParameter(i++, readExpression());
2943                read("THEN");
2944                function.setParameter(i++, readExpression());
2945            } while (readIf("WHEN"));
2946        } else {
2947            Expression expr = readExpression();
2948            if (readIf("END")) {
2949                readIf("CASE");
2950                return ValueExpression.getNull();
2951            }
2952            if (readIf("ELSE")) {
2953                Expression elsePart = readExpression().optimize(session);
2954                read("END");
2955                readIf("CASE");
2956                return elsePart;
2957            }
2958            function = Function.getFunction(database, "CASE");
2959            function.setParameter(0, expr);
2960            i = 1;
2961            read("WHEN");
2962            do {
2963                function.setParameter(i++, readExpression());
2964                read("THEN");
2965                function.setParameter(i++, readExpression());
2966            } while (readIf("WHEN"));
2967        }
2968        if (readIf("ELSE")) {
2969            function.setParameter(i, readExpression());
2970        }
2971        read("END");
2972        readIf("CASE");
2973        function.doneWithParameters();
2974        return function;
2975    }
2976 
2977    private int readPositiveInt() {
2978        int v = readInt();
2979        if (v < 0) {
2980            throw DbException.getInvalidValueException("positive integer", v);
2981        }
2982        return v;
2983    }
2984 
2985    private int readInt() {
2986        boolean minus = false;
2987        if (currentTokenType == MINUS) {
2988            minus = true;
2989            read();
2990        } else if (currentTokenType == PLUS) {
2991            read();
2992        }
2993        if (currentTokenType != VALUE) {
2994            throw DbException.getSyntaxError(sqlCommand, parseIndex, "integer");
2995        }
2996        if (minus) {
2997            // must do that now, otherwise Integer.MIN_VALUE would not work
2998            currentValue = currentValue.negate();
2999        }
3000        int i = currentValue.getInt();
3001        read();
3002        return i;
3003    }
3004 
3005    private long readLong() {
3006        boolean minus = false;
3007        if (currentTokenType == MINUS) {
3008            minus = true;
3009            read();
3010        } else if (currentTokenType == PLUS) {
3011            read();
3012        }
3013        if (currentTokenType != VALUE) {
3014            throw DbException.getSyntaxError(sqlCommand, parseIndex, "long");
3015        }
3016        if (minus) {
3017            // must do that now, otherwise Long.MIN_VALUE would not work
3018            currentValue = currentValue.negate();
3019        }
3020        long i = currentValue.getLong();
3021        read();
3022        return i;
3023    }
3024 
3025    private boolean readBooleanSetting() {
3026        if (currentTokenType == VALUE) {
3027            boolean result = currentValue.getBoolean().booleanValue();
3028            read();
3029            return result;
3030        }
3031        if (readIf("TRUE") || readIf("ON")) {
3032            return true;
3033        } else if (readIf("FALSE") || readIf("OFF")) {
3034            return false;
3035        } else {
3036            throw getSyntaxError();
3037        }
3038    }
3039 
3040    private String readString() {
3041        Expression expr = readExpression().optimize(session);
3042        if (!(expr instanceof ValueExpression)) {
3043            throw DbException.getSyntaxError(sqlCommand, parseIndex, "string");
3044        }
3045        String s = expr.getValue(session).getString();
3046        return s;
3047    }
3048 
3049    private String readIdentifierWithSchema(String defaultSchemaName) {
3050        if (currentTokenType != IDENTIFIER) {
3051            throw DbException.getSyntaxError(sqlCommand, parseIndex,
3052                    "identifier");
3053        }
3054        String s = currentToken;
3055        read();
3056        schemaName = defaultSchemaName;
3057        if (readIf(".")) {
3058            schemaName = s;
3059            if (currentTokenType != IDENTIFIER) {
3060                throw DbException.getSyntaxError(sqlCommand, parseIndex,
3061                        "identifier");
3062            }
3063            s = currentToken;
3064            read();
3065        }
3066        if (equalsToken(".", currentToken)) {
3067            if (equalsToken(schemaName, database.getShortName())) {
3068                read(".");
3069                schemaName = s;
3070                if (currentTokenType != IDENTIFIER) {
3071                    throw DbException.getSyntaxError(sqlCommand, parseIndex,
3072                            "identifier");
3073                }
3074                s = currentToken;
3075                read();
3076            }
3077        }
3078        return s;
3079    }
3080 
3081    private String readIdentifierWithSchema() {
3082        return readIdentifierWithSchema(session.getCurrentSchemaName());
3083    }
3084 
3085    private String readAliasIdentifier() {
3086        return readColumnIdentifier();
3087    }
3088 
3089    private String readUniqueIdentifier() {
3090        return readColumnIdentifier();
3091    }
3092 
3093    private String readColumnIdentifier() {
3094        if (currentTokenType != IDENTIFIER) {
3095            throw DbException.getSyntaxError(sqlCommand, parseIndex,
3096                    "identifier");
3097        }
3098        String s = currentToken;
3099        read();
3100        return s;
3101    }
3102 
3103    private void read(String expected) {
3104        if (currentTokenQuoted || !equalsToken(expected, currentToken)) {
3105            addExpected(expected);
3106            throw getSyntaxError();
3107        }
3108        read();
3109    }
3110 
3111    private boolean readIf(String token) {
3112        if (!currentTokenQuoted && equalsToken(token, currentToken)) {
3113            read();
3114            return true;
3115        }
3116        addExpected(token);
3117        return false;
3118    }
3119 
3120    private boolean isToken(String token) {
3121        boolean result = equalsToken(token, currentToken) &&
3122                !currentTokenQuoted;
3123        if (result) {
3124            return true;
3125        }
3126        addExpected(token);
3127        return false;
3128    }
3129 
3130    private boolean equalsToken(String a, String b) {
3131        if (a == null) {
3132            return b == null;
3133        } else if (a.equals(b)) {
3134            return true;
3135        } else if (!identifiersToUpper && a.equalsIgnoreCase(b)) {
3136            return true;
3137        }
3138        return false;
3139    }
3140 
3141    private void addExpected(String token) {
3142        if (expectedList != null) {
3143            expectedList.add(token);
3144        }
3145    }
3146 
3147    private void read() {
3148        currentTokenQuoted = false;
3149        if (expectedList != null) {
3150            expectedList.clear();
3151        }
3152        int[] types = characterTypes;
3153        lastParseIndex = parseIndex;
3154        int i = parseIndex;
3155        int type = types[i];
3156        while (type == 0) {
3157            type = types[++i];
3158        }
3159        int start = i;
3160        char[] chars = sqlCommandChars;
3161        char c = chars[i++];
3162        currentToken = "";
3163        switch (type) {
3164        case CHAR_NAME:
3165            while (true) {
3166                type = types[i];
3167                if (type != CHAR_NAME && type != CHAR_VALUE) {
3168                    break;
3169                }
3170                i++;
3171            }
3172            currentToken = StringUtils.fromCacheOrNew(sqlCommand.substring(
3173                    start, i));
3174            currentTokenType = getTokenType(currentToken);
3175            parseIndex = i;
3176            return;
3177        case CHAR_QUOTED: {
3178            String result = null;
3179            while (true) {
3180                for (int begin = i;; i++) {
3181                    if (chars[i] == '\"') {
3182                        if (result == null) {
3183                            result = sqlCommand.substring(begin, i);
3184                        } else {
3185                            result += sqlCommand.substring(begin - 1, i);
3186                        }
3187                        break;
3188                    }
3189                }
3190                if (chars[++i] != '\"') {
3191                    break;
3192                }
3193                i++;
3194            }
3195            currentToken = StringUtils.fromCacheOrNew(result);
3196            parseIndex = i;
3197            currentTokenQuoted = true;
3198            currentTokenType = IDENTIFIER;
3199            return;
3200        }
3201        case CHAR_SPECIAL_2:
3202            if (types[i] == CHAR_SPECIAL_2) {
3203                i++;
3204            }
3205            currentToken = sqlCommand.substring(start, i);
3206            currentTokenType = getSpecialType(currentToken);
3207            parseIndex = i;
3208            return;
3209        case CHAR_SPECIAL_1:
3210            currentToken = sqlCommand.substring(start, i);
3211            currentTokenType = getSpecialType(currentToken);
3212            parseIndex = i;
3213            return;
3214        case CHAR_VALUE:
3215            if (c == '0' && chars[i] == 'X') {
3216                // hex number
3217                long number = 0;
3218                start += 2;
3219                i++;
3220                while (true) {
3221                    c = chars[i];
3222                    if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
3223                        checkLiterals(false);
3224                        currentValue = ValueInt.get((int) number);
3225                        currentTokenType = VALUE;
3226                        currentToken = "0";
3227                        parseIndex = i;
3228                        return;
3229                    }
3230                    number = (number << 4) + c -
3231                            (c >= 'A' ? ('A' - 0xa) : ('0'));
3232                    if (number > Integer.MAX_VALUE) {
3233                        readHexDecimal(start, i);
3234                        return;
3235                    }
3236                    i++;
3237                }
3238            }
3239            long number = c - '0';
3240            while (true) {
3241                c = chars[i];
3242                if (c < '0' || c > '9') {
3243                    if (c == '.' || c == 'E' || c == 'L') {
3244                        readDecimal(start, i);
3245                        break;
3246                    }
3247                    checkLiterals(false);
3248                    currentValue = ValueInt.get((int) number);
3249                    currentTokenType = VALUE;
3250                    currentToken = "0";
3251                    parseIndex = i;
3252                    break;
3253                }
3254                number = number * 10 + (c - '0');
3255                if (number > Integer.MAX_VALUE) {
3256                    readDecimal(start, i);
3257                    break;
3258                }
3259                i++;
3260            }
3261            return;
3262        case CHAR_DOT:
3263            if (types[i] != CHAR_VALUE) {
3264                currentTokenType = KEYWORD;
3265                currentToken = ".";
3266                parseIndex = i;
3267                return;
3268            }
3269            readDecimal(i - 1, i);
3270            return;
3271        case CHAR_STRING: {
3272            String result = null;
3273            while (true) {
3274                for (int begin = i;; i++) {
3275                    if (chars[i] == '\'') {
3276                        if (result == null) {
3277                            result = sqlCommand.substring(begin, i);
3278                        } else {
3279                            result += sqlCommand.substring(begin - 1, i);
3280                        }
3281                        break;
3282                    }
3283                }
3284                if (chars[++i] != '\'') {
3285                    break;
3286                }
3287                i++;
3288            }
3289            currentToken = "'";
3290            checkLiterals(true);
3291            currentValue = ValueString.get(StringUtils.fromCacheOrNew(result),
3292                    database.getMode().treatEmptyStringsAsNull);
3293            parseIndex = i;
3294            currentTokenType = VALUE;
3295            return;
3296        }
3297        case CHAR_DOLLAR_QUOTED_STRING: {
3298            String result = null;
3299            int begin = i - 1;
3300            while (types[i] == CHAR_DOLLAR_QUOTED_STRING) {
3301                i++;
3302            }
3303            result = sqlCommand.substring(begin, i);
3304            currentToken = "'";
3305            checkLiterals(true);
3306            currentValue = ValueString.get(StringUtils.fromCacheOrNew(result),
3307                    database.getMode().treatEmptyStringsAsNull);
3308            parseIndex = i;
3309            currentTokenType = VALUE;
3310            return;
3311        }
3312        case CHAR_END:
3313            currentToken = "";
3314            currentTokenType = END;
3315            parseIndex = i;
3316            return;
3317        default:
3318            throw getSyntaxError();
3319        }
3320    }
3321 
3322    private void checkLiterals(boolean text) {
3323        if (!session.getAllowLiterals()) {
3324            int allowed = database.getAllowLiterals();
3325            if (allowed == Constants.ALLOW_LITERALS_NONE ||
3326                    (text && allowed != Constants.ALLOW_LITERALS_ALL)) {
3327                throw DbException.get(ErrorCode.LITERALS_ARE_NOT_ALLOWED);
3328            }
3329        }
3330    }
3331 
3332    private void readHexDecimal(int start, int i) {
3333        char[] chars = sqlCommandChars;
3334        char c;
3335        do {
3336            c = chars[++i];
3337        } while ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'));
3338        parseIndex = i;
3339        String sub = sqlCommand.substring(start, i);
3340        BigDecimal bd = new BigDecimal(new BigInteger(sub, 16));
3341        checkLiterals(false);
3342        currentValue = ValueDecimal.get(bd);
3343        currentTokenType = VALUE;
3344    }
3345 
3346    private void readDecimal(int start, int i) {
3347        char[] chars = sqlCommandChars;
3348        int[] types = characterTypes;
3349        // go until the first non-number
3350        while (true) {
3351            int t = types[i];
3352            if (t != CHAR_DOT && t != CHAR_VALUE) {
3353                break;
3354            }
3355            i++;
3356        }
3357        boolean containsE = false;
3358        if (chars[i] == 'E' || chars[i] == 'e') {
3359            containsE = true;
3360            i++;
3361            if (chars[i] == '+' || chars[i] == '-') {
3362                i++;
3363            }
3364            if (types[i] != CHAR_VALUE) {
3365                throw getSyntaxError();
3366            }
3367            while (types[++i] == CHAR_VALUE) {
3368                // go until the first non-number
3369            }
3370        }
3371        parseIndex = i;
3372        String sub = sqlCommand.substring(start, i);
3373        checkLiterals(false);
3374        if (!containsE && sub.indexOf('.') < 0) {
3375            BigInteger bi = new BigInteger(sub);
3376            if (bi.compareTo(ValueLong.MAX) <= 0) {
3377                // parse constants like "10000000L"
3378                if (chars[i] == 'L') {
3379                    parseIndex++;
3380                }
3381                currentValue = ValueLong.get(bi.longValue());
3382                currentTokenType = VALUE;
3383                return;
3384            }
3385        }
3386        BigDecimal bd;
3387        try {
3388            bd = new BigDecimal(sub);
3389        } catch (NumberFormatException e) {
3390            throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, e, sub);
3391        }
3392        currentValue = ValueDecimal.get(bd);
3393        currentTokenType = VALUE;
3394    }
3395 
3396    public Session getSession() {
3397        return session;
3398    }
3399 
3400    private void initialize(String sql) {
3401        if (sql == null) {
3402            sql = "";
3403        }
3404        originalSQL = sql;
3405        sqlCommand = sql;
3406        int len = sql.length() + 1;
3407        char[] command = new char[len];
3408        int[] types = new int[len];
3409        len--;
3410        sql.getChars(0, len, command, 0);
3411        boolean changed = false;
3412        command[len] = ' ';
3413        int startLoop = 0;
3414        int lastType = 0;
3415        for (int i = 0; i < len; i++) {
3416            char c = command[i];
3417            int type = 0;
3418            switch (c) {
3419            case '/':
3420                if (command[i + 1] == '*') {
3421                    // block comment
3422                    changed = true;
3423                    command[i] = ' ';
3424                    command[i + 1] = ' ';
3425                    startLoop = i;
3426                    i += 2;
3427                    checkRunOver(i, len, startLoop);
3428                    while (command[i] != '*' || command[i + 1] != '/') {
3429                        command[i++] = ' ';
3430                        checkRunOver(i, len, startLoop);
3431                    }
3432                    command[i] = ' ';
3433                    command[i + 1] = ' ';
3434                    i++;
3435                } else if (command[i + 1] == '/') {
3436                    // single line comment
3437                    changed = true;
3438                    startLoop = i;
3439                    while (true) {
3440                        c = command[i];
3441                        if (c == '\n' || c == '\r' || i >= len - 1) {
3442                            break;
3443                        }
3444                        command[i++] = ' ';
3445                        checkRunOver(i, len, startLoop);
3446                    }
3447                } else {
3448                    type = CHAR_SPECIAL_1;
3449                }
3450                break;
3451            case '-':
3452                if (command[i + 1] == '-') {
3453                    // single line comment
3454                    changed = true;
3455                    startLoop = i;
3456                    while (true) {
3457                        c = command[i];
3458                        if (c == '\n' || c == '\r' || i >= len - 1) {
3459                            break;
3460                        }
3461                        command[i++] = ' ';
3462                        checkRunOver(i, len, startLoop);
3463                    }
3464                } else {
3465                    type = CHAR_SPECIAL_1;
3466                }
3467                break;
3468            case '$':
3469                if (command[i + 1] == '$' && (i == 0 || command[i - 1] <= ' ')) {
3470                    // dollar quoted string
3471                    changed = true;
3472                    command[i] = ' ';
3473                    command[i + 1] = ' ';
3474                    startLoop = i;
3475                    i += 2;
3476                    checkRunOver(i, len, startLoop);
3477                    while (command[i] != '$' || command[i + 1] != '$') {
3478                        types[i++] = CHAR_DOLLAR_QUOTED_STRING;
3479                        checkRunOver(i, len, startLoop);
3480                    }
3481                    command[i] = ' ';
3482                    command[i + 1] = ' ';
3483                    i++;
3484                } else {
3485                    if (lastType == CHAR_NAME || lastType == CHAR_VALUE) {
3486                        // $ inside an identifier is supported
3487                        type = CHAR_NAME;
3488                    } else {
3489                        // but not at the start, to support PostgreSQL $1
3490                        type = CHAR_SPECIAL_1;
3491                    }
3492                }
3493                break;
3494            case '(':
3495            case ')':
3496            case '{':
3497            case '}':
3498            case '*':
3499            case ',':
3500            case ';':
3501            case '+':
3502            case '%':
3503            case '?':
3504            case '@':
3505            case ']':
3506                type = CHAR_SPECIAL_1;
3507                break;
3508            case '!':
3509            case '<':
3510            case '>':
3511            case '|':
3512            case '=':
3513            case ':':
3514            case '&':
3515            case '~':
3516                type = CHAR_SPECIAL_2;
3517                break;
3518            case '.':
3519                type = CHAR_DOT;
3520                break;
3521            case '\'':
3522                type = types[i] = CHAR_STRING;
3523                startLoop = i;
3524                while (command[++i] != '\'') {
3525                    checkRunOver(i, len, startLoop);
3526                }
3527                break;
3528            case '[':
3529                if (database.getMode().squareBracketQuotedNames) {
3530                    // SQL Server alias for "
3531                    command[i] = '"';
3532                    changed = true;
3533                    type = types[i] = CHAR_QUOTED;
3534                    startLoop = i;
3535                    while (command[++i] != ']') {
3536                        checkRunOver(i, len, startLoop);
3537                    }
3538                    command[i] = '"';
3539                } else {
3540                    type = CHAR_SPECIAL_1;
3541                }
3542                break;
3543            case '`':
3544                // MySQL alias for ", but not case sensitive
3545                command[i] = '"';
3546                changed = true;
3547                type = types[i] = CHAR_QUOTED;
3548                startLoop = i;
3549                while (command[++i] != '`') {
3550                    checkRunOver(i, len, startLoop);
3551                    c = command[i];
3552                    command[i] = Character.toUpperCase(c);
3553                }
3554                command[i] = '"';
3555                break;
3556            case '\"':
3557                type = types[i] = CHAR_QUOTED;
3558                startLoop = i;
3559                while (command[++i] != '\"') {
3560                    checkRunOver(i, len, startLoop);
3561                }
3562                break;
3563            case '_':
3564                type = CHAR_NAME;
3565                break;
3566            default:
3567                if (c >= 'a' && c <= 'z') {
3568                    if (identifiersToUpper) {
3569                        command[i] = (char) (c - ('a' - 'A'));
3570                        changed = true;
3571                    }
3572                    type = CHAR_NAME;
3573                } else if (c >= 'A' && c <= 'Z') {
3574                    type = CHAR_NAME;
3575                } else if (c >= '0' && c <= '9') {
3576                    type = CHAR_VALUE;
3577                } else {
3578                    if (c <= ' ' || Character.isSpaceChar(c)) {
3579                        // whitespace
3580                    } else if (Character.isJavaIdentifierPart(c)) {
3581                        type = CHAR_NAME;
3582                        if (identifiersToUpper) {
3583                            char u = Character.toUpperCase(c);
3584                            if (u != c) {
3585                                command[i] = u;
3586                                changed = true;
3587                            }
3588                        }
3589                    } else {
3590                        type = CHAR_SPECIAL_1;
3591                    }
3592                }
3593            }
3594            types[i] = type;
3595            lastType = type;
3596        }
3597        sqlCommandChars = command;
3598        types[len] = CHAR_END;
3599        characterTypes = types;
3600        if (changed) {
3601            sqlCommand = new String(command);
3602        }
3603        parseIndex = 0;
3604    }
3605 
3606    private void checkRunOver(int i, int len, int startLoop) {
3607        if (i >= len) {
3608            parseIndex = startLoop;
3609            throw getSyntaxError();
3610        }
3611    }
3612 
3613    private int getSpecialType(String s) {
3614        char c0 = s.charAt(0);
3615        if (s.length() == 1) {
3616            switch (c0) {
3617            case '?':
3618            case '$':
3619                return PARAMETER;
3620            case '@':
3621                return AT;
3622            case '+':
3623                return PLUS;
3624            case '-':
3625                return MINUS;
3626            case '{':
3627            case '}':
3628            case '*':
3629            case '/':
3630            case '%':
3631            case ';':
3632            case ',':
3633            case ':':
3634            case '[':
3635            case ']':
3636            case '~':
3637                return KEYWORD;
3638            case '(':
3639                return OPEN;
3640            case ')':
3641                return CLOSE;
3642            case '<':
3643                return SMALLER;
3644            case '>':
3645                return BIGGER;
3646            case '=':
3647                return EQUAL;
3648            default:
3649                break;
3650            }
3651        } else if (s.length() == 2) {
3652            switch (c0) {
3653            case ':':
3654                if ("::".equals(s)) {
3655                    return KEYWORD;
3656                } else if (":=".equals(s)) {
3657                    return KEYWORD;
3658                }
3659                break;
3660            case '>':
3661                if (">=".equals(s)) {
3662                    return BIGGER_EQUAL;
3663                }
3664                break;
3665            case '<':
3666                if ("<=".equals(s)) {
3667                    return SMALLER_EQUAL;
3668                } else if ("<>".equals(s)) {
3669                    return NOT_EQUAL;
3670                }
3671                break;
3672            case '!':
3673                if ("!=".equals(s)) {
3674                    return NOT_EQUAL;
3675                } else if ("!~".equals(s)) {
3676                    return KEYWORD;
3677                }
3678                break;
3679            case '|':
3680                if ("||".equals(s)) {
3681                    return STRING_CONCAT;
3682                }
3683                break;
3684            case '&':
3685                if ("&&".equals(s)) {
3686                    return SPATIAL_INTERSECTS;
3687                }
3688                break;
3689            }
3690        }
3691        throw getSyntaxError();
3692    }
3693 
3694    private int getTokenType(String s) {
3695        int len = s.length();
3696        if (len == 0) {
3697            throw getSyntaxError();
3698        }
3699        if (!identifiersToUpper) {
3700            // if not yet converted to uppercase, do it now
3701            s = StringUtils.toUpperEnglish(s);
3702        }
3703        return getSaveTokenType(s, database.getMode().supportOffsetFetch);
3704    }
3705 
3706    private boolean isKeyword(String s) {
3707        if (!identifiersToUpper) {
3708            // if not yet converted to uppercase, do it now
3709            s = StringUtils.toUpperEnglish(s);
3710        }
3711        return isKeyword(s, false);
3712    }
3713 
3714    /**
3715     * Checks if this string is a SQL keyword.
3716     *
3717     * @param s the token to check
3718     * @param supportOffsetFetch if OFFSET and FETCH are keywords
3719     * @return true if it is a keyword
3720     */
3721    public static boolean isKeyword(String s, boolean supportOffsetFetch) {
3722        if (s == null || s.length() == 0) {
3723            return false;
3724        }
3725        return getSaveTokenType(s, supportOffsetFetch) != IDENTIFIER;
3726    }
3727 
3728    private static int getSaveTokenType(String s, boolean supportOffsetFetch) {
3729        switch (s.charAt(0)) {
3730        case 'C':
3731            if (s.equals("CURRENT_TIMESTAMP")) {
3732                return CURRENT_TIMESTAMP;
3733            } else if (s.equals("CURRENT_TIME")) {
3734                return CURRENT_TIME;
3735            } else if (s.equals("CURRENT_DATE")) {
3736                return CURRENT_DATE;
3737            }
3738            return getKeywordOrIdentifier(s, "CROSS", KEYWORD);
3739        case 'D':
3740            return getKeywordOrIdentifier(s, "DISTINCT", KEYWORD);
3741        case 'E':
3742            if ("EXCEPT".equals(s)) {
3743                return KEYWORD;
3744            }
3745            return getKeywordOrIdentifier(s, "EXISTS", KEYWORD);
3746        case 'F':
3747            if ("FROM".equals(s)) {
3748                return KEYWORD;
3749            } else if ("FOR".equals(s)) {
3750                return KEYWORD;
3751            } else if ("FULL".equals(s)) {
3752                return KEYWORD;
3753            } else if (supportOffsetFetch && "FETCH".equals(s)) {
3754                return KEYWORD;
3755            }
3756            return getKeywordOrIdentifier(s, "FALSE", FALSE);
3757        case 'G':
3758            return getKeywordOrIdentifier(s, "GROUP", KEYWORD);
3759        case 'H':
3760            return getKeywordOrIdentifier(s, "HAVING", KEYWORD);
3761        case 'I':
3762            if ("INNER".equals(s)) {
3763                return KEYWORD;
3764            } else if ("INTERSECT".equals(s)) {
3765                return KEYWORD;
3766            }
3767            return getKeywordOrIdentifier(s, "IS", KEYWORD);
3768        case 'J':
3769            return getKeywordOrIdentifier(s, "JOIN", KEYWORD);
3770        case 'L':
3771            if ("LIMIT".equals(s)) {
3772                return KEYWORD;
3773            }
3774            return getKeywordOrIdentifier(s, "LIKE", KEYWORD);
3775        case 'M':
3776            return getKeywordOrIdentifier(s, "MINUS", KEYWORD);
3777        case 'N':
3778            if ("NOT".equals(s)) {
3779                return KEYWORD;
3780            } else if ("NATURAL".equals(s)) {
3781                return KEYWORD;
3782            }
3783            return getKeywordOrIdentifier(s, "NULL", NULL);
3784        case 'O':
3785            if ("ON".equals(s)) {
3786                return KEYWORD;
3787            } else if (supportOffsetFetch && "OFFSET".equals(s)) {
3788                return KEYWORD;
3789            }
3790            return getKeywordOrIdentifier(s, "ORDER", KEYWORD);
3791        case 'P':
3792            return getKeywordOrIdentifier(s, "PRIMARY", KEYWORD);
3793        case 'R':
3794            return getKeywordOrIdentifier(s, "ROWNUM", ROWNUM);
3795        case 'S':
3796            if (s.equals("SYSTIMESTAMP")) {
3797                return CURRENT_TIMESTAMP;
3798            } else if (s.equals("SYSTIME")) {
3799                return CURRENT_TIME;
3800            } else if (s.equals("SYSDATE")) {
3801                return CURRENT_TIMESTAMP;
3802            }
3803            return getKeywordOrIdentifier(s, "SELECT", KEYWORD);
3804        case 'T':
3805            if ("TODAY".equals(s)) {
3806                return CURRENT_DATE;
3807            }
3808            return getKeywordOrIdentifier(s, "TRUE", TRUE);
3809        case 'U':
3810            if ("UNIQUE".equals(s)) {
3811                return KEYWORD;
3812            }
3813            return getKeywordOrIdentifier(s, "UNION", KEYWORD);
3814        case 'W':
3815            if ("WITH".equals(s)) {
3816                return KEYWORD;
3817            }
3818            return getKeywordOrIdentifier(s, "WHERE", KEYWORD);
3819        default:
3820            return IDENTIFIER;
3821        }
3822    }
3823 
3824    private static int getKeywordOrIdentifier(String s1, String s2,
3825            int keywordType) {
3826        if (s1.equals(s2)) {
3827            return keywordType;
3828        }
3829        return IDENTIFIER;
3830    }
3831 
3832    private Column parseColumnForTable(String columnName,
3833            boolean defaultNullable) {
3834        Column column;
3835        boolean isIdentity = false;
3836        if (readIf("IDENTITY") || readIf("BIGSERIAL")) {
3837            column = new Column(columnName, Value.LONG);
3838            column.setOriginalSQL("IDENTITY");
3839            parseAutoIncrement(column);
3840            // PostgreSQL compatibility
3841            if (!database.getMode().serialColumnIsNotPK) {
3842                column.setPrimaryKey(true);
3843            }
3844        } else if (readIf("SERIAL")) {
3845            column = new Column(columnName, Value.INT);
3846            column.setOriginalSQL("SERIAL");
3847            parseAutoIncrement(column);
3848            // PostgreSQL compatibility
3849            if (!database.getMode().serialColumnIsNotPK) {
3850                column.setPrimaryKey(true);
3851            }
3852        } else {
3853            column = parseColumnWithType(columnName);
3854        }
3855        if (readIf("NOT")) {
3856            read("NULL");
3857            column.setNullable(false);
3858        } else if (readIf("NULL")) {
3859            column.setNullable(true);
3860        } else {
3861            // domains may be defined as not nullable
3862            column.setNullable(defaultNullable & column.isNullable());
3863        }
3864        if (readIf("AS")) {
3865            if (isIdentity) {
3866                getSyntaxError();
3867            }
3868            Expression expr = readExpression();
3869            column.setComputedExpression(expr);
3870        } else if (readIf("DEFAULT")) {
3871            Expression defaultExpression = readExpression();
3872            column.setDefaultExpression(session, defaultExpression);
3873        } else if (readIf("GENERATED")) {
3874            if (!readIf("ALWAYS")) {
3875                read("BY");
3876                read("DEFAULT");
3877            }
3878            read("AS");
3879            read("IDENTITY");
3880            long start = 1, increment = 1;
3881            if (readIf("(")) {
3882                read("START");
3883                readIf("WITH");
3884                start = readLong();
3885                readIf(",");
3886                if (readIf("INCREMENT")) {
3887                    readIf("BY");
3888                    increment = readLong();
3889                }
3890                read(")");
3891            }
3892            column.setPrimaryKey(true);
3893            column.setAutoIncrement(true, start, increment);
3894        }
3895        if (readIf("NOT")) {
3896            read("NULL");
3897            column.setNullable(false);
3898        } else {
3899            readIf("NULL");
3900        }
3901        if (readIf("AUTO_INCREMENT") || readIf("BIGSERIAL") || readIf("SERIAL")) {
3902            parseAutoIncrement(column);
3903            if (readIf("NOT")) {
3904                read("NULL");
3905            }
3906        } else if (readIf("IDENTITY")) {
3907            parseAutoIncrement(column);
3908            column.setPrimaryKey(true);
3909            if (readIf("NOT")) {
3910                read("NULL");
3911            }
3912        }
3913        if (readIf("NULL_TO_DEFAULT")) {
3914            column.setConvertNullToDefault(true);
3915        }
3916        if (readIf("SEQUENCE")) {
3917            Sequence sequence = readSequence();
3918            column.setSequence(sequence);
3919        }
3920        if (readIf("SELECTIVITY")) {
3921            int value = readPositiveInt();
3922            column.setSelectivity(value);
3923        }
3924        String comment = readCommentIf();
3925        if (comment != null) {
3926            column.setComment(comment);
3927        }
3928        return column;
3929    }
3930 
3931    private void parseAutoIncrement(Column column) {
3932        long start = 1, increment = 1;
3933        if (readIf("(")) {
3934            start = readLong();
3935            if (readIf(",")) {
3936                increment = readLong();
3937            }
3938            read(")");
3939        }
3940        column.setAutoIncrement(true, start, increment);
3941    }
3942 
3943    private String readCommentIf() {
3944        if (readIf("COMMENT")) {
3945            readIf("IS");
3946            return readString();
3947        }
3948        return null;
3949    }
3950 
3951    private Column parseColumnWithType(String columnName) {
3952        String original = currentToken;
3953        boolean regular = false;
3954        if (readIf("LONG")) {
3955            if (readIf("RAW")) {
3956                original += " RAW";
3957            }
3958        } else if (readIf("DOUBLE")) {
3959            if (readIf("PRECISION")) {
3960                original += " PRECISION";
3961            }
3962        } else if (readIf("CHARACTER")) {
3963            if (readIf("VARYING")) {
3964                original += " VARYING";
3965            }
3966        } else {
3967            regular = true;
3968        }
3969        long precision = -1;
3970        int displaySize = -1;
3971        int scale = -1;
3972        String comment = null;
3973        Column templateColumn = null;
3974        DataType dataType;
3975        if (!identifiersToUpper) {
3976            original = StringUtils.toUpperEnglish(original);
3977        }
3978        UserDataType userDataType = database.findUserDataType(original);
3979        if (userDataType != null) {
3980            templateColumn = userDataType.getColumn();
3981            dataType = DataType.getDataType(templateColumn.getType());
3982            comment = templateColumn.getComment();
3983            original = templateColumn.getOriginalSQL();
3984            precision = templateColumn.getPrecision();
3985            displaySize = templateColumn.getDisplaySize();
3986            scale = templateColumn.getScale();
3987        } else {
3988            dataType = DataType.getTypeByName(original);
3989            if (dataType == null) {
3990                throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1,
3991                        currentToken);
3992            }
3993        }
3994        if (database.getIgnoreCase() && dataType.type == Value.STRING &&
3995                !equalsToken("VARCHAR_CASESENSITIVE", original)) {
3996            original = "VARCHAR_IGNORECASE";
3997            dataType = DataType.getTypeByName(original);
3998        }
3999        if (regular) {
4000            read();
4001        }
4002        precision = precision == -1 ? dataType.defaultPrecision : precision;
4003        displaySize = displaySize == -1 ? dataType.defaultDisplaySize
4004                : displaySize;
4005        scale = scale == -1 ? dataType.defaultScale : scale;
4006        if (dataType.supportsPrecision || dataType.supportsScale) {
4007            if (readIf("(")) {
4008                if (!readIf("MAX")) {
4009                    long p = readLong();
4010                    if (readIf("K")) {
4011                        p *= 1024;
4012                    } else if (readIf("M")) {
4013                        p *= 1024 * 1024;
4014                    } else if (readIf("G")) {
4015                        p *= 1024 * 1024 * 1024;
4016                    }
4017                    if (p > Long.MAX_VALUE) {
4018                        p = Long.MAX_VALUE;
4019                    }
4020                    original += "(" + p;
4021                    // Oracle syntax
4022                    readIf("CHAR");
4023                    if (dataType.supportsScale) {
4024                        if (readIf(",")) {
4025                            scale = readInt();
4026                            original += ", " + scale;
4027                        } else {
4028                            // special case: TIMESTAMP(5) actually means
4029                            // TIMESTAMP(23, 5)
4030                            if (dataType.type == Value.TIMESTAMP) {
4031                                scale = MathUtils.convertLongToInt(p);
4032                                p = precision;
4033                            } else {
4034                                scale = 0;
4035                            }
4036                        }
4037                    }
4038                    precision = p;
4039                    displaySize = MathUtils.convertLongToInt(precision);
4040                    original += ")";
4041                }
4042                read(")");
4043            }
4044        } else if (readIf("(")) {
4045            // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
4046            // Just ignore the precision.
4047            readPositiveInt();
4048            read(")");
4049        }
4050        if (readIf("FOR")) {
4051            read("BIT");
4052            read("DATA");
4053            if (dataType.type == Value.STRING) {
4054                dataType = DataType.getTypeByName("BINARY");
4055            }
4056        }
4057        // MySQL compatibility
4058        readIf("UNSIGNED");
4059        int type = dataType.type;
4060        if (scale > precision) {
4061            throw DbException.get(ErrorCode.INVALID_VALUE_2,
4062                    Integer.toString(scale), "scale (precision = " + precision +
4063                            ")");
4064        }
4065        Column column = new Column(columnName, type, precision, scale,
4066                displaySize);
4067        if (templateColumn != null) {
4068            column.setNullable(templateColumn.isNullable());
4069            column.setDefaultExpression(session,
4070                    templateColumn.getDefaultExpression());
4071            int selectivity = templateColumn.getSelectivity();
4072            if (selectivity != Constants.SELECTIVITY_DEFAULT) {
4073                column.setSelectivity(selectivity);
4074            }
4075            Expression checkConstraint = templateColumn.getCheckConstraint(
4076                    session, columnName);
4077            column.addCheckConstraint(session, checkConstraint);
4078        }
4079        column.setComment(comment);
4080        column.setOriginalSQL(original);
4081        return column;
4082    }
4083 
4084    private Prepared parseCreate() {
4085        boolean orReplace = false;
4086        if (readIf("OR")) {
4087            read("REPLACE");
4088            orReplace = true;
4089        }
4090        boolean force = readIf("FORCE");
4091        if (readIf("VIEW")) {
4092            return parseCreateView(force, orReplace);
4093        } else if (readIf("ALIAS")) {
4094            return parseCreateFunctionAlias(force);
4095        } else if (readIf("SEQUENCE")) {
4096            return parseCreateSequence();
4097        } else if (readIf("USER")) {
4098            return parseCreateUser();
4099        } else if (readIf("TRIGGER")) {
4100            return parseCreateTrigger(force);
4101        } else if (readIf("ROLE")) {
4102            return parseCreateRole();
4103        } else if (readIf("SCHEMA")) {
4104            return parseCreateSchema();
4105        } else if (readIf("CONSTANT")) {
4106            return parseCreateConstant();
4107        } else if (readIf("DOMAIN")) {
4108            return parseCreateUserDataType();
4109        } else if (readIf("TYPE")) {
4110            return parseCreateUserDataType();
4111        } else if (readIf("DATATYPE")) {
4112            return parseCreateUserDataType();
4113        } else if (readIf("AGGREGATE")) {
4114            return parseCreateAggregate(force);
4115        } else if (readIf("LINKED")) {
4116            return parseCreateLinkedTable(false, false, force);
4117        }
4118        // tables or linked tables
4119        boolean memory = false, cached = false;
4120        if (readIf("MEMORY")) {
4121            memory = true;
4122        } else if (readIf("CACHED")) {
4123            cached = true;
4124        }
4125        if (readIf("LOCAL")) {
4126            read("TEMPORARY");
4127            if (readIf("LINKED")) {
4128                return parseCreateLinkedTable(true, false, force);
4129            }
4130            read("TABLE");
4131            return parseCreateTable(true, false, cached);
4132        } else if (readIf("GLOBAL")) {
4133            read("TEMPORARY");
4134            if (readIf("LINKED")) {
4135                return parseCreateLinkedTable(true, true, force);
4136            }
4137            read("TABLE");
4138            return parseCreateTable(true, true, cached);
4139        } else if (readIf("TEMP") || readIf("TEMPORARY")) {
4140            if (readIf("LINKED")) {
4141                return parseCreateLinkedTable(true, true, force);
4142            }
4143            read("TABLE");
4144            return parseCreateTable(true, true, cached);
4145        } else if (readIf("TABLE")) {
4146            if (!cached && !memory) {
4147                cached = database.getDefaultTableType() == Table.TYPE_CACHED;
4148            }
4149            return parseCreateTable(false, false, cached);
4150        } else {
4151            boolean hash = false, primaryKey = false;
4152            boolean unique = false, spatial = false;
4153            String indexName = null;
4154            Schema oldSchema = null;
4155            boolean ifNotExists = false;
4156            if (readIf("PRIMARY")) {
4157                read("KEY");
4158                if (readIf("HASH")) {
4159                    hash = true;
4160                }
4161                primaryKey = true;
4162                if (!isToken("ON")) {
4163                    ifNotExists = readIfNoExists();
4164                    indexName = readIdentifierWithSchema(null);
4165                    oldSchema = getSchema();
4166                }
4167            } else {
4168                if (readIf("UNIQUE")) {
4169                    unique = true;
4170                }
4171                if (readIf("HASH")) {
4172                    hash = true;
4173                }
4174                if (readIf("SPATIAL")) {
4175                    spatial = true;
4176                }
4177                if (readIf("INDEX")) {
4178                    if (!isToken("ON")) {
4179                        ifNotExists = readIfNoExists();
4180                        indexName = readIdentifierWithSchema(null);
4181                        oldSchema = getSchema();
4182                    }
4183                } else {
4184                    throw getSyntaxError();
4185                }
4186            }
4187            read("ON");
4188            String tableName = readIdentifierWithSchema();
4189            checkSchema(oldSchema);
4190            CreateIndex command = new CreateIndex(session, getSchema());
4191            command.setIfNotExists(ifNotExists);
4192            command.setHash(hash);
4193            command.setSpatial(spatial);
4194            command.setPrimaryKey(primaryKey);
4195            command.setTableName(tableName);
4196            command.setUnique(unique);
4197            command.setIndexName(indexName);
4198            command.setComment(readCommentIf());
4199            read("(");
4200            command.setIndexColumns(parseIndexColumnList());
4201            return command;
4202        }
4203    }
4204 
4205    /**
4206     * @return true if we expect to see a TABLE clause
4207     */
4208    private boolean addRoleOrRight(GrantRevoke command) {
4209        if (readIf("SELECT")) {
4210            command.addRight(Right.SELECT);
4211            return true;
4212        } else if (readIf("DELETE")) {
4213            command.addRight(Right.DELETE);
4214            return true;
4215        } else if (readIf("INSERT")) {
4216            command.addRight(Right.INSERT);
4217            return true;
4218        } else if (readIf("UPDATE")) {
4219            command.addRight(Right.UPDATE);
4220            return true;
4221        } else if (readIf("ALL")) {
4222            command.addRight(Right.ALL);
4223            return true;
4224        } else if (readIf("ALTER")) {
4225            read("ANY");
4226            read("SCHEMA");
4227            command.addRight(Right.ALTER_ANY_SCHEMA);
4228            command.addTable(null);
4229            return false;
4230        } else if (readIf("CONNECT")) {
4231            // ignore this right
4232            return true;
4233        } else if (readIf("RESOURCE")) {
4234            // ignore this right
4235            return true;
4236        } else {
4237            command.addRoleName(readUniqueIdentifier());
4238            return false;
4239        }
4240    }
4241 
4242    private GrantRevoke parseGrantRevoke(int operationType) {
4243        GrantRevoke command = new GrantRevoke(session);
4244        command.setOperationType(operationType);
4245        boolean tableClauseExpected = addRoleOrRight(command);
4246        while (readIf(",")) {
4247            addRoleOrRight(command);
4248            if (command.isRightMode() && command.isRoleMode()) {
4249                throw DbException
4250                        .get(ErrorCode.ROLES_AND_RIGHT_CANNOT_BE_MIXED);
4251            }
4252        }
4253        if (tableClauseExpected) {
4254            if (readIf("ON")) {
4255                do {
4256                    Table table = readTableOrView();
4257                    command.addTable(table);
4258                } while (readIf(","));
4259            }
4260        }
4261        if (operationType == CommandInterface.GRANT) {
4262            read("TO");
4263        } else {
4264            read("FROM");
4265        }
4266        command.setGranteeName(readUniqueIdentifier());
4267        return command;
4268    }
4269 
4270    private Select parseValues() {
4271        Select command = new Select(session);
4272        currentSelect = command;
4273        TableFilter filter = parseValuesTable();
4274        ArrayList<Expression> list = New.arrayList();
4275        list.add(new Wildcard(null, null));
4276        command.setExpressions(list);
4277        command.addTableFilter(filter, true);
4278        command.init();
4279        return command;
4280    }
4281 
4282    private TableFilter parseValuesTable() {
4283        Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
4284        TableFunction tf = (TableFunction) Function.getFunction(database,
4285                "TABLE");
4286        ArrayList<Column> columns = New.arrayList();
4287        ArrayList<ArrayList<Expression>> rows = New.arrayList();
4288        do {
4289            int i = 0;
4290            ArrayList<Expression> row = New.arrayList();
4291            boolean multiColumn = readIf("(");
4292            do {
4293                Expression expr = readExpression();
4294                expr = expr.optimize(session);
4295                int type = expr.getType();
4296                long prec;
4297                int scale, displaySize;
4298                Column column;
4299                String columnName = "C" + (i + 1);
4300                if (rows.size() == 0) {
4301                    if (type == Value.UNKNOWN) {
4302                        type = Value.STRING;
4303                    }
4304                    DataType dt = DataType.getDataType(type);
4305                    prec = dt.defaultPrecision;
4306                    scale = dt.defaultScale;
4307                    displaySize = dt.defaultDisplaySize;
4308                    column = new Column(columnName, type, prec, scale,
4309                            displaySize);
4310                    columns.add(column);
4311                }
4312                prec = expr.getPrecision();
4313                scale = expr.getScale();
4314                displaySize = expr.getDisplaySize();
4315                if (i >= columns.size()) {
4316                    throw DbException
4317                            .get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
4318                }
4319                Column c = columns.get(i);
4320                type = Value.getHigherOrder(c.getType(), type);
4321                prec = Math.max(c.getPrecision(), prec);
4322                scale = Math.max(c.getScale(), scale);
4323                displaySize = Math.max(c.getDisplaySize(), displaySize);
4324                column = new Column(columnName, type, prec, scale, displaySize);
4325                columns.set(i, column);
4326                row.add(expr);
4327                i++;
4328            } while (multiColumn && readIf(","));
4329            if (multiColumn) {
4330                read(")");
4331            }
4332            rows.add(row);
4333        } while (readIf(","));
4334        int columnCount = columns.size();
4335        int rowCount = rows.size();
4336        for (int i = 0; i < rowCount; i++) {
4337            if (rows.get(i).size() != columnCount) {
4338                throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
4339            }
4340        }
4341        for (int i = 0; i < columnCount; i++) {
4342            Column c = columns.get(i);
4343            if (c.getType() == Value.UNKNOWN) {
4344                c = new Column(c.getName(), Value.STRING, 0, 0, 0);
4345                columns.set(i, c);
4346            }
4347            Expression[] array = new Expression[rowCount];
4348            for (int j = 0; j < rowCount; j++) {
4349                array[j] = rows.get(j).get(i);
4350            }
4351            ExpressionList list = new ExpressionList(array);
4352            tf.setParameter(i, list);
4353        }
4354        tf.setColumns(columns);
4355        tf.doneWithParameters();
4356        Table table = new FunctionTable(mainSchema, session, tf, tf);
4357        TableFilter filter = new TableFilter(session, table, null,
4358                rightsChecked, currentSelect);
4359        return filter;
4360    }
4361 
4362    private Call parseCall() {
4363        Call command = new Call(session);
4364        currentPrepared = command;
4365        command.setExpression(readExpression());
4366        return command;
4367    }
4368 
4369    private CreateRole parseCreateRole() {
4370        CreateRole command = new CreateRole(session);
4371        command.setIfNotExists(readIfNoExists());
4372        command.setRoleName(readUniqueIdentifier());
4373        return command;
4374    }
4375 
4376    private CreateSchema parseCreateSchema() {
4377        CreateSchema command = new CreateSchema(session);
4378        command.setIfNotExists(readIfNoExists());
4379        command.setSchemaName(readUniqueIdentifier());
4380        if (readIf("AUTHORIZATION")) {
4381            command.setAuthorization(readUniqueIdentifier());
4382        } else {
4383            command.setAuthorization(session.getUser().getName());
4384        }
4385        return command;
4386    }
4387 
4388    private CreateSequence parseCreateSequence() {
4389        boolean ifNotExists = readIfNoExists();
4390        String sequenceName = readIdentifierWithSchema();
4391        CreateSequence command = new CreateSequence(session, getSchema());
4392        command.setIfNotExists(ifNotExists);
4393        command.setSequenceName(sequenceName);
4394        while (true) {
4395            if (readIf("START")) {
4396                readIf("WITH");
4397                command.setStartWith(readExpression());
4398            } else if (readIf("INCREMENT")) {
4399                readIf("BY");
4400                command.setIncrement(readExpression());
4401            } else if (readIf("MINVALUE")) {
4402                command.setMinValue(readExpression());
4403            } else if (readIf("NOMINVALUE")) {
4404                command.setMinValue(null);
4405            } else if (readIf("MAXVALUE")) {
4406                command.setMaxValue(readExpression());
4407            } else if (readIf("NOMAXVALUE")) {
4408                command.setMaxValue(null);
4409            } else if (readIf("CYCLE")) {
4410                command.setCycle(true);
4411            } else if (readIf("NOCYCLE")) {
4412                command.setCycle(false);
4413            } else if (readIf("NO")) {
4414                if (readIf("MINVALUE")) {
4415                    command.setMinValue(null);
4416                } else if (readIf("MAXVALUE")) {
4417                    command.setMaxValue(null);
4418                } else if (readIf("CYCLE")) {
4419                    command.setCycle(false);
4420                } else if (readIf("CACHE")) {
4421                    command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
4422                } else {
4423                    break;
4424                }
4425            } else if (readIf("CACHE")) {
4426                command.setCacheSize(readExpression());
4427            } else if (readIf("NOCACHE")) {
4428                command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
4429            } else if (readIf("BELONGS_TO_TABLE")) {
4430                command.setBelongsToTable(true);
4431            } else {
4432                break;
4433            }
4434        }
4435        return command;
4436    }
4437 
4438    private boolean readIfNoExists() {
4439        if (readIf("IF")) {
4440            read("NOT");
4441            read("EXISTS");
4442            return true;
4443        }
4444        return false;
4445    }
4446 
4447    private CreateConstant parseCreateConstant() {
4448        boolean ifNotExists = readIfNoExists();
4449        String constantName = readIdentifierWithSchema();
4450        Schema schema = getSchema();
4451        if (isKeyword(constantName)) {
4452            throw DbException.get(ErrorCode.CONSTANT_ALREADY_EXISTS_1,
4453                    constantName);
4454        }
4455        read("VALUE");
4456        Expression expr = readExpression();
4457        CreateConstant command = new CreateConstant(session, schema);
4458        command.setConstantName(constantName);
4459        command.setExpression(expr);
4460        command.setIfNotExists(ifNotExists);
4461        return command;
4462    }
4463 
4464    private CreateAggregate parseCreateAggregate(boolean force) {
4465        boolean ifNotExists = readIfNoExists();
4466        CreateAggregate command = new CreateAggregate(session);
4467        command.setForce(force);
4468        String name = readIdentifierWithSchema();
4469        if (isKeyword(name) || Function.getFunction(database, name) != null ||
4470                getAggregateType(name) >= 0) {
4471            throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1,
4472                    name);
4473        }
4474        command.setName(name);
4475        command.setSchema(getSchema());
4476        command.setIfNotExists(ifNotExists);
4477        read("FOR");
4478        command.setJavaClassMethod(readUniqueIdentifier());
4479        return command;
4480    }
4481 
4482    private CreateUserDataType parseCreateUserDataType() {
4483        boolean ifNotExists = readIfNoExists();
4484        CreateUserDataType command = new CreateUserDataType(session);
4485        command.setTypeName(readUniqueIdentifier());
4486        read("AS");
4487        Column col = parseColumnForTable("VALUE", true);
4488        if (readIf("CHECK")) {
4489            Expression expr = readExpression();
4490            col.addCheckConstraint(session, expr);
4491        }
4492        col.rename(null);
4493        command.setColumn(col);
4494        command.setIfNotExists(ifNotExists);
4495        return command;
4496    }
4497 
4498    private CreateTrigger parseCreateTrigger(boolean force) {
4499        boolean ifNotExists = readIfNoExists();
4500        String triggerName = readIdentifierWithSchema(null);
4501        Schema schema = getSchema();
4502        boolean insteadOf, isBefore;
4503        if (readIf("INSTEAD")) {
4504            read("OF");
4505            isBefore = true;
4506            insteadOf = true;
4507        } else if (readIf("BEFORE")) {
4508            insteadOf = false;
4509            isBefore = true;
4510        } else {
4511            read("AFTER");
4512            insteadOf = false;
4513            isBefore = false;
4514        }
4515        int typeMask = 0;
4516        boolean onRollback = false;
4517        do {
4518            if (readIf("INSERT")) {
4519                typeMask |= Trigger.INSERT;
4520            } else if (readIf("UPDATE")) {
4521                typeMask |= Trigger.UPDATE;
4522            } else if (readIf("DELETE")) {
4523                typeMask |= Trigger.DELETE;
4524            } else if (readIf("SELECT")) {
4525                typeMask |= Trigger.SELECT;
4526            } else if (readIf("ROLLBACK")) {
4527                onRollback = true;
4528            } else {
4529                throw getSyntaxError();
4530            }
4531        } while (readIf(","));
4532        read("ON");
4533        String tableName = readIdentifierWithSchema();
4534        checkSchema(schema);
4535        CreateTrigger command = new CreateTrigger(session, getSchema());
4536        command.setForce(force);
4537        command.setTriggerName(triggerName);
4538        command.setIfNotExists(ifNotExists);
4539        command.setInsteadOf(insteadOf);
4540        command.setBefore(isBefore);
4541        command.setOnRollback(onRollback);
4542        command.setTypeMask(typeMask);
4543        command.setTableName(tableName);
4544        if (readIf("FOR")) {
4545            read("EACH");
4546            read("ROW");
4547            command.setRowBased(true);
4548        } else {
4549            command.setRowBased(false);
4550        }
4551        if (readIf("QUEUE")) {
4552            command.setQueueSize(readPositiveInt());
4553        }
4554        command.setNoWait(readIf("NOWAIT"));
4555        if (readIf("AS")) {
4556            command.setTriggerSource(readString());
4557        } else {
4558            read("CALL");
4559            command.setTriggerClassName(readUniqueIdentifier());
4560        }
4561        return command;
4562    }
4563 
4564    private CreateUser parseCreateUser() {
4565        CreateUser command = new CreateUser(session);
4566        command.setIfNotExists(readIfNoExists());
4567        command.setUserName(readUniqueIdentifier());
4568        command.setComment(readCommentIf());
4569        if (readIf("PASSWORD")) {
4570            command.setPassword(readExpression());
4571        } else if (readIf("SALT")) {
4572            command.setSalt(readExpression());
4573            read("HASH");
4574            command.setHash(readExpression());
4575        } else if (readIf("IDENTIFIED")) {
4576            read("BY");
4577            // uppercase if not quoted
4578            command.setPassword(ValueExpression.get(ValueString
4579                    .get(readColumnIdentifier())));
4580        } else {
4581            throw getSyntaxError();
4582        }
4583        if (readIf("ADMIN")) {
4584            command.setAdmin(true);
4585        }
4586        return command;
4587    }
4588 
4589    private CreateFunctionAlias parseCreateFunctionAlias(boolean force) {
4590        boolean ifNotExists = readIfNoExists();
4591        String aliasName = readIdentifierWithSchema();
4592        if (isKeyword(aliasName) ||
4593                Function.getFunction(database, aliasName) != null ||
4594                getAggregateType(aliasName) >= 0) {
4595            throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1,
4596                    aliasName);
4597        }
4598        CreateFunctionAlias command = new CreateFunctionAlias(session,
4599                getSchema());
4600        command.setForce(force);
4601        command.setAliasName(aliasName);
4602        command.setIfNotExists(ifNotExists);
4603        command.setDeterministic(readIf("DETERMINISTIC"));
4604        command.setBufferResultSetToLocalTemp(!readIf("NOBUFFER"));
4605        if (readIf("AS")) {
4606            command.setSource(readString());
4607        } else {
4608            read("FOR");
4609            command.setJavaClassMethod(readUniqueIdentifier());
4610        }
4611        return command;
4612    }
4613 
4614    private Query parseWith() {
4615        readIf("RECURSIVE");
4616        String tempViewName = readIdentifierWithSchema();
4617        Schema schema = getSchema();
4618        Table recursiveTable;
4619        read("(");
4620        ArrayList<Column> columns = New.arrayList();
4621        String[] cols = parseColumnList();
4622        for (String c : cols) {
4623            columns.add(new Column(c, Value.STRING));
4624        }
4625        Table old = session.findLocalTempTable(tempViewName);
4626        if (old != null) {
4627            if (!(old instanceof TableView)) {
4628                throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
4629                        tempViewName);
4630            }
4631            TableView tv = (TableView) old;
4632            if (!tv.isTableExpression()) {
4633                throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
4634                        tempViewName);
4635            }
4636            session.removeLocalTempTable(old);
4637        }
4638        CreateTableData data = new CreateTableData();
4639        data.id = database.allocateObjectId();
4640        data.columns = columns;
4641        data.tableName = tempViewName;
4642        data.temporary = true;
4643        data.persistData = true;
4644        data.persistIndexes = false;
4645        data.create = true;
4646        data.session = session;
4647        recursiveTable = schema.createTable(data);
4648        session.addLocalTempTable(recursiveTable);
4649        String querySQL;
4650        try {
4651            read("AS");
4652            read("(");
4653            Query withQuery = parseSelect();
4654            read(")");
4655            withQuery.prepare();
4656            querySQL = StringUtils.fromCacheOrNew(withQuery.getPlanSQL());
4657        } finally {
4658            session.removeLocalTempTable(recursiveTable);
4659        }
4660        int id = database.allocateObjectId();
4661        TableView view = new TableView(schema, id, tempViewName, querySQL,
4662                null, cols, session, true);
4663        view.setTableExpression(true);
4664        view.setTemporary(true);
4665        session.addLocalTempTable(view);
4666        view.setOnCommitDrop(true);
4667        Query q = parseSelect();
4668        q.setPrepareAlways(true);
4669        return q;
4670    }
4671 
4672    private CreateView parseCreateView(boolean force, boolean orReplace) {
4673        boolean ifNotExists = readIfNoExists();
4674        String viewName = readIdentifierWithSchema();
4675        CreateView command = new CreateView(session, getSchema());
4676        this.createView = command;
4677        command.setViewName(viewName);
4678        command.setIfNotExists(ifNotExists);
4679        command.setComment(readCommentIf());
4680        command.setOrReplace(orReplace);
4681        command.setForce(force);
4682        if (readIf("(")) {
4683            String[] cols = parseColumnList();
4684            command.setColumnNames(cols);
4685        }
4686        String select = StringUtils.fromCacheOrNew(sqlCommand
4687                .substring(parseIndex));
4688        read("AS");
4689        try {
4690            Query query = parseSelect();
4691            query.prepare();
4692            command.setSelect(query);
4693        } catch (DbException e) {
4694            if (force) {
4695                command.setSelectSQL(select);
4696                while (currentTokenType != END) {
4697                    read();
4698                }
4699            } else {
4700                throw e;
4701            }
4702        }
4703        return command;
4704    }
4705 
4706    private TransactionCommand parseCheckpoint() {
4707        TransactionCommand command;
4708        if (readIf("SYNC")) {
4709            command = new TransactionCommand(session,
4710                    CommandInterface.CHECKPOINT_SYNC);
4711        } else {
4712            command = new TransactionCommand(session,
4713                    CommandInterface.CHECKPOINT);
4714        }
4715        return command;
4716    }
4717 
4718    private Prepared parseAlter() {
4719        if (readIf("TABLE")) {
4720            return parseAlterTable();
4721        } else if (readIf("USER")) {
4722            return parseAlterUser();
4723        } else if (readIf("INDEX")) {
4724            return parseAlterIndex();
4725        } else if (readIf("SCHEMA")) {
4726            return parseAlterSchema();
4727        } else if (readIf("SEQUENCE")) {
4728            return parseAlterSequence();
4729        } else if (readIf("VIEW")) {
4730            return parseAlterView();
4731        }
4732        throw getSyntaxError();
4733    }
4734 
4735    private void checkSchema(Schema old) {
4736        if (old != null && getSchema() != old) {
4737            throw DbException.get(ErrorCode.SCHEMA_NAME_MUST_MATCH);
4738        }
4739    }
4740 
4741    private AlterIndexRename parseAlterIndex() {
4742        String indexName = readIdentifierWithSchema();
4743        Schema old = getSchema();
4744        AlterIndexRename command = new AlterIndexRename(session);
4745        command.setOldIndex(getSchema().getIndex(indexName));
4746        read("RENAME");
4747        read("TO");
4748        String newName = readIdentifierWithSchema(old.getName());
4749        checkSchema(old);
4750        command.setNewName(newName);
4751        return command;
4752    }
4753 
4754    private AlterView parseAlterView() {
4755        AlterView command = new AlterView(session);
4756        String viewName = readIdentifierWithSchema();
4757        Table tableView = getSchema().findTableOrView(session, viewName);
4758        if (!(tableView instanceof TableView)) {
4759            throw DbException.get(ErrorCode.VIEW_NOT_FOUND_1, viewName);
4760        }
4761        TableView view = (TableView) tableView;
4762        command.setView(view);
4763        read("RECOMPILE");
4764        return command;
4765    }
4766 
4767    private AlterSchemaRename parseAlterSchema() {
4768        String schemaName = readIdentifierWithSchema();
4769        Schema old = getSchema();
4770        AlterSchemaRename command = new AlterSchemaRename(session);
4771        command.setOldSchema(getSchema(schemaName));
4772        read("RENAME");
4773        read("TO");
4774        String newName = readIdentifierWithSchema(old.getName());
4775        checkSchema(old);
4776        command.setNewName(newName);
4777        return command;
4778    }
4779 
4780    private AlterSequence parseAlterSequence() {
4781        String sequenceName = readIdentifierWithSchema();
4782        Sequence sequence = getSchema().getSequence(sequenceName);
4783        AlterSequence command = new AlterSequence(session, sequence.getSchema());
4784        command.setSequence(sequence);
4785        while (true) {
4786            if (readIf("RESTART")) {
4787                read("WITH");
4788                command.setStartWith(readExpression());
4789            } else if (readIf("INCREMENT")) {
4790                read("BY");
4791                command.setIncrement(readExpression());
4792            } else if (readIf("MINVALUE")) {
4793                command.setMinValue(readExpression());
4794            } else if (readIf("NOMINVALUE")) {
4795                command.setMinValue(null);
4796            } else if (readIf("MAXVALUE")) {
4797                command.setMaxValue(readExpression());
4798            } else if (readIf("NOMAXVALUE")) {
4799                command.setMaxValue(null);
4800            } else if (readIf("CYCLE")) {
4801                command.setCycle(true);
4802            } else if (readIf("NOCYCLE")) {
4803                command.setCycle(false);
4804            } else if (readIf("NO")) {
4805                if (readIf("MINVALUE")) {
4806                    command.setMinValue(null);
4807                } else if (readIf("MAXVALUE")) {
4808                    command.setMaxValue(null);
4809                } else if (readIf("CYCLE")) {
4810                    command.setCycle(false);
4811                } else if (readIf("CACHE")) {
4812                    command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
4813                } else {
4814                    break;
4815                }
4816            } else if (readIf("CACHE")) {
4817                command.setCacheSize(readExpression());
4818            } else if (readIf("NOCACHE")) {
4819                command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
4820            } else {
4821                break;
4822            }
4823        }
4824        return command;
4825    }
4826 
4827    private AlterUser parseAlterUser() {
4828        String userName = readUniqueIdentifier();
4829        if (readIf("SET")) {
4830            AlterUser command = new AlterUser(session);
4831            command.setType(CommandInterface.ALTER_USER_SET_PASSWORD);
4832            command.setUser(database.getUser(userName));
4833            if (readIf("PASSWORD")) {
4834                command.setPassword(readExpression());
4835            } else if (readIf("SALT")) {
4836                command.setSalt(readExpression());
4837                read("HASH");
4838                command.setHash(readExpression());
4839            } else {
4840                throw getSyntaxError();
4841            }
4842            return command;
4843        } else if (readIf("RENAME")) {
4844            read("TO");
4845            AlterUser command = new AlterUser(session);
4846            command.setType(CommandInterface.ALTER_USER_RENAME);
4847            command.setUser(database.getUser(userName));
4848            String newName = readUniqueIdentifier();
4849            command.setNewName(newName);
4850            return command;
4851        } else if (readIf("ADMIN")) {
4852            AlterUser command = new AlterUser(session);
4853            command.setType(CommandInterface.ALTER_USER_ADMIN);
4854            User user = database.getUser(userName);
4855            command.setUser(user);
4856            if (readIf("TRUE")) {
4857                command.setAdmin(true);
4858            } else if (readIf("FALSE")) {
4859                command.setAdmin(false);
4860            } else {
4861                throw getSyntaxError();
4862            }
4863            return command;
4864        }
4865        throw getSyntaxError();
4866    }
4867 
4868    private void readIfEqualOrTo() {
4869        if (!readIf("=")) {
4870            readIf("TO");
4871        }
4872    }
4873 
4874    private Prepared parseSet() {
4875        if (readIf("@")) {
4876            Set command = new Set(session, SetTypes.VARIABLE);
4877            command.setString(readAliasIdentifier());
4878            readIfEqualOrTo();
4879            command.setExpression(readExpression());
4880            return command;
4881        } else if (readIf("AUTOCOMMIT")) {
4882            readIfEqualOrTo();
4883            boolean value = readBooleanSetting();
4884            int setting = value ? CommandInterface.SET_AUTOCOMMIT_TRUE
4885                    : CommandInterface.SET_AUTOCOMMIT_FALSE;
4886            return new TransactionCommand(session, setting);
4887        } else if (readIf("MVCC")) {
4888            readIfEqualOrTo();
4889            boolean value = readBooleanSetting();
4890            Set command = new Set(session, SetTypes.MVCC);
4891            command.setInt(value ? 1 : 0);
4892            return command;
4893        } else if (readIf("EXCLUSIVE")) {
4894            readIfEqualOrTo();
4895            Set command = new Set(session, SetTypes.EXCLUSIVE);
4896            command.setExpression(readExpression());
4897            return command;
4898        } else if (readIf("IGNORECASE")) {
4899            readIfEqualOrTo();
4900            boolean value = readBooleanSetting();
4901            Set command = new Set(session, SetTypes.IGNORECASE);
4902            command.setInt(value ? 1 : 0);
4903            return command;
4904        } else if (readIf("PASSWORD")) {
4905            readIfEqualOrTo();
4906            AlterUser command = new AlterUser(session);
4907            command.setType(CommandInterface.ALTER_USER_SET_PASSWORD);
4908            command.setUser(session.getUser());
4909            command.setPassword(readExpression());
4910            return command;
4911        } else if (readIf("SALT")) {
4912            readIfEqualOrTo();
4913            AlterUser command = new AlterUser(session);
4914            command.setType(CommandInterface.ALTER_USER_SET_PASSWORD);
4915            command.setUser(session.getUser());
4916            command.setSalt(readExpression());
4917            read("HASH");
4918            command.setHash(readExpression());
4919            return command;
4920        } else if (readIf("MODE")) {
4921            readIfEqualOrTo();
4922            Set command = new Set(session, SetTypes.MODE);
4923            command.setString(readAliasIdentifier());
4924            return command;
4925        } else if (readIf("COMPRESS_LOB")) {
4926            readIfEqualOrTo();
4927            Set command = new Set(session, SetTypes.COMPRESS_LOB);
4928            if (currentTokenType == VALUE) {
4929                command.setString(readString());
4930            } else {
4931                command.setString(readUniqueIdentifier());
4932            }
4933            return command;
4934        } else if (readIf("DATABASE")) {
4935            readIfEqualOrTo();
4936            read("COLLATION");
4937            return parseSetCollation();
4938        } else if (readIf("COLLATION")) {
4939            readIfEqualOrTo();
4940            return parseSetCollation();
4941        } else if (readIf("BINARY_COLLATION")) {
4942            readIfEqualOrTo();
4943            return parseSetBinaryCollation();
4944        } else if (readIf("CLUSTER")) {
4945            readIfEqualOrTo();
4946            Set command = new Set(session, SetTypes.CLUSTER);
4947            command.setString(readString());
4948            return command;
4949        } else if (readIf("DATABASE_EVENT_LISTENER")) {
4950            readIfEqualOrTo();
4951            Set command = new Set(session, SetTypes.DATABASE_EVENT_LISTENER);
4952            command.setString(readString());
4953            return command;
4954        } else if (readIf("ALLOW_LITERALS")) {
4955            readIfEqualOrTo();
4956            Set command = new Set(session, SetTypes.ALLOW_LITERALS);
4957            if (readIf("NONE")) {
4958                command.setInt(Constants.ALLOW_LITERALS_NONE);
4959            } else if (readIf("ALL")) {
4960                command.setInt(Constants.ALLOW_LITERALS_ALL);
4961            } else if (readIf("NUMBERS")) {
4962                command.setInt(Constants.ALLOW_LITERALS_NUMBERS);
4963            } else {
4964                command.setInt(readPositiveInt());
4965            }
4966            return command;
4967        } else if (readIf("DEFAULT_TABLE_TYPE")) {
4968            readIfEqualOrTo();
4969            Set command = new Set(session, SetTypes.DEFAULT_TABLE_TYPE);
4970            if (readIf("MEMORY")) {
4971                command.setInt(Table.TYPE_MEMORY);
4972            } else if (readIf("CACHED")) {
4973                command.setInt(Table.TYPE_CACHED);
4974            } else {
4975                command.setInt(readPositiveInt());
4976            }
4977            return command;
4978        } else if (readIf("CREATE")) {
4979            readIfEqualOrTo();
4980            // Derby compatibility (CREATE=TRUE in the database URL)
4981            read();
4982            return new NoOperation(session);
4983        } else if (readIf("HSQLDB.DEFAULT_TABLE_TYPE")) {
4984            readIfEqualOrTo();
4985            read();
4986            return new NoOperation(session);
4987        } else if (readIf("PAGE_STORE")) {
4988            readIfEqualOrTo();
4989            read();
4990            return new NoOperation(session);
4991        } else if (readIf("CACHE_TYPE")) {
4992            readIfEqualOrTo();
4993            read();
4994            return new NoOperation(session);
4995        } else if (readIf("FILE_LOCK")) {
4996            readIfEqualOrTo();
4997            read();
4998            return new NoOperation(session);
4999        } else if (readIf("DB_CLOSE_ON_EXIT")) {
5000            readIfEqualOrTo();
5001            read();
5002            return new NoOperation(session);
5003        } else if (readIf("AUTO_SERVER")) {
5004            readIfEqualOrTo();
5005            read();
5006            return new NoOperation(session);
5007        } else if (readIf("AUTO_SERVER_PORT")) {
5008            readIfEqualOrTo();
5009            read();
5010            return new NoOperation(session);
5011        } else if (readIf("AUTO_RECONNECT")) {
5012            readIfEqualOrTo();
5013            read();
5014            return new NoOperation(session);
5015        } else if (readIf("ASSERT")) {
5016            readIfEqualOrTo();
5017            read();
5018            return new NoOperation(session);
5019        } else if (readIf("ACCESS_MODE_DATA")) {
5020            readIfEqualOrTo();
5021            read();
5022            return new NoOperation(session);
5023        } else if (readIf("OPEN_NEW")) {
5024            readIfEqualOrTo();
5025            read();
5026            return new NoOperation(session);
5027        } else if (readIf("JMX")) {
5028            readIfEqualOrTo();
5029            read();
5030            return new NoOperation(session);
5031        } else if (readIf("PAGE_SIZE")) {
5032            readIfEqualOrTo();
5033            read();
5034            return new NoOperation(session);
5035        } else if (readIf("RECOVER")) {
5036            readIfEqualOrTo();
5037            read();
5038            return new NoOperation(session);
5039        } else if (readIf("NAMES")) {
5040            // Quercus PHP MySQL driver compatibility
5041            readIfEqualOrTo();
5042            read();
5043            return new NoOperation(session);
5044        } else if (readIf("SCHEMA")) {
5045            readIfEqualOrTo();
5046            Set command = new Set(session, SetTypes.SCHEMA);
5047            command.setString(readAliasIdentifier());
5048            return command;
5049        } else if (readIf("DATESTYLE")) {
5050            // PostgreSQL compatibility
5051            readIfEqualOrTo();
5052            if (!readIf("ISO")) {
5053                String s = readString();
5054                if (!equalsToken(s, "ISO")) {
5055                    throw getSyntaxError();
5056                }
5057            }
5058            return new NoOperation(session);
5059        } else if (readIf("SEARCH_PATH") ||
5060                readIf(SetTypes.getTypeName(SetTypes.SCHEMA_SEARCH_PATH))) {
5061            readIfEqualOrTo();
5062            Set command = new Set(session, SetTypes.SCHEMA_SEARCH_PATH);
5063            ArrayList<String> list = New.arrayList();
5064            list.add(readAliasIdentifier());
5065            while (readIf(",")) {
5066                list.add(readAliasIdentifier());
5067            }
5068            String[] schemaNames = new String[list.size()];
5069            list.toArray(schemaNames);
5070            command.setStringArray(schemaNames);
5071            return command;
5072        } else if (readIf("JAVA_OBJECT_SERIALIZER")) {
5073            readIfEqualOrTo();
5074            return parseSetJavaObjectSerializer();
5075        } else {
5076            if (isToken("LOGSIZE")) {
5077                // HSQLDB compatibility
5078                currentToken = SetTypes.getTypeName(SetTypes.MAX_LOG_SIZE);
5079            }
5080            if (isToken("FOREIGN_KEY_CHECKS")) {
5081                // MySQL compatibility
5082                currentToken = SetTypes
5083                        .getTypeName(SetTypes.REFERENTIAL_INTEGRITY);
5084            }
5085            int type = SetTypes.getType(currentToken);
5086            if (type < 0) {
5087                throw getSyntaxError();
5088            }
5089            read();
5090            readIfEqualOrTo();
5091            Set command = new Set(session, type);
5092            command.setExpression(readExpression());
5093            return command;
5094        }
5095    }
5096 
5097    private Prepared parseUse() {
5098        readIfEqualOrTo();
5099        Set command = new Set(session, SetTypes.SCHEMA);
5100        command.setString(readAliasIdentifier());
5101        return command;
5102    }
5103 
5104    private Set parseSetCollation() {
5105        Set command = new Set(session, SetTypes.COLLATION);
5106        String name = readAliasIdentifier();
5107        command.setString(name);
5108        if (equalsToken(name, CompareMode.OFF)) {
5109            return command;
5110        }
5111        Collator coll = CompareMode.getCollator(name);
5112        if (coll == null) {
5113            throw DbException.getInvalidValueException("collation", name);
5114        }
5115        if (readIf("STRENGTH")) {
5116            if (readIf("PRIMARY")) {
5117                command.setInt(Collator.PRIMARY);
5118            } else if (readIf("SECONDARY")) {
5119                command.setInt(Collator.SECONDARY);
5120            } else if (readIf("TERTIARY")) {
5121                command.setInt(Collator.TERTIARY);
5122            } else if (readIf("IDENTICAL")) {
5123                command.setInt(Collator.IDENTICAL);
5124            }
5125        } else {
5126            command.setInt(coll.getStrength());
5127        }
5128        return command;
5129    }
5130 
5131    private Set parseSetBinaryCollation() {
5132        Set command = new Set(session, SetTypes.BINARY_COLLATION);
5133        String name = readAliasIdentifier();
5134        command.setString(name);
5135        if (equalsToken(name, CompareMode.UNSIGNED) ||
5136                equalsToken(name, CompareMode.SIGNED)) {
5137            return command;
5138        }
5139        throw DbException.getInvalidValueException("BINARY_COLLATION", name);
5140    }
5141 
5142    private Set parseSetJavaObjectSerializer() {
5143        Set command = new Set(session, SetTypes.JAVA_OBJECT_SERIALIZER);
5144        String name = readString();
5145        command.setString(name);
5146        return command;
5147    }
5148 
5149    private RunScriptCommand parseRunScript() {
5150        RunScriptCommand command = new RunScriptCommand(session);
5151        read("FROM");
5152        command.setFileNameExpr(readExpression());
5153        if (readIf("COMPRESSION")) {
5154            command.setCompressionAlgorithm(readUniqueIdentifier());
5155        }
5156        if (readIf("CIPHER")) {
5157            command.setCipher(readUniqueIdentifier());
5158            if (readIf("PASSWORD")) {
5159                command.setPassword(readExpression());
5160            }
5161        }
5162        if (readIf("CHARSET")) {
5163            command.setCharset(Charset.forName(readString()));
5164        }
5165        return command;
5166    }
5167 
5168    private ScriptCommand parseScript() {
5169        ScriptCommand command = new ScriptCommand(session);
5170        boolean data = true, passwords = true, settings = true;
5171        boolean dropTables = false, simple = false;
5172        if (readIf("SIMPLE")) {
5173            simple = true;
5174        }
5175        if (readIf("NODATA")) {
5176            data = false;
5177        }
5178        if (readIf("NOPASSWORDS")) {
5179            passwords = false;
5180        }
5181        if (readIf("NOSETTINGS")) {
5182            settings = false;
5183        }
5184        if (readIf("DROP")) {
5185            dropTables = true;
5186        }
5187        if (readIf("BLOCKSIZE")) {
5188            long blockSize = readLong();
5189            command.setLobBlockSize(blockSize);
5190        }
5191        command.setData(data);
5192        command.setPasswords(passwords);
5193        command.setSettings(settings);
5194        command.setDrop(dropTables);
5195        command.setSimple(simple);
5196        if (readIf("TO")) {
5197            command.setFileNameExpr(readExpression());
5198            if (readIf("COMPRESSION")) {
5199                command.setCompressionAlgorithm(readUniqueIdentifier());
5200            }
5201            if (readIf("CIPHER")) {
5202                command.setCipher(readUniqueIdentifier());
5203                if (readIf("PASSWORD")) {
5204                    command.setPassword(readExpression());
5205                }
5206            }
5207            if (readIf("CHARSET")) {
5208                command.setCharset(Charset.forName(readString()));
5209            }
5210        }
5211        if (readIf("SCHEMA")) {
5212            HashSet<String> schemaNames = New.hashSet();
5213            do {
5214                schemaNames.add(readUniqueIdentifier());
5215            } while (readIf(","));
5216            command.setSchemaNames(schemaNames);
5217        } else if (readIf("TABLE")) {
5218            ArrayList<Table> tables = New.arrayList();
5219            do {
5220                tables.add(readTableOrView());
5221            } while (readIf(","));
5222            command.setTables(tables);
5223        }
5224        return command;
5225    }
5226 
5227    private Table readTableOrView() {
5228        return readTableOrView(readIdentifierWithSchema(null));
5229    }
5230 
5231    private Table readTableOrView(String tableName) {
5232        // same algorithm than readSequence
5233        if (schemaName != null) {
5234            return getSchema().getTableOrView(session, tableName);
5235        }
5236        Table table = database.getSchema(session.getCurrentSchemaName())
5237                .findTableOrView(session, tableName);
5238        if (table != null) {
5239            return table;
5240        }
5241        String[] schemaNames = session.getSchemaSearchPath();
5242        if (schemaNames != null) {
5243            for (String name : schemaNames) {
5244                Schema s = database.getSchema(name);
5245                table = s.findTableOrView(session, tableName);
5246                if (table != null) {
5247                    return table;
5248                }
5249            }
5250        }
5251        throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableName);
5252    }
5253 
5254    private FunctionAlias findFunctionAlias(String schema, String aliasName) {
5255        FunctionAlias functionAlias = database.getSchema(schema).findFunction(
5256                aliasName);
5257        if (functionAlias != null) {
5258            return functionAlias;
5259        }
5260        String[] schemaNames = session.getSchemaSearchPath();
5261        if (schemaNames != null) {
5262            for (String n : schemaNames) {
5263                functionAlias = database.getSchema(n).findFunction(aliasName);
5264                if (functionAlias != null) {
5265                    return functionAlias;
5266                }
5267            }
5268        }
5269        return null;
5270    }
5271 
5272    private Sequence findSequence(String schema, String sequenceName) {
5273        Sequence sequence = database.getSchema(schema).findSequence(
5274                sequenceName);
5275        if (sequence != null) {
5276            return sequence;
5277        }
5278        String[] schemaNames = session.getSchemaSearchPath();
5279        if (schemaNames != null) {
5280            for (String n : schemaNames) {
5281                sequence = database.getSchema(n).findSequence(sequenceName);
5282                if (sequence != null) {
5283                    return sequence;
5284                }
5285            }
5286        }
5287        return null;
5288    }
5289 
5290    private Sequence readSequence() {
5291        // same algorithm as readTableOrView
5292        String sequenceName = readIdentifierWithSchema(null);
5293        if (schemaName != null) {
5294            return getSchema().getSequence(sequenceName);
5295        }
5296        Sequence sequence = findSequence(session.getCurrentSchemaName(),
5297                sequenceName);
5298        if (sequence != null) {
5299            return sequence;
5300        }
5301        throw DbException.get(ErrorCode.SEQUENCE_NOT_FOUND_1, sequenceName);
5302    }
5303 
5304    private Prepared parseAlterTable() {
5305        Table table = readTableOrView();
5306        if (readIf("ADD")) {
5307            Prepared command = parseAlterTableAddConstraintIf(table.getName(),
5308                    table.getSchema());
5309            if (command != null) {
5310                return command;
5311            }
5312            return parseAlterTableAddColumn(table);
5313        } else if (readIf("SET")) {
5314            read("REFERENTIAL_INTEGRITY");
5315            int type = CommandInterface.ALTER_TABLE_SET_REFERENTIAL_INTEGRITY;
5316            boolean value = readBooleanSetting();
5317            AlterTableSet command = new AlterTableSet(session,
5318                    table.getSchema(), type, value);
5319            command.setTableName(table.getName());
5320            if (readIf("CHECK")) {
5321                command.setCheckExisting(true);
5322            } else if (readIf("NOCHECK")) {
5323                command.setCheckExisting(false);
5324            }
5325            return command;
5326        } else if (readIf("RENAME")) {
5327            read("TO");
5328            String newName = readIdentifierWithSchema(table.getSchema()
5329                    .getName());
5330            checkSchema(table.getSchema());
5331            AlterTableRename command = new AlterTableRename(session,
5332                    getSchema());
5333            command.setOldTable(table);
5334            command.setNewTableName(newName);
5335            command.setHidden(readIf("HIDDEN"));
5336            return command;
5337        } else if (readIf("DROP")) {
5338            if (readIf("CONSTRAINT")) {
5339                boolean ifExists = readIfExists(false);
5340                String constraintName = readIdentifierWithSchema(table
5341                        .getSchema().getName());
5342                ifExists = readIfExists(ifExists);
5343                checkSchema(table.getSchema());
5344                AlterTableDropConstraint command = new AlterTableDropConstraint(
5345                        session, getSchema(), ifExists);
5346                command.setConstraintName(constraintName);
5347                return command;
5348            } else if (readIf("FOREIGN")) {
5349                // MySQL compatibility
5350                read("KEY");
5351                String constraintName = readIdentifierWithSchema(table
5352                        .getSchema().getName());
5353                checkSchema(table.getSchema());
5354                AlterTableDropConstraint command = new AlterTableDropConstraint(
5355                        session, getSchema(), false);
5356                command.setConstraintName(constraintName);
5357                return command;
5358            } else if (readIf("INDEX")) {
5359                // MySQL compatibility
5360                String indexName = readIdentifierWithSchema();
5361                DropIndex command = new DropIndex(session, getSchema());
5362                command.setIndexName(indexName);
5363                return command;
5364            } else if (readIf("PRIMARY")) {
5365                read("KEY");
5366                Index idx = table.getPrimaryKey();
5367                DropIndex command = new DropIndex(session, table.getSchema());
5368                command.setIndexName(idx.getName());
5369                return command;
5370            } else {
5371                readIf("COLUMN");
5372                boolean ifExists = readIfExists(false);
5373                AlterTableAlterColumn command = new AlterTableAlterColumn(
5374                        session, table.getSchema());
5375                command.setType(CommandInterface.ALTER_TABLE_DROP_COLUMN);
5376                String columnName = readColumnIdentifier();
5377                command.setTable(table);
5378                if (ifExists && !table.doesColumnExist(columnName)) {
5379                    return new NoOperation(session);
5380                }
5381                command.setOldColumn(table.getColumn(columnName));
5382                return command;
5383            }
5384        } else if (readIf("CHANGE")) {
5385            // MySQL compatibility
5386            readIf("COLUMN");
5387            String columnName = readColumnIdentifier();
5388            Column column = table.getColumn(columnName);
5389            String newColumnName = readColumnIdentifier();
5390            // new column type ignored. RENAME and MODIFY are
5391            // a single command in MySQL but two different commands in H2.
5392            parseColumnForTable(newColumnName, column.isNullable());
5393            AlterTableRenameColumn command = new AlterTableRenameColumn(session);
5394            command.setTable(table);
5395            command.setColumn(column);
5396            command.setNewColumnName(newColumnName);
5397            return command;
5398        } else if (readIf("MODIFY")) {
5399            // MySQL compatibility
5400            readIf("COLUMN");
5401            String columnName = readColumnIdentifier();
5402            Column column = table.getColumn(columnName);
5403            return parseAlterTableAlterColumnType(table, columnName, column);
5404        } else if (readIf("ALTER")) {
5405            readIf("COLUMN");
5406            String columnName = readColumnIdentifier();
5407            Column column = table.getColumn(columnName);
5408            if (readIf("RENAME")) {
5409                read("TO");
5410                AlterTableRenameColumn command = new AlterTableRenameColumn(
5411                        session);
5412                command.setTable(table);
5413                command.setColumn(column);
5414                String newName = readColumnIdentifier();
5415                command.setNewColumnName(newName);
5416                return command;
5417            } else if (readIf("DROP")) {
5418                // PostgreSQL compatibility
5419                if (readIf("DEFAULT")) {
5420                    AlterTableAlterColumn command = new AlterTableAlterColumn(
5421                            session, table.getSchema());
5422                    command.setTable(table);
5423                    command.setOldColumn(column);
5424                    command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
5425                    command.setDefaultExpression(null);
5426                    return command;
5427                }
5428                read("NOT");
5429                read("NULL");
5430                AlterTableAlterColumn command = new AlterTableAlterColumn(
5431                        session, table.getSchema());
5432                command.setTable(table);
5433                command.setOldColumn(column);
5434                command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
5435                return command;
5436            } else if (readIf("TYPE")) {
5437                // PostgreSQL compatibility
5438                return parseAlterTableAlterColumnType(table, columnName, column);
5439            } else if (readIf("SET")) {
5440                if (readIf("DATA")) {
5441                    // Derby compatibility
5442                    read("TYPE");
5443                    return parseAlterTableAlterColumnType(table, columnName,
5444                            column);
5445                }
5446                AlterTableAlterColumn command = new AlterTableAlterColumn(
5447                        session, table.getSchema());
5448                command.setTable(table);
5449                command.setOldColumn(column);
5450                if (readIf("NULL")) {
5451                    command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
5452                    return command;
5453                } else if (readIf("NOT")) {
5454                    read("NULL");
5455                    command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
5456                    return command;
5457                } else if (readIf("DEFAULT")) {
5458                    Expression defaultExpression = readExpression();
5459                    command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
5460                    command.setDefaultExpression(defaultExpression);
5461                    return command;
5462                }
5463            } else if (readIf("RESTART")) {
5464                readIf("WITH");
5465                Expression start = readExpression();
5466                AlterSequence command = new AlterSequence(session,
5467                        table.getSchema());
5468                command.setColumn(column);
5469                command.setStartWith(start);
5470                return command;
5471            } else if (readIf("SELECTIVITY")) {
5472                AlterTableAlterColumn command = new AlterTableAlterColumn(
5473                        session, table.getSchema());
5474                command.setTable(table);
5475                command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_SELECTIVITY);
5476                command.setOldColumn(column);
5477                command.setSelectivity(readExpression());
5478                return command;
5479            } else {
5480                return parseAlterTableAlterColumnType(table, columnName, column);
5481            }
5482        }
5483        throw getSyntaxError();
5484    }
5485 
5486    private AlterTableAlterColumn parseAlterTableAlterColumnType(Table table,
5487            String columnName, Column column) {
5488        Column newColumn = parseColumnForTable(columnName, column.isNullable());
5489        AlterTableAlterColumn command = new AlterTableAlterColumn(session,
5490                table.getSchema());
5491        command.setTable(table);
5492        command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_CHANGE_TYPE);
5493        command.setOldColumn(column);
5494        command.setNewColumn(newColumn);
5495        return command;
5496    }
5497 
5498    private AlterTableAlterColumn parseAlterTableAddColumn(Table table) {
5499        readIf("COLUMN");
5500        Schema schema = table.getSchema();
5501        AlterTableAlterColumn command = new AlterTableAlterColumn(session,
5502                schema);
5503        command.setType(CommandInterface.ALTER_TABLE_ADD_COLUMN);
5504        command.setTable(table);
5505        ArrayList<Column> columnsToAdd = New.arrayList();
5506        if (readIf("(")) {
5507            command.setIfNotExists(false);
5508            do {
5509                String columnName = readColumnIdentifier();
5510                Column column = parseColumnForTable(columnName, true);
5511                columnsToAdd.add(column);
5512            } while (readIf(","));
5513            read(")");
5514            command.setNewColumns(columnsToAdd);
5515        } else {
5516            boolean ifNotExists = readIfNoExists();
5517            command.setIfNotExists(ifNotExists);
5518            String columnName = readColumnIdentifier();
5519            Column column = parseColumnForTable(columnName, true);
5520            columnsToAdd.add(column);
5521            if (readIf("BEFORE")) {
5522                command.setAddBefore(readColumnIdentifier());
5523            } else if (readIf("AFTER")) {
5524                command.setAddAfter(readColumnIdentifier());
5525            }
5526        }
5527        command.setNewColumns(columnsToAdd);
5528        return command;
5529    }
5530 
5531    private int parseAction() {
5532        Integer result = parseCascadeOrRestrict();
5533        if (result != null) {
5534            return result;
5535        }
5536        if (readIf("NO")) {
5537            read("ACTION");
5538            return ConstraintReferential.RESTRICT;
5539        }
5540        read("SET");
5541        if (readIf("NULL")) {
5542            return ConstraintReferential.SET_NULL;
5543        }
5544        read("DEFAULT");
5545        return ConstraintReferential.SET_DEFAULT;
5546    }
5547 
5548    private Integer parseCascadeOrRestrict() {
5549        if (readIf("CASCADE")) {
5550            return ConstraintReferential.CASCADE;
5551        } else if (readIf("RESTRICT")) {
5552            return ConstraintReferential.RESTRICT;
5553        } else {
5554            return null;
5555        }
5556    }
5557 
5558    private DefineCommand parseAlterTableAddConstraintIf(String tableName,
5559            Schema schema) {
5560        String constraintName = null, comment = null;
5561        boolean ifNotExists = false;
5562        boolean allowIndexDefinition = database.getMode().indexDefinitionInCreateTable;
5563        if (readIf("CONSTRAINT")) {
5564            ifNotExists = readIfNoExists();
5565            constraintName = readIdentifierWithSchema(schema.getName());
5566            checkSchema(schema);
5567            comment = readCommentIf();
5568            allowIndexDefinition = true;
5569        }
5570        if (readIf("PRIMARY")) {
5571            read("KEY");
5572            AlterTableAddConstraint command = new AlterTableAddConstraint(
5573                    session, schema, ifNotExists);
5574            command.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY);
5575            command.setComment(comment);
5576            command.setConstraintName(constraintName);
5577            command.setTableName(tableName);
5578            if (readIf("HASH")) {
5579                command.setPrimaryKeyHash(true);
5580            }
5581            read("(");
5582            command.setIndexColumns(parseIndexColumnList());
5583            if (readIf("INDEX")) {
5584                String indexName = readIdentifierWithSchema();
5585                command.setIndex(getSchema().findIndex(session, indexName));
5586            }
5587            return command;
5588        } else if (allowIndexDefinition && (isToken("INDEX") || isToken("KEY"))) {
5589            // MySQL
5590            // need to read ahead, as it could be a column name
5591            int start = lastParseIndex;
5592            read();
5593            if (DataType.getTypeByName(currentToken) != null) {
5594                // known data type
5595                parseIndex = start;
5596                read();
5597                return null;
5598            }
5599            CreateIndex command = new CreateIndex(session, schema);
5600            command.setComment(comment);
5601            command.setTableName(tableName);
5602            if (!readIf("(")) {
5603                command.setIndexName(readUniqueIdentifier());
5604                read("(");
5605            }
5606            command.setIndexColumns(parseIndexColumnList());
5607            // MySQL compatibility
5608            if (readIf("USING")) {
5609                read("BTREE");
5610            }
5611            return command;
5612        }
5613        AlterTableAddConstraint command;
5614        if (readIf("CHECK")) {
5615            command = new AlterTableAddConstraint(session, schema, ifNotExists);
5616            command.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_CHECK);
5617            command.setCheckExpression(readExpression());
5618        } else if (readIf("UNIQUE")) {
5619            readIf("KEY");
5620            readIf("INDEX");
5621            command = new AlterTableAddConstraint(session, schema, ifNotExists);
5622            command.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_UNIQUE);
5623            if (!readIf("(")) {
5624                constraintName = readUniqueIdentifier();
5625                read("(");
5626            }
5627            command.setIndexColumns(parseIndexColumnList());
5628            if (readIf("INDEX")) {
5629                String indexName = readIdentifierWithSchema();
5630                command.setIndex(getSchema().findIndex(session, indexName));
5631            }
5632            // MySQL compatibility
5633            if (readIf("USING")) {
5634                read("BTREE");
5635            }
5636        } else if (readIf("FOREIGN")) {
5637            command = new AlterTableAddConstraint(session, schema, ifNotExists);
5638            command.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_REFERENTIAL);
5639            read("KEY");
5640            read("(");
5641            command.setIndexColumns(parseIndexColumnList());
5642            if (readIf("INDEX")) {
5643                String indexName = readIdentifierWithSchema();
5644                command.setIndex(schema.findIndex(session, indexName));
5645            }
5646            read("REFERENCES");
5647            parseReferences(command, schema, tableName);
5648        } else {
5649            if (constraintName != null) {
5650                throw getSyntaxError();
5651            }
5652            return null;
5653        }
5654        if (readIf("NOCHECK")) {
5655            command.setCheckExisting(false);
5656        } else {
5657            readIf("CHECK");
5658            command.setCheckExisting(true);
5659        }
5660        command.setTableName(tableName);
5661        command.setConstraintName(constraintName);
5662        command.setComment(comment);
5663        return command;
5664    }
5665 
5666    private void parseReferences(AlterTableAddConstraint command,
5667            Schema schema, String tableName) {
5668        if (readIf("(")) {
5669            command.setRefTableName(schema, tableName);
5670            command.setRefIndexColumns(parseIndexColumnList());
5671        } else {
5672            String refTableName = readIdentifierWithSchema(schema.getName());
5673            command.setRefTableName(getSchema(), refTableName);
5674            if (readIf("(")) {
5675                command.setRefIndexColumns(parseIndexColumnList());
5676            }
5677        }
5678        if (readIf("INDEX")) {
5679            String indexName = readIdentifierWithSchema();
5680            command.setRefIndex(getSchema().findIndex(session, indexName));
5681        }
5682        while (readIf("ON")) {
5683            if (readIf("DELETE")) {
5684                command.setDeleteAction(parseAction());
5685            } else {
5686                read("UPDATE");
5687                command.setUpdateAction(parseAction());
5688            }
5689        }
5690        if (readIf("NOT")) {
5691            read("DEFERRABLE");
5692        } else {
5693            readIf("DEFERRABLE");
5694        }
5695    }
5696 
5697    private CreateLinkedTable parseCreateLinkedTable(boolean temp,
5698            boolean globalTemp, boolean force) {
5699        read("TABLE");
5700        boolean ifNotExists = readIfNoExists();
5701        String tableName = readIdentifierWithSchema();
5702        CreateLinkedTable command = new CreateLinkedTable(session, getSchema());
5703        command.setTemporary(temp);
5704        command.setGlobalTemporary(globalTemp);
5705        command.setForce(force);
5706        command.setIfNotExists(ifNotExists);
5707        command.setTableName(tableName);
5708        command.setComment(readCommentIf());
5709        read("(");
5710        command.setDriver(readString());
5711        read(",");
5712        command.setUrl(readString());
5713        read(",");
5714        command.setUser(readString());
5715        read(",");
5716        command.setPassword(readString());
5717        read(",");
5718        String originalTable = readString();
5719        if (readIf(",")) {
5720            command.setOriginalSchema(originalTable);
5721            originalTable = readString();
5722        }
5723        command.setOriginalTable(originalTable);
5724        read(")");
5725        if (readIf("EMIT")) {
5726            read("UPDATES");
5727            command.setEmitUpdates(true);
5728        } else if (readIf("READONLY")) {
5729            command.setReadOnly(true);
5730        }
5731        return command;
5732    }
5733 
5734    private CreateTable parseCreateTable(boolean temp, boolean globalTemp,
5735            boolean persistIndexes) {
5736        boolean ifNotExists = readIfNoExists();
5737        String tableName = readIdentifierWithSchema();
5738        if (temp && globalTemp && equalsToken("SESSION", schemaName)) {
5739            // support weird syntax: declare global temporary table session.xy
5740            // (...) not logged
5741            schemaName = session.getCurrentSchemaName();
5742            globalTemp = false;
5743        }
5744        Schema schema = getSchema();
5745        CreateTable command = new CreateTable(session, schema);
5746        command.setPersistIndexes(persistIndexes);
5747        command.setTemporary(temp);
5748        command.setGlobalTemporary(globalTemp);
5749        command.setIfNotExists(ifNotExists);
5750        command.setTableName(tableName);
5751        command.setComment(readCommentIf());
5752        if (readIf("(")) {
5753            if (!readIf(")")) {
5754                do {
5755                    DefineCommand c = parseAlterTableAddConstraintIf(tableName,
5756                            schema);
5757                    if (c != null) {
5758                        command.addConstraintCommand(c);
5759                    } else {
5760                        String columnName = readColumnIdentifier();
5761                        Column column = parseColumnForTable(columnName, true);
5762                        if (column.isAutoIncrement() && column.isPrimaryKey()) {
5763                            column.setPrimaryKey(false);
5764                            IndexColumn[] cols = { new IndexColumn() };
5765                            cols[0].columnName = column.getName();
5766                            AlterTableAddConstraint pk = new AlterTableAddConstraint(
5767                                    session, schema, false);
5768                            pk.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY);
5769                            pk.setTableName(tableName);
5770                            pk.setIndexColumns(cols);
5771                            command.addConstraintCommand(pk);
5772                        }
5773                        command.addColumn(column);
5774                        String constraintName = null;
5775                        if (readIf("CONSTRAINT")) {
5776                            constraintName = readColumnIdentifier();
5777                        }
5778                        if (readIf("PRIMARY")) {
5779                            read("KEY");
5780                            boolean hash = readIf("HASH");
5781                            IndexColumn[] cols = { new IndexColumn() };
5782                            cols[0].columnName = column.getName();
5783                            AlterTableAddConstraint pk = new AlterTableAddConstraint(
5784                                    session, schema, false);
5785                            pk.setPrimaryKeyHash(hash);
5786                            pk.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY);
5787                            pk.setTableName(tableName);
5788                            pk.setIndexColumns(cols);
5789                            command.addConstraintCommand(pk);
5790                            if (readIf("AUTO_INCREMENT")) {
5791                                parseAutoIncrement(column);
5792                            }
5793                        } else if (readIf("UNIQUE")) {
5794                            AlterTableAddConstraint unique = new AlterTableAddConstraint(
5795                                    session, schema, false);
5796                            unique.setConstraintName(constraintName);
5797                            unique.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_UNIQUE);
5798                            IndexColumn[] cols = { new IndexColumn() };
5799                            cols[0].columnName = columnName;
5800                            unique.setIndexColumns(cols);
5801                            unique.setTableName(tableName);
5802                            command.addConstraintCommand(unique);
5803                        }
5804                        if (readIf("NOT")) {
5805                            read("NULL");
5806                            column.setNullable(false);
5807                        } else {
5808                            readIf("NULL");
5809                        }
5810                        if (readIf("CHECK")) {
5811                            Expression expr = readExpression();
5812                            column.addCheckConstraint(session, expr);
5813                        }
5814                        if (readIf("REFERENCES")) {
5815                            AlterTableAddConstraint ref = new AlterTableAddConstraint(
5816                                    session, schema, false);
5817                            ref.setConstraintName(constraintName);
5818                            ref.setType(CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_REFERENTIAL);
5819                            IndexColumn[] cols = { new IndexColumn() };
5820                            cols[0].columnName = columnName;
5821                            ref.setIndexColumns(cols);
5822                            ref.setTableName(tableName);
5823                            parseReferences(ref, schema, tableName);
5824                            command.addConstraintCommand(ref);
5825                        }
5826                    }
5827                } while (readIfMore());
5828            }
5829        }
5830        // Allows "COMMENT='comment'" in DDL statements (MySQL syntax)
5831        if (readIf("COMMENT")) {
5832            if (readIf("=")) {
5833                // read the complete string comment, but nothing with it for now
5834                readString();
5835            }
5836        }
5837        if (readIf("ENGINE")) {
5838            if (readIf("=")) {
5839                // map MySQL engine types onto H2 behavior
5840                String tableEngine = readUniqueIdentifier();
5841                if ("InnoDb".equalsIgnoreCase(tableEngine)) {
5842                    // ok
5843                } else if (!"MyISAM".equalsIgnoreCase(tableEngine)) {
5844                    throw DbException.getUnsupportedException(tableEngine);
5845                }
5846            } else {
5847                command.setTableEngine(readUniqueIdentifier());
5848                if (readIf("WITH")) {
5849                    ArrayList<String> tableEngineParams = New.arrayList();
5850                    do {
5851                        tableEngineParams.add(readUniqueIdentifier());
5852                    } while (readIf(","));
5853                    command.setTableEngineParams(tableEngineParams);
5854                }
5855            }
5856        } else if (database.getSettings().defaultTableEngine != null) {
5857            command.setTableEngine(database.getSettings().defaultTableEngine);
5858        }
5859        // MySQL compatibility
5860        if (readIf("AUTO_INCREMENT")) {
5861            read("=");
5862            if (currentTokenType != VALUE ||
5863                    currentValue.getType() != Value.INT) {
5864                throw DbException.getSyntaxError(sqlCommand, parseIndex,
5865                        "integer");
5866            }
5867            read();
5868        }
5869        readIf("DEFAULT");
5870        if (readIf("CHARSET")) {
5871            read("=");
5872            read("UTF8");
5873        }
5874        if (temp) {
5875            if (readIf("ON")) {
5876                read("COMMIT");
5877                if (readIf("DROP")) {
5878                    command.setOnCommitDrop();
5879                } else if (readIf("DELETE")) {
5880                    read("ROWS");
5881                    command.setOnCommitTruncate();
5882                }
5883            } else if (readIf("NOT")) {
5884                if (readIf("PERSISTENT")) {
5885                    command.setPersistData(false);
5886                } else {
5887                    read("LOGGED");
5888                }
5889            }
5890            if (readIf("TRANSACTIONAL")) {
5891                command.setTransactional(true);
5892            }
5893        } else if (!persistIndexes && readIf("NOT")) {
5894            read("PERSISTENT");
5895            command.setPersistData(false);
5896        }
5897        if (readIf("HIDDEN")) {
5898            command.setHidden(true);
5899        }
5900        if (readIf("AS")) {
5901            if (readIf("SORTED")) {
5902                command.setSortedInsertMode(true);
5903            }
5904            command.setQuery(parseSelect());
5905        }
5906        // for MySQL compatibility
5907        if (readIf("ROW_FORMAT")) {
5908            if (readIf("=")) {
5909                readColumnIdentifier();
5910            }
5911        }
5912        return command;
5913    }
5914 
5915    private static int getCompareType(int tokenType) {
5916        switch (tokenType) {
5917        case EQUAL:
5918            return Comparison.EQUAL;
5919        case BIGGER_EQUAL:
5920            return Comparison.BIGGER_EQUAL;
5921        case BIGGER:
5922            return Comparison.BIGGER;
5923        case SMALLER:
5924            return Comparison.SMALLER;
5925        case SMALLER_EQUAL:
5926            return Comparison.SMALLER_EQUAL;
5927        case NOT_EQUAL:
5928            return Comparison.NOT_EQUAL;
5929        case SPATIAL_INTERSECTS:
5930            return Comparison.SPATIAL_INTERSECTS;
5931        default:
5932            return -1;
5933        }
5934    }
5935 
5936    /**
5937     * Add double quotes around an identifier if required.
5938     *
5939     * @param s the identifier
5940     * @return the quoted identifier
5941     */
5942    public static String quoteIdentifier(String s) {
5943        if (s == null || s.length() == 0) {
5944            return "\"\"";
5945        }
5946        char c = s.charAt(0);
5947        // lowercase a-z is quoted as well
5948        if ((!Character.isLetter(c) && c != '_') || Character.isLowerCase(c)) {
5949            return StringUtils.quoteIdentifier(s);
5950        }
5951        for (int i = 1, length = s.length(); i < length; i++) {
5952            c = s.charAt(i);
5953            if ((!Character.isLetterOrDigit(c) && c != '_') ||
5954                    Character.isLowerCase(c)) {
5955                return StringUtils.quoteIdentifier(s);
5956            }
5957        }
5958        if (isKeyword(s, true)) {
5959            return StringUtils.quoteIdentifier(s);
5960        }
5961        return s;
5962    }
5963 
5964    public void setRightsChecked(boolean rightsChecked) {
5965        this.rightsChecked = rightsChecked;
5966    }
5967 
5968    /**
5969     * Parse a SQL code snippet that represents an expression.
5970     *
5971     * @param sql the code snippet
5972     * @return the expression object
5973     */
5974    public Expression parseExpression(String sql) {
5975        parameters = New.arrayList();
5976        initialize(sql);
5977        read();
5978        return readExpression();
5979    }
5980 
5981    /**
5982     * Parse a SQL code snippet that represents a table name.
5983     *
5984     * @param sql the code snippet
5985     * @return the table object
5986     */
5987    public Table parseTableName(String sql) {
5988        parameters = New.arrayList();
5989        initialize(sql);
5990        read();
5991        return readTableOrView();
5992    }
5993}

[all classes][org.h2.command]
EMMA 2.0.5312 (C) Vladimir Roubtsov