KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > command > Parser


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.command;
6
7 import java.math.BigDecimal JavaDoc;
8 import java.math.BigInteger JavaDoc;
9 import java.sql.SQLException JavaDoc;
10 import java.text.Collator JavaDoc;
11
12 import org.h2.command.ddl.AlterIndexRename;
13 import org.h2.command.ddl.AlterSequence;
14 import org.h2.command.ddl.AlterTableAddConstraint;
15 import org.h2.command.ddl.AlterTableAlterColumn;
16 import org.h2.command.ddl.AlterTableDropConstraint;
17 import org.h2.command.ddl.AlterTableRename;
18 import org.h2.command.ddl.AlterTableRenameColumn;
19 import org.h2.command.ddl.AlterUser;
20 import org.h2.command.ddl.AlterView;
21 import org.h2.command.ddl.Analyze;
22 import org.h2.command.ddl.CreateConstant;
23 import org.h2.command.ddl.CreateFunctionAlias;
24 import org.h2.command.ddl.CreateIndex;
25 import org.h2.command.ddl.CreateLinkedTable;
26 import org.h2.command.ddl.CreateRole;
27 import org.h2.command.ddl.CreateSchema;
28 import org.h2.command.ddl.CreateSequence;
29 import org.h2.command.ddl.CreateTable;
30 import org.h2.command.ddl.CreateTrigger;
31 import org.h2.command.ddl.CreateUser;
32 import org.h2.command.ddl.CreateUserDataType;
33 import org.h2.command.ddl.CreateView;
34 import org.h2.command.ddl.DropConstant;
35 import org.h2.command.ddl.DropDatabase;
36 import org.h2.command.ddl.DropFunctionAlias;
37 import org.h2.command.ddl.DropIndex;
38 import org.h2.command.ddl.DropRole;
39 import org.h2.command.ddl.DropSchema;
40 import org.h2.command.ddl.DropSequence;
41 import org.h2.command.ddl.DropTable;
42 import org.h2.command.ddl.DropTrigger;
43 import org.h2.command.ddl.DropUser;
44 import org.h2.command.ddl.DropUserDataType;
45 import org.h2.command.ddl.DropView;
46 import org.h2.command.ddl.GrantRevoke;
47 import org.h2.command.ddl.SetComment;
48 import org.h2.command.ddl.TruncateTable;
49 import org.h2.command.dml.Call;
50 import org.h2.command.dml.Delete;
51 import org.h2.command.dml.ExplainPlan;
52 import org.h2.command.dml.Insert;
53 import org.h2.command.dml.Merge;
54 import org.h2.command.dml.NoOperation;
55 import org.h2.command.dml.Query;
56 import org.h2.command.dml.RunScript;
57 import org.h2.command.dml.Script;
58 import org.h2.command.dml.Select;
59 import org.h2.command.dml.SelectOrderBy;
60 import org.h2.command.dml.SelectUnion;
61 import org.h2.command.dml.Set;
62 import org.h2.command.dml.SetTypes;
63 import org.h2.command.dml.TransactionCommand;
64 import org.h2.command.dml.Update;
65 import org.h2.constraint.ConstraintReferential;
66 import org.h2.engine.Constants;
67 import org.h2.engine.Database;
68 import org.h2.engine.DbObject;
69 import org.h2.engine.FunctionAlias;
70 import org.h2.engine.Mode;
71 import org.h2.engine.Right;
72 import org.h2.engine.Session;
73 import org.h2.engine.Setting;
74 import org.h2.engine.User;
75 import org.h2.engine.UserDataType;
76 import org.h2.expression.Aggregate;
77 import org.h2.expression.Alias;
78 import org.h2.expression.CompareLike;
79 import org.h2.expression.Comparison;
80 import org.h2.expression.ConditionAndOr;
81 import org.h2.expression.ConditionExists;
82 import org.h2.expression.ConditionIn;
83 import org.h2.expression.ConditionInSelect;
84 import org.h2.expression.ConditionNot;
85 import org.h2.expression.Expression;
86 import org.h2.expression.ExpressionColumn;
87 import org.h2.expression.ExpressionList;
88 import org.h2.expression.Function;
89 import org.h2.expression.FunctionCall;
90 import org.h2.expression.JavaFunction;
91 import org.h2.expression.Operation;
92 import org.h2.expression.Parameter;
93 import org.h2.expression.Rownum;
94 import org.h2.expression.SequenceValue;
95 import org.h2.expression.Subquery;
96 import org.h2.expression.ValueExpression;
97 import org.h2.expression.Wildcard;
98 import org.h2.index.Index;
99 import org.h2.message.Message;
100 import org.h2.schema.Schema;
101 import org.h2.schema.Sequence;
102 import org.h2.schema.TriggerObject;
103 import org.h2.table.Column;
104 import org.h2.table.FunctionTable;
105 import org.h2.table.RangeTable;
106 import org.h2.table.Table;
107 import org.h2.table.TableFilter;
108 import org.h2.table.TableView;
109 import org.h2.util.ByteUtils;
110 import org.h2.util.ObjectArray;
111 import org.h2.util.StringCache;
112 import org.h2.util.StringUtils;
113 import org.h2.value.CompareMode;
114 import org.h2.value.DataType;
115 import org.h2.value.Value;
116 import org.h2.value.ValueBoolean;
117 import org.h2.value.ValueBytes;
118 import org.h2.value.ValueDate;
119 import org.h2.value.ValueDecimal;
120 import org.h2.value.ValueInt;
121 import org.h2.value.ValueLong;
122 import org.h2.value.ValueString;
123 import org.h2.value.ValueTime;
124 import org.h2.value.ValueTimestamp;
125
126 public class Parser {
127
128     // used during the tokenizer phase
129
private static final int CHAR_END = -1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
130     private static final int CHAR_NAME = 4, CHAR_SPECIAL_1 = 5, CHAR_SPECIAL_2 = 6;
131     private static final int CHAR_STRING = 7, CHAR_DECIMAL = 8;
132
133     // this are token types
134
private static final int KEYWORD = 1, IDENTIFIER = 2, PARAMETER = 3, END = 4, VALUE = 5;
135     private static final int EQUAL = 6, BIGGER_EQUAL = 7, BIGGER = 8;
136     private static final int SMALLER = 9, SMALLER_EQUAL = 10, NOT_EQUAL = 11;
137     private static final int MINUS = 17, PLUS = 18;
138     private static final int STRINGCONCAT = 22;
139     private static final int OPEN = 31, CLOSE = 32, NULL = 34, TRUE = 40, FALSE = 41;
140
141     private static final int CURRENT_TIMESTAMP = 42, CURRENT_DATE = 43, CURRENT_TIME = 44, ROWNUM = 45;
142
143     private int[] characterTypes;
144     private int currentTokenType;
145     private String JavaDoc currentToken;
146     private boolean currentTokenQuoted;
147     private Value currentValue;
148     private String JavaDoc sqlCommand, originalSQL;
149     private char[] sqlCommandChars;
150     private int lastParseIndex;
151     private int parseIndex;
152     private Prepared currentPrepared;
153     private Select currentSelect;
154     private Session session;
155     private Database database;
156     private ObjectArray parameters;
157     private String JavaDoc schemaName;
158     private ObjectArray expected;
159     private boolean rightsChecked;
160     private boolean recompileAlways;
161     private ObjectArray indexedParameterList;
162
163     public Parser(Session session) {
164         this.session = session;
165         database = session.getDatabase();
166     }
167
168     public Prepared prepare(String JavaDoc sql) throws SQLException JavaDoc {
169         try {
170             Prepared p = parse(sql);
171             p.prepare();
172             return p;
173         } catch(Exception JavaDoc e) {
174             throw Message.convert(e);
175         }
176     }
177
178     public Prepared parseOnly(String JavaDoc sql) throws SQLException JavaDoc {
179         try {
180             Prepared p = parse(sql);
181             return p;
182         } catch(Exception JavaDoc e) {
183             throw Message.convert(e);
184         }
185     }
186
187     public Command prepareCommand(String JavaDoc sql) throws SQLException JavaDoc {
188         try {
189             Prepared p = parse(sql);
190             p.prepare();
191             Command c = new CommandContainer(this, p);
192             p.setCommand(c);
193             if (isToken(";")) {
194                 String JavaDoc remaining = originalSQL.substring(parseIndex);
195                 if(remaining.trim().length()!=0) {
196                     CommandList list = new CommandList(this, c, remaining);
197         // list.addCommand(c);
198
// do {
199
// c = parseCommand();
200
// list.addCommand(c);
201
// } while(currentToken.equals(";"));
202
c = list;
203                 }
204             } else if (currentTokenType != END) {
205                 // TODO exception: expected end of command
206
throw getSyntaxError();
207             }
208             return c;
209         } catch(Exception JavaDoc e) {
210             throw Message.addSQL(Message.convert(e), this.originalSQL);
211         }
212     }
213
214     private Prepared parse(String JavaDoc sql) throws SQLException JavaDoc {
215         Prepared p;
216         try {
217             // first, try the fast variant
218
p = parse(sql, false);
219         } catch(SQLException JavaDoc e) {
220             if(e.getErrorCode() == Message.SYNTAX_ERROR_1) {
221                 // now, get the detailed exception
222
p = parse(sql, true);
223             } else {
224                 throw Message.addSQL(e, sql);
225             }
226         }
227         p.setPrepareAlways(recompileAlways);
228         p.setParameterList(parameters);
229         return p;
230     }
231
232     private Prepared parse(String JavaDoc sql, boolean withExpectedList) throws SQLException JavaDoc {
233         initialize(sql);
234         if(withExpectedList) {
235             expected = new ObjectArray();
236         } else {
237             expected = null;
238         }
239         parameters = new ObjectArray();
240         int start = lastParseIndex;
241         currentSelect = null;
242         currentPrepared = null;
243         Prepared c = null;
244         recompileAlways = false;
245         indexedParameterList = null;
246         read();
247          String JavaDoc token = currentToken;
248         if(token.length()==0) {
249             c = new NoOperation(session);
250         } else {
251             char first = token.charAt(0);
252             switch (first) {
253             case '(':
254                 c = parseSelect();
255                 break;
256             case 'A':
257                 if(readIf("ALTER")) {
258                     c = parseAlter();
259                 } else if(readIf("ANALYZE")) {
260                     c = parseAnalyse();
261                 }
262                 break;
263             case 'C':
264                 if (readIf("COMMIT")) {
265                     c = parseCommit();
266                 } else if (readIf("CREATE")) {
267                     c = parseCreate();
268                 } else if (readIf("CALL")) {
269                     c = parserCall();
270                 } else if (readIf("CHECKPOINT")) {
271                     c = parseCheckpoint();
272                 } else if (readIf("COMMENT")) {
273                     c = parseComment();
274                 }
275                 break;
276             case 'D':
277                 if (readIf("DELETE")) {
278                     c = parseDelete();
279                 } else if (readIf("DROP")) {
280                     c = parseDrop();
281                 } else if (readIf("DECLARE")) {
282                     // support for DECLARE GLOBAL TEMPORARY TABLE...
283
c = parseCreate();
284                 }
285                 break;
286             case 'E':
287                 if (readIf("EXPLAIN")) {
288                     c = parseExplain();
289                 }
290                 break;
291             case 'F':
292                 if (isToken("FROM")) {
293                     c = parseSelect();
294                 }
295                 break;
296             case 'G':
297                 if (readIf("GRANT")) {
298                     c = parseGrantRevoke(GrantRevoke.GRANT);
299                 }
300                 break;
301             case 'H':
302                 if(readIf("HELP")) {
303                     c = parseHelp();
304                 }
305                 break;
306             case 'I':
307                 if(readIf("INSERT")) {
308                     c = parseInsert();
309                 }
310                 break;
311             case 'M':
312                 if(readIf("MERGE")) {
313                     c = parseMerge();
314                 }
315                 break;
316             case 'P':
317                 if(readIf("PREPARE")) {
318                     c = parsePrepareCommit();
319                 }
320                 break;
321             case 'R':
322                 if(readIf("ROLLBACK")) {
323                     c = parseRollback();
324                 } else if (readIf("REVOKE")) {
325                     c = parseGrantRevoke(GrantRevoke.REVOKE);
326                 } else if(readIf("RUNSCRIPT")) {
327                     c = parseRunScript();
328                 }
329                 break;
330             case 'S':
331                 if (isToken("SELECT")) {
332                     c = parseSelect();
333                 } else if (readIf("SET")) {
334                     c = parseSet();
335                 } else if (readIf("SAVEPOINT")) {
336                     c = parseSavepoint();
337                 } else if (readIf("SCRIPT")) {
338                     c = parseScript();
339                 } else if (readIf("SHUTDOWN")) {
340                     c = parseShutdown();
341                 }
342                 break;
343             case 'T':
344                 if(readIf("TRUNCATE")) {
345                     c = parseTruncate();
346                 }
347                 break;
348             case 'U':
349                 if(readIf("UPDATE")) {
350                     c = parseUpdate();
351                 }
352                 break;
353             case 'V':
354                 if(readIf("VALUES")) {
355                     c = parserCall();
356                 }
357                 break;
358             default:
359                 // TODO exception: unknown command
360
throw getSyntaxError();
361             }
362             if(indexedParameterList != null) {
363                 for(int i=0; i<indexedParameterList.size(); i++) {
364                     if(indexedParameterList.get(i) == null) {
365                          indexedParameterList.set(i, new Parameter(i));
366                     }
367                 }
368                 parameters = indexedParameterList;
369             }
370             if(readIf("{")) {
371                 do {
372                     int index = (int)readLong() - 1;
373                     if(index < 0 || index >= parameters.size()) {
374                         throw getSyntaxError();
375                     }
376                     Parameter p = (Parameter)parameters.get(index);
377                     if(p == null) {
378                         throw getSyntaxError();
379                     }
380                     read(":");
381                     Expression expr = readExpression();
382                     expr = expr.optimize(session);
383                     p.setValue(expr.getValue(session));
384                     index++;
385                 } while(readIf(","));
386                 read("}");
387                 int len = parameters.size();
388                 for(int i=0; i<len; i++) {
389                     Parameter p = (Parameter)parameters.get(i);
390                     p.checkSet();
391                 }
392                 parameters.clear();
393             }
394         }
395         if(c==null) {
396             //return new ParserInt(session).parse(sql);
397
throw getSyntaxError();
398         }
399         setSQL(c, null, start);
400         return c;
401     }
402
403     private SQLException JavaDoc getSyntaxError() {
404         if(expected == null || expected.size()==0) {
405             return Message.getSyntaxError(sqlCommand, parseIndex);
406         } else {
407             StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
408             for(int i=0; i<expected.size(); i++) {
409                 if(i>0) {
410                     buff.append(", ");
411                 }
412                 buff.append(expected.get(i));
413             }
414             return Message.getSyntaxError(sqlCommand, parseIndex, buff.toString());
415         }
416     }
417
418     private Prepared parseAnalyse() throws SQLException JavaDoc {
419         Analyze command = new Analyze(session);
420         if(readIf("SAMPLE_SIZE")) {
421             command.setTop(getPositiveInt());
422         }
423         return command;
424     }
425
426     private TransactionCommand parseCommit() throws SQLException JavaDoc {
427         TransactionCommand command;
428         if(readIf("TRANSACTION")) {
429             command = new TransactionCommand(session, TransactionCommand.COMMIT_TRANSACTION);
430             command.setTransactionName(readUniqueIdentifier());
431             return command;
432         }
433         command = new TransactionCommand(session, TransactionCommand.COMMIT);
434         readIf("WORK");
435         return command;
436     }
437
438     private TransactionCommand parseShutdown() throws SQLException JavaDoc {
439         int type = TransactionCommand.SHUTDOWN;
440         if(readIf("IMMEDIATELY")) {
441             type = TransactionCommand.SHUTDOWN_IMMEDIATELY;
442         } else if(readIf("COMPACT")) {
443         } else if(readIf("SCRIPT")) {
444         }
445         TransactionCommand command = new TransactionCommand(session, type);
446         return command;
447     }
448
449     private TransactionCommand parseRollback() throws SQLException JavaDoc {
450         TransactionCommand command;
451         if(readIf("TRANSACTION")) {
452             command = new TransactionCommand(session, TransactionCommand.ROLLBACK_TRANSACTION);
453             command.setTransactionName(readUniqueIdentifier());
454             return command;
455         }
456         if(readIf("TO")) {
457             read("SAVEPOINT");
458             command = new TransactionCommand(session, TransactionCommand.ROLLBACK_TO_SAVEPOINT);
459             command.setSavepointName(readUniqueIdentifier());
460         } else {
461             readIf("WORK");
462             command = new TransactionCommand(session, TransactionCommand.ROLLBACK);
463         }
464         return command;
465     }
466
467     private TransactionCommand parsePrepareCommit() throws SQLException JavaDoc {
468         TransactionCommand command = new TransactionCommand(session, TransactionCommand.PREPARE_COMMIT);
469         read("COMMIT");
470         command.setTransactionName(readUniqueIdentifier());
471         return command;
472     }
473
474     private TransactionCommand parseSavepoint() throws SQLException JavaDoc {
475         TransactionCommand command = new TransactionCommand(session, TransactionCommand.SAVEPOINT);
476         command.setSavepointName(readUniqueIdentifier());
477         return command;
478     }
479
480     private Schema getSchema() throws SQLException JavaDoc {
481         if(schemaName == null) {
482             return null;
483         }
484         Schema schema = database.findSchema(schemaName);
485         if(schema == null) {
486             if("SESSION".equals(schemaName)) {
487                 // for local temporary tables
488
schema = database.getSchema(session.getCurrentSchemaName());
489             } else {
490                 throw Message.getSQLException(Message.SCHEMA_NOT_FOUND_1, schemaName);
491             }
492         }
493         return schema;
494     }
495     
496     private Column readTableColumn(TableFilter filter) throws SQLException JavaDoc {
497         String JavaDoc tableAlias = null;
498         String JavaDoc columnName = readColumnIdentifier();
499         if(readIf(".")) {
500             tableAlias = columnName;
501             columnName = readColumnIdentifier();
502         }
503         if (tableAlias != null && !tableAlias.equals(filter.getTableAlias())) {
504             throw Message.getSQLException(Message.TABLE_OR_VIEW_NOT_FOUND_1, tableAlias);
505         }
506         return filter.getTable().getColumn(columnName);
507     }
508
509     private Update parseUpdate() throws SQLException JavaDoc {
510         Update command = new Update(session);
511         currentPrepared = command;
512         int start = lastParseIndex;
513         TableFilter filter = readSimpleTableFilter();
514         command.setTableFilter(filter);
515         read("SET");
516         if(readIf("(")) {
517             ObjectArray columns = new ObjectArray();
518             do {
519                 Column column = readTableColumn(filter);
520                 columns.add(column);
521             } while(readIf(","));
522             read(")");
523             read("=");
524             Expression expression = readExpression();
525             for(int i=0; i<columns.size(); i++) {
526                 Column column = (Column) columns.get(i);
527                 Function f = Function.getFunction(database, "ARRAY_GET");
528                 f.setParameter(0, expression);
529                 f.setParameter(1, ValueExpression.get(ValueInt.get(i+1)));
530                 f.doneWithParameters();
531                 command.setAssignment(column, f);
532             }
533         } else {
534             do {
535                 Column column = readTableColumn(filter);
536                 read("=");
537                 Expression expression = readExpression();
538                 command.setAssignment(column, expression);
539             } while(readIf(","));
540         }
541         if (readIf("WHERE")) {
542             Expression condition = readExpression();
543             command.setCondition(condition);
544         }
545         setSQL(command, "UPDATE", start);
546         return command;
547     }
548     
549     private TableFilter readSimpleTableFilter() throws SQLException JavaDoc {
550         String JavaDoc tableName = readIdentifierWithSchema();
551         Table table = getSchema().getTableOrView(session, tableName);
552         String JavaDoc alias = null;
553         if(readIf("AS")) {
554             alias = readAliasIdentifier();
555         } else if(currentTokenType == IDENTIFIER) {
556             if(!"SET".equals(currentToken)) {
557                 // SET is not a keyword (PostgreSQL supports it as a table name)
558
alias = readAliasIdentifier();
559             }
560         }
561         TableFilter filter = new TableFilter(session, table, alias, rightsChecked);
562         return filter;
563     }
564
565     private Delete parseDelete() throws SQLException JavaDoc {
566         Delete command = new Delete(session);
567         currentPrepared = command;
568         int start = lastParseIndex;
569         readIf("FROM");
570         TableFilter filter = readSimpleTableFilter();
571         command.setTableFilter(filter);
572         if (readIf("WHERE")) {
573             Expression condition = readExpression();
574             command.setCondition(condition);
575         }
576         setSQL(command, "DELETE", start);
577         return command;
578     }
579
580     private String JavaDoc[] parseColumnList(boolean ascDesc) throws SQLException JavaDoc {
581         ObjectArray columns = new ObjectArray();
582         do {
583             String JavaDoc columnName = readColumnIdentifier();
584             columns.add(columnName);
585             if(readIf("ASC")) {
586                 // ignore
587
} else {
588                 readIf("DESC");
589             }
590         } while(readIf(","));
591         read(")");
592         String JavaDoc[] cols = new String JavaDoc[columns.size()];
593         columns.toArray(cols);
594         return cols;
595     }
596
597     private Column[] parseColumnList(Table table) throws SQLException JavaDoc {
598         ObjectArray columns = new ObjectArray();
599         if(!readIf(")")) {
600             do {
601                 Column column = table.getColumn(readColumnIdentifier());
602                 columns.add(column);
603             } while(readIf(","));
604             read(")");
605         }
606         Column[] cols = new Column[columns.size()];
607         columns.toArray(cols);
608         return cols;
609     }
610
611     private Prepared parseHelp() throws SQLException JavaDoc {
612         StringBuffer JavaDoc buff = new StringBuffer JavaDoc("SELECT * FROM INFORMATION_SCHEMA.HELP");
613         int i=0;
614         while (currentTokenType != END) {
615             String JavaDoc s = currentToken;
616             read();
617             if(i==0) {
618                 buff.append(" WHERE ");
619             } else {
620                 buff.append(" AND ");
621             }
622             i++;
623             buff.append("UPPER(TOPIC) LIKE ");
624             buff.append(StringUtils.quoteStringSQL("%"+s+"%"));
625         }
626         Prepared command = session.prepare(buff.toString());
627         return command;
628     }
629
630     private Merge parseMerge() throws SQLException JavaDoc {
631         Merge command = new Merge(session);
632         currentPrepared = command;
633         read("INTO");
634         String JavaDoc tableName = readIdentifierWithSchema();
635         Table table = getSchema().getTableOrView(session, tableName);
636         command.setTable(table);
637         if (readIf("(")) {
638             Column[] columns = parseColumnList(table);
639             command.setColumns(columns);
640         }
641         if(readIf("KEY")) {
642             read("(");
643             Column[] keys = parseColumnList(table);
644             command.setKeys(keys);
645         }
646         if (readIf("VALUES")) {
647             do {
648                 ObjectArray values = new ObjectArray();
649                 read("(");
650                 if(!readIf(")")) {
651                     do {
652                         if(readIf("DEFAULT")) {
653                             values.add(null);
654                         } else {
655                             values.add(readExpression());
656                         }
657                     } while(readIf(","));
658                     read(")");
659                 }
660                 Expression[] expr = new Expression[values.size()];
661                 values.toArray(expr);
662                 command.addRow(expr);
663             } while(readIf(","));
664         } else {
665             command.setQuery(parseQueryWithParams());
666         }
667         return command;
668     }
669
670     private Insert parseInsert() throws SQLException JavaDoc {
671         Insert command = new Insert(session);
672         currentPrepared = command;
673         read("INTO");
674         String JavaDoc tableName = readIdentifierWithSchema();
675         Table table = getSchema().getTableOrView(session, tableName);
676         command.setTable(table);
677         if (readIf("(")) {
678             Column[] columns = parseColumnList(table);
679             command.setColumns(columns);
680         }
681         if(readIf("DEFAULT")) {
682             read("VALUES");
683             Expression[] expr = new Expression[0];
684             command.addRow(expr);
685         } else if (readIf("VALUES")) {
686             do {
687                 ObjectArray values = new ObjectArray();
688                 read("(");
689                 if(!readIf(")")) {
690                     do {
691                         if(readIf("DEFAULT")) {
692                             values.add(null);
693                         } else {
694                             values.add(readExpression());
695                         }
696                     } while(readIf(","));
697                     read(")");
698                 }
699                 Expression[] expr = new Expression[values.size()];
700                 values.toArray(expr);
701                 command.addRow(expr);
702             } while(readIf(","));
703         } else {
704             command.setQuery(parseQueryWithParams());
705         }
706         return command;
707     }
708
709     private TableFilter readTableFilter() throws SQLException JavaDoc {
710         Table table;
711         Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
712         if(readIf("(")) {
713             if(isToken("SELECT") || isToken("FROM")) {
714                 Query query = parseQueryWithParams();
715                 String JavaDoc querySQL = query.getSQL();
716                 table = new TableView(mainSchema, 0, "TEMP_VIEW", querySQL, query.getParameters(), null, session);
717                 read(")");
718             } else {
719                 TableFilter top = readTableFilter();
720                 top = readJoin(top, currentSelect);
721                 read(")");
722                 return top;
723             }
724         } else {
725             String JavaDoc tableName = readIdentifierWithSchema();
726             if(readIf("(")) {
727                 if(tableName.equals(RangeTable.NAME)) {
728                     long min = readLong();
729                     read(",");
730                     long max = readLong();
731                     read(")");
732                     table = new RangeTable(mainSchema, min, max);
733                 } else {
734                     Expression func = readFunction(tableName);
735                     if(!(func instanceof FunctionCall)) {
736                         throw getSyntaxError();
737                     }
738                     table = new FunctionTable(mainSchema, session, (FunctionCall)func);
739                 }
740             } else if(tableName.equals("DUAL")) {
741                 table = new RangeTable(mainSchema, 1, 1);
742             } else {
743                 table = getSchema().getTableOrView(session, tableName);
744             }
745         }
746         String JavaDoc alias = null;
747         if(readIf("AS")) {
748             alias = readAliasIdentifier();
749         } else if(currentTokenType == IDENTIFIER) {
750             // left and right are not keywords (because they are functions as well)
751
if(!isToken("LEFT") && !isToken("RIGHT")) {
752                 alias = readAliasIdentifier();
753             }
754         }
755         TableFilter filter = new TableFilter(session, table, alias, rightsChecked);
756         return filter;
757     }
758
759     private Prepared parseTruncate() throws SQLException JavaDoc {
760         read("TABLE");
761         String JavaDoc tableName = readIdentifierWithSchema();
762         TruncateTable command = new TruncateTable(session, getSchema());
763         command.setTableName(tableName);
764         return command;
765     }
766
767     private boolean readIfExists(boolean ifExists) throws SQLException JavaDoc {
768         if(readIf("IF")) {
769             read("EXISTS");
770             ifExists = true;
771         }
772         return ifExists;
773     }
774
775     private Prepared parseComment() throws SQLException JavaDoc {
776         int type = 0;
777         read("ON");
778         boolean column = false;
779         if(readIf("TABLE") || readIf("VIEW")) {
780             type = DbObject.TABLE_OR_VIEW;
781         } else if(readIf("COLUMN")) {
782             column = true;
783             type = DbObject.TABLE_OR_VIEW;
784         } else if(readIf("CONSTANT")) {
785             type = DbObject.CONSTANT;
786         } else if(readIf("CONSTRAINT")) {
787             type = DbObject.CONSTRAINT;
788         } else if(readIf("ALIAS")) {
789             type = DbObject.FUNCTION_ALIAS;
790         } else if(readIf("INDEX")) {
791             type = DbObject.INDEX;
792         } else if(readIf("ROLE")) {
793             type = DbObject.ROLE;
794         } else if(readIf("SCHEMA")) {
795             type = DbObject.SCHEMA;
796         } else if(readIf("SEQUENCE")) {
797             type = DbObject.SEQUENCE;
798         } else if(readIf("TRIGGER")) {
799             type = DbObject.TRIGGER;
800         } else if(readIf("USER")) {
801             type = DbObject.USER;
802         } else if(readIf("DOMAIN")) {
803             type = DbObject.USER_DATATYPE;
804         } else {
805             throw getSyntaxError();
806         }
807         SetComment command = new SetComment(session);
808         String JavaDoc objectName = readIdentifierWithSchema();
809         if(column) {
810             String JavaDoc columnName = objectName;
811             objectName = schemaName;
812             schemaName = session.getCurrentSchemaName();
813             if(readIf(".")) {
814                 schemaName = objectName;
815                 objectName = columnName;
816                 columnName = readUniqueIdentifier();
817             }
818             command.setColumn(true);
819             command.setColumnName(columnName);
820         }
821         command.setSchemaName(schemaName);
822         command.setObjectName(objectName);
823         command.setObjectType(type);
824         read("IS");
825         command.setCommentExpression(readExpression());
826         return command;
827     }
828
829     private Prepared parseDrop() throws SQLException JavaDoc {
830         if (readIf("TABLE")) {
831             boolean ifExists = readIfExists(false);
832             String JavaDoc tableName = readIdentifierWithSchema();
833             DropTable command = new DropTable(session, getSchema());
834             command.setTableName(tableName);
835             while(readIf(",")) {
836                 tableName = readIdentifierWithSchema();
837                 DropTable next = new DropTable(session, getSchema());
838                 next.setTableName(tableName);
839                 command.addNextDropTable(next);
840             }
841             ifExists = readIfExists(ifExists);
842             command.setIfExists(ifExists);
843             if(readIf("CASCADE")) {
844                 readIf("CONSTRAINTS");
845             }
846             return command;
847         } else if (readIf("INDEX")) {
848             boolean ifExists = readIfExists(false);
849             String JavaDoc indexName = readIdentifierWithSchema();
850             // TODO drop index: how to drop a primary key?
851
DropIndex command = new DropIndex(session, getSchema());
852             command.setIndexName(indexName);
853             ifExists = readIfExists(ifExists);
854             command.setIfExists(ifExists);
855             return command;
856         } else if (readIf("USER")) {
857             boolean ifExists = readIfExists(false);
858             DropUser command = new DropUser(session);
859             command.setUserName(readUniqueIdentifier());
860             ifExists = readIfExists(ifExists);
861             readIf("CASCADE");
862             command.setIfExists(ifExists);
863             return command;
864         } else if(readIf("SEQUENCE")) {
865             boolean ifExists = readIfExists(false);
866             String JavaDoc sequenceName = readIdentifierWithSchema();
867             DropSequence command = new DropSequence(session, getSchema());
868             command.setSequenceName(sequenceName);
869             ifExists = readIfExists(ifExists);
870             command.setIfExists(ifExists);
871             return command;
872         } else if(readIf("CONSTANT")) {
873             boolean ifExists = readIfExists(false);
874             String JavaDoc constantName = readIdentifierWithSchema();
875             DropConstant command = new DropConstant(session, getSchema());
876             command.setConstantName(constantName);
877             ifExists = readIfExists(ifExists);
878             command.setIfExists(ifExists);
879             return command;
880         } else if(readIf("TRIGGER")) {
881             boolean ifExists = readIfExists(false);
882             String JavaDoc triggerName = readIdentifierWithSchema();
883             DropTrigger command = new DropTrigger(session, getSchema());
884             command.setTriggerName(triggerName);
885             ifExists = readIfExists(ifExists);
886             command.setIfExists(ifExists);
887             return command;
888         } else if(readIf("VIEW")) {
889             boolean ifExists = readIfExists(false);
890             String JavaDoc viewName = readIdentifierWithSchema();
891             DropView command = new DropView(session, getSchema());
892             command.setViewName(viewName);
893             ifExists = readIfExists(ifExists);
894             command.setIfExists(ifExists);
895             return command;
896         } else if(readIf("ROLE")) {
897             boolean ifExists = readIfExists(false);
898             DropRole command = new DropRole(session);
899             command.setRoleName(readUniqueIdentifier());
900             ifExists = readIfExists(ifExists);
901             command.setIfExists(ifExists);
902             return command;
903             // TODO role: support role names SELECT | DELETE | INSERT | UPDATE | ALL? does quoting work?
904
} else if(readIf("ALIAS")) {
905             boolean ifExists = readIfExists(false);
906             DropFunctionAlias command = new DropFunctionAlias(session);
907             command.setAliasName(readUniqueIdentifier());
908             ifExists = readIfExists(ifExists);
909             command.setIfExists(ifExists);
910             return command;
911         } else if(readIf("SCHEMA")) {
912             boolean ifExists = readIfExists(false);
913             DropSchema command = new DropSchema(session);
914             command.setSchemaName(readUniqueIdentifier());
915             ifExists = readIfExists(ifExists);
916             command.setIfExists(ifExists);
917             return command;
918         } else if(readIf("ALL")) {
919             read("OBJECTS");
920             DropDatabase command = new DropDatabase(session);
921             command.setDropAllObjects(true);
922             if(readIf("DELETE")) {
923                 read("FILES");
924                 command.setDeleteFiles(true);
925             }
926             return command;
927         } else if(readIf("DOMAIN")) {
928             return parseDropUserDataType();
929         } else if(readIf("TYPE")) {
930             return parseDropUserDataType();
931         } else if(readIf("DATATYPE")) {
932             return parseDropUserDataType();
933         }
934         throw getSyntaxError();
935     }
936
937     DropUserDataType parseDropUserDataType() throws SQLException JavaDoc {
938         boolean ifExists = readIfExists(false);
939         DropUserDataType command = new DropUserDataType(session);
940         command.setTypeName(readUniqueIdentifier());
941         ifExists = readIfExists(ifExists);
942         command.setIfExists(ifExists);
943         return command;
944     }
945
946     private TableFilter readJoin(TableFilter top, Select command) throws SQLException JavaDoc {
947         TableFilter last = top;
948         while (true) {
949             if (readIf("RIGHT")) {
950                 readIf("OUTER");
951                 read("JOIN");
952                 TableFilter newTop = readTableFilter();
953                 Expression on = null;
954                 if(readIf("ON")) {
955                     on = readExpression();
956                 }
957                 newTop.addJoin(top, true, on);
958                 top = newTop;
959                 last = newTop;
960             } else if (readIf("LEFT")) {
961                 readIf("OUTER");
962                 read("JOIN");
963                 TableFilter join = readTableFilter();
964                 Expression on = null;
965                 if(readIf("ON")) {
966                     on = readExpression();
967                 }
968                 top.addJoin(join, true, on);
969                 last = join;
970             } else if (readIf("INNER")) {
971                 read("JOIN");
972
973                 TableFilter join = readTableFilter();
974                 Expression on = null;
975                 if(readIf("ON")) {
976                     on = readExpression();
977                 }
978                 top.addJoin(join, false, on);
979                 last = join;
980             } else if(readIf("JOIN")) {
981                 TableFilter join = readTableFilter();
982                 Expression on = null;
983                 if(readIf("ON")) {
984                     on = readExpression();
985                 }
986                 top.addJoin(join, false, on);
987                 last = join;
988             } else if(readIf("CROSS")) {
989                 read("JOIN");
990                 TableFilter join = readTableFilter();
991                 top.addJoin(join, false, null);
992                 last = join;
993             } else if(readIf("NATURAL")) {
994                 read("JOIN");
995                 TableFilter join = readTableFilter();
996                 Column[] tc = last.getTable().getColumns();
997                 Column[] jc = join.getTable().getColumns();
998                 String JavaDoc ts = last.getTable().getSchema().getName();
999                 String JavaDoc js = join.getTable().getSchema().getName();
1000                Expression on = null;
1001                for(int t=0; t<tc.length; t++) {
1002                    String JavaDoc tcn = tc[t].getName();
1003                    for(int j=0; j<jc.length; j++) {
1004                        String JavaDoc jcn = jc[j].getName();
1005                        if(tcn.equals(jcn)) {
1006                            Expression te = new ExpressionColumn(database, currentSelect, ts, last.getTableAlias(), tcn);
1007                            Expression je = new ExpressionColumn(database, currentSelect, js, join.getTableAlias(), jcn);
1008                            Expression eq = new Comparison(session, Comparison.EQUAL, te, je);
1009                            if(on == null) {
1010                                on = eq;
1011                            } else {
1012                                on = new ConditionAndOr(ConditionAndOr.AND, on, eq);
1013                            }
1014                        }
1015                    }
1016                }
1017                top.addJoin(join, false, on);
1018                last = join;
1019            } else {
1020                break;
1021            }
1022        }
1023        return top;
1024    }
1025
1026    private void parseJoinTableFilter(TableFilter top, Select command) throws SQLException JavaDoc {
1027        top = readJoin(top, command);
1028        command.addTableFilter(top, true);
1029        boolean isOuter = false;
1030        while(true) {
1031            TableFilter join = top.getJoin();
1032            if(join == null) {
1033                break;
1034            }
1035            isOuter = isOuter | join.isJoinOuter();
1036            if(isOuter) {
1037                command.addTableFilter(join, false);
1038            } else {
1039                // make flat so the optimizer can work better
1040
Expression on = join.getJoinCondition();
1041                if(on != null) {
1042                    command.addCondition(on);
1043                }
1044                join.removeJoinCondition();
1045                top.removeJoin();
1046                command.addTableFilter(join, true);
1047            }
1048            top = join;
1049        }
1050    }
1051
1052    private ExplainPlan parseExplain() throws SQLException JavaDoc {
1053        ExplainPlan command = new ExplainPlan(session);
1054        readIf("PLAN");
1055        readIf("FOR");
1056        if(isToken("SELECT") || isToken("FROM")) {
1057            command.setCommand(parseSelect());
1058        } else if(isToken("(")) {
1059            command.setCommand(parseSelect());
1060        } else if(readIf("DELETE")) {
1061            command.setCommand(parseDelete());
1062        } else if(readIf("UPDATE")) {
1063            command.setCommand(parseUpdate());
1064        } else if(readIf("INSERT")) {
1065            command.setCommand(parseInsert());
1066        } else if(readIf("MERGE")) {
1067            command.setCommand(parseMerge());
1068        } else {
1069            throw getSyntaxError();
1070        }
1071        return command;
1072    }
1073
1074    private Query parseSelect() throws SQLException JavaDoc {
1075        Query command = parseSelectUnion();
1076        command.init();
1077        return command;
1078    }
1079    
1080    private Query parseQueryWithParams() throws SQLException JavaDoc {
1081        int paramIndex = parameters.size();
1082        Query command = parseSelectUnion();
1083        command.init();
1084        ObjectArray params = new ObjectArray();
1085        for(int i=paramIndex; i<parameters.size(); i++) {
1086            params.add(parameters.get(i));
1087        }
1088        command.setParameterList(params);
1089        return command;
1090    }
1091
1092    private Query parseSelectUnion() throws SQLException JavaDoc {
1093        int start = lastParseIndex;
1094        Query command = parseSelectSub();
1095        while(true) {
1096            if (readIf("UNION")) {
1097                SelectUnion union = new SelectUnion(session, command);
1098                if(readIf("ALL")) {
1099                    union.setUnionType(SelectUnion.UNION_ALL);
1100                } else {
1101                    readIf("DISTINCT");
1102                    union.setUnionType(SelectUnion.UNION);
1103                }
1104                // TODO exceptions: always add the SQL statement to the exception, if possible!
1105
union.setRight(parseSelectSub());
1106                command = union;
1107            } else if(readIf("MINUS") || readIf("EXCEPT")) {
1108                SelectUnion union = new SelectUnion(session, command);
1109                union.setUnionType(SelectUnion.EXCEPT);
1110                union.setRight(parseSelectSub());
1111                command = union;
1112            } else if(readIf("INTERSECT")) {
1113                SelectUnion union = new SelectUnion(session, command);
1114                union.setUnionType(SelectUnion.INTERSECT);
1115                union.setRight(parseSelectSub());
1116                command = union;
1117            } else {
1118                break;
1119            }
1120        }
1121        if (readIf("ORDER")) {
1122            read("BY");
1123            Select oldSelect = currentSelect;
1124            if(command instanceof Select) {
1125                currentSelect = (Select)command;
1126            }
1127            ObjectArray orderList = new ObjectArray();
1128            do {
1129                boolean canBeNumber = true;
1130                if(readIf("=")) {
1131                    canBeNumber = false;
1132                }
1133                SelectOrderBy order = new SelectOrderBy();
1134                Expression expr = readExpression();
1135                if(canBeNumber && expr instanceof ValueExpression && expr.getType() == Value.INT) {
1136                    int i = expr.getValue(null).getInt();
1137                    order.column = i-1;
1138                } else {
1139                    order.expression = expr;
1140                }
1141                if(readIf("DESC")) {
1142                    order.descending = true;
1143                } else {
1144                    readIf("ASC");
1145                }
1146                if(readIf("NULLS")) {
1147                    if(readIf("FIRST")) {
1148                        order.nullsFirst = true;
1149                    } else {
1150                        read("LAST");
1151                        order.nullsLast = true;
1152                    }
1153                }
1154                orderList.add(order);
1155            } while(readIf(","));
1156            command.setOrder(orderList);
1157            currentSelect = oldSelect;
1158        }
1159        if(readIf("LIMIT")) {
1160            Expression limit = readExpression().optimize(session);
1161            command.setLimit(limit);
1162            if(readIf("OFFSET")) {
1163                Expression offset = readExpression().optimize(session);
1164                command.setOffset(offset);
1165            } else if(readIf(",")) {
1166                // MySQL: [offset, ] rowcount
1167
Expression offset = limit;
1168                limit = readExpression().optimize(session);
1169                command.setOffset(offset);
1170                command.setLimit(limit);
1171            }
1172            if(readIf("SAMPLE_SIZE")) {
1173                command.setSampleSize(getPositiveInt());
1174            }
1175        }
1176        if(readIf("FOR")) {
1177            if(readIf("UPDATE")) {
1178                if(readIf("OF")) {
1179                    // TODO parser: select for update of: should do something with the list!
1180
do {
1181                        readIdentifierWithSchema();
1182                    } while(readIf(","));
1183                } else if(readIf("NOWAIT")) {
1184                    // TODO parser: select for update nowait: should not wait
1185
} else if(readIf("WITH")) {
1186                    // Hibernate / Derby support
1187
read("RR");
1188                }
1189                command.setForUpdate(true);
1190            } else if(readIf("READ")) {
1191                read("ONLY");
1192                if(readIf("WITH")) {
1193                    read("RS");
1194                }
1195            }
1196        }
1197        setSQL(command, null, start);
1198        return command;
1199    }
1200
1201    private Query parseSelectSub() throws SQLException JavaDoc {
1202        if(readIf("(")) {
1203            Query command = parseSelectUnion();
1204            read(")");
1205            return command;
1206        }
1207        Select select = parseSelectSimple();
1208        return select;
1209    }
1210
1211    private void parseSelectSimpleFromPart(Select command) throws SQLException JavaDoc {
1212        do {
1213            TableFilter filter = readTableFilter();
1214            parseJoinTableFilter(filter, command);
1215        } while(readIf(","));
1216    }
1217
1218    private void parseSelectSimpleSelectPart(Select command) throws SQLException JavaDoc {
1219        if(readIf("TOP")) {
1220            // can't read more complex expressions here because
1221
// SELECT TOP 1 +? A FROM TEST could mean
1222
// SELECT TOP (1+?) A FROM TEST or
1223
// SELECT TOP 1 (+?) AS A FROM TEST
1224
Expression limit = readTerm().optimize(session);
1225            command.setLimit(limit);
1226        } else if(readIf("LIMIT")) {
1227            Expression offset = readTerm().optimize(session);
1228            command.setOffset(offset);
1229            Expression limit = readTerm().optimize(session);
1230            command.setLimit(limit);
1231        }
1232        if(readIf("DISTINCT")) {
1233            command.setDistinct(true);
1234        } else {
1235            readIf("ALL");
1236        }
1237        ObjectArray expressions = new ObjectArray();
1238        do {
1239            if(readIf("*")) {
1240                expressions.add(new Wildcard(null, null));
1241            } else {
1242                Expression expr = readExpression();
1243                if(readIf("AS") || currentTokenType == IDENTIFIER) {
1244                    String JavaDoc alias = readAliasIdentifier();
1245                    expr = new Alias(expr, alias);
1246                }
1247                expressions.add(expr);
1248            }
1249        } while(readIf(","));
1250        command.setExpressions(expressions);
1251    }
1252
1253    private Select parseSelectSimple() throws SQLException JavaDoc {
1254        boolean fromFirst;
1255        if(readIf("SELECT")) {
1256            fromFirst = false;
1257        } else if(readIf("FROM")) {
1258            fromFirst = true;
1259        } else {
1260            throw getSyntaxError();
1261        }
1262        Select command = new Select(session);
1263        int start = lastParseIndex;
1264        Select oldSelect = currentSelect;
1265        currentSelect = command;
1266        currentPrepared = command;
1267        if(fromFirst) {
1268            parseSelectSimpleFromPart(command);
1269            read("SELECT");
1270            parseSelectSimpleSelectPart(command);
1271        } else {
1272            parseSelectSimpleSelectPart(command);
1273            if(!readIf("FROM")) {
1274                // select without FROM: convert to SELECT ... FROM SYSTEM_RANGE(1,1)
1275
Schema main = database.findSchema(Constants.SCHEMA_MAIN);
1276                Table dual = new RangeTable(main, 1, 1);
1277                TableFilter filter = new TableFilter(session, dual, null, rightsChecked);
1278                command.addTableFilter(filter, true);
1279            } else {
1280                parseSelectSimpleFromPart(command);
1281            }
1282        }
1283        if (readIf("WHERE")) {
1284            Expression condition = readExpression();
1285            command.addCondition(condition);
1286        }
1287        // the group by is read for the outer select (or not a select)
1288
// so that columns that are not grouped can be used
1289
currentSelect = oldSelect;
1290        if (readIf("GROUP")) {
1291            read("BY");
1292            command.setGroupQuery();
1293            ObjectArray list = new ObjectArray();
1294            do {
1295                Expression expr = readExpression();
1296                list.add(expr);
1297            } while(readIf(","));
1298            command.setGroupBy(list);
1299        }
1300        currentSelect = command;
1301        if(readIf("HAVING")) {
1302            command.setGroupQuery();
1303            Expression condition = readExpression();
1304            command.setHaving(condition);
1305        }
1306        currentSelect = oldSelect;
1307        setSQL(command, "SELECT", start);
1308        return command;
1309    }
1310
1311    private void setSQL(Prepared command, String JavaDoc start, int startIndex) {
1312        String JavaDoc sql = originalSQL.substring(startIndex, lastParseIndex).trim();
1313        if(start != null) {
1314            sql = start + " " + sql;
1315        }
1316        command.setSQL(sql);
1317    }
1318
1319    private Expression readExpression() throws SQLException JavaDoc {
1320        Expression r = readAnd();
1321        while (readIf("OR")) {
1322            r = new ConditionAndOr(ConditionAndOr.OR, r, readAnd());
1323        }
1324        return r;
1325    }
1326
1327    private Expression readAnd() throws SQLException JavaDoc {
1328        Expression r = readCondition();
1329        while (readIf("AND")) {
1330            r = new ConditionAndOr(ConditionAndOr.AND, r, readCondition());
1331        }
1332        return r;
1333    }
1334
1335    private Expression readCondition() throws SQLException JavaDoc {
1336        // TODO parser: should probably use switch case for performance
1337
if (readIf("NOT")) {
1338            return new ConditionNot(readCondition());
1339        }
1340        if (readIf("EXISTS")) {
1341            read("(");
1342            Query query = parseQueryWithParams();
1343            // can not reduce expression because it might be a union except query with distinct
1344
read(")");
1345            return new ConditionExists(query);
1346        }
1347        Expression r = readConcat();
1348        while(true) {
1349            // special case: NOT NULL is not part of an expression (as in CREATE TABLE TEST(ID INT DEFAULT 0 NOT NULL))
1350
int backup = parseIndex;
1351            boolean not = false;
1352            if (readIf("NOT")) {
1353                not = true;
1354                if(isToken("NULL")) {
1355                    // this really only works for NOT NULL!
1356
parseIndex = backup;
1357                    currentToken = "NOT";
1358                    break;
1359                }
1360            }
1361            if (readIf("LIKE")) {
1362                Expression b = readConcat();
1363                Expression esc = null;
1364                if (readIf("ESCAPE")) {
1365                    esc = readExpression();
1366                }
1367                recompileAlways = true;
1368                r = new CompareLike(database.getCompareMode(), r, b, esc);
1369            } else if (readIf("IS")) {
1370                int type;
1371                if (readIf("NOT")) {
1372                    type = Comparison.IS_NOT_NULL;
1373                } else {
1374                    type = Comparison.IS_NULL;
1375                }
1376                read("NULL");
1377                r = new Comparison(session, type, r, null);
1378            } else if (readIf("IN")) {
1379                // TODO extend IN to support arrays (using setArray?)
1380
if(Constants.OPTIMIZE_IN) {
1381                    recompileAlways = true;
1382                }
1383                read("(");
1384                if(readIf(")")) {
1385                    r = ValueExpression.get(ValueBoolean.get(false));
1386                } else {
1387                    if (isToken("SELECT") || isToken("FROM")) {
1388                        Query query = parseQueryWithParams();
1389                        r = new ConditionInSelect(database, r, query, false, Comparison.EQUAL);
1390                    } else {
1391                        ObjectArray v = new ObjectArray();
1392                        Expression last;
1393                        do {
1394                            last = readExpression();
1395                            v.add(last);
1396                        } while(readIf(","));
1397                        if(v.size()==1 && (last instanceof Subquery)) {
1398                            Subquery s = (Subquery) last;
1399                            Query q = s.getQuery();
1400                            r = new ConditionInSelect(database, r, q, false, Comparison.EQUAL);
1401                        } else {
1402                            r = new ConditionIn(database, r, v);
1403                        }
1404                    }
1405                    read(")");
1406                }
1407            } else if (readIf("BETWEEN")) {
1408                Expression low = readConcat();
1409                read("AND");
1410                Expression high = readConcat();
1411                Expression condLow = new Comparison(session, Comparison.SMALLER_EQUAL, low, r);
1412                Expression condHigh = new Comparison(session, Comparison.BIGGER_EQUAL, high, r);
1413                r = new ConditionAndOr(ConditionAndOr.AND, condLow, condHigh);
1414            } else {
1415                // TODO parser: if we use a switch case, we don't need getCompareType any more
1416
int compareType = getCompareType(currentTokenType);
1417                if(compareType < 0) {
1418                    break;
1419                }
1420                read();
1421                if(readIf("ALL")) {
1422                    read("(");
1423                    Query query = parseQueryWithParams();
1424                    r = new ConditionInSelect(database, r, query, true, compareType);
1425                    read(")");
1426                } else if(readIf("ANY") || readIf("SOME")) {
1427                    read("(");
1428                    Query query = parseQueryWithParams();
1429                    r = new ConditionInSelect(database, r, query, false, compareType);
1430                    read(")");
1431                } else {
1432                    Expression right = readConcat();
1433                    if(readIf("(") && readIf("+") && readIf(")")) {
1434                        // support for a subset of old-fashioned Oracle outer join with (+)
1435
if(r instanceof ExpressionColumn && right instanceof ExpressionColumn) {
1436                            ExpressionColumn lcol = (ExpressionColumn) r;
1437                            ExpressionColumn rcol = (ExpressionColumn) right;
1438                            ObjectArray filters = currentSelect.getTopFilters();
1439                            for(int i=0; filters != null && i<filters.size(); i++) {
1440                                TableFilter f = (TableFilter) filters.get(i);
1441                                lcol.mapColumns(f, 0);
1442                                rcol.mapColumns(f, 0);
1443                            }
1444                            TableFilter lfilter = lcol.getTableFilter();
1445                            TableFilter rfilter = rcol.getTableFilter();
1446                            r = new Comparison(session, compareType, r, right);
1447                            if(lfilter != null && rfilter != null) {
1448                                filters.remove(filters.indexOf(rfilter));
1449                                lfilter.addJoin(rfilter, true, r);
1450                                r = ValueExpression.get(ValueBoolean.get(true));
1451                            }
1452                        }
1453                    } else {
1454                        r = new Comparison(session, compareType, r, right);
1455                    }
1456                }
1457            }
1458            if (not) {
1459                r = new ConditionNot(r);
1460            }
1461        }
1462        return r;
1463    }
1464
1465    private Expression readConcat() throws SQLException JavaDoc {
1466        Expression r = readSum();
1467        while (readIf("||")) {
1468            r = new Operation(Operation.CONCAT, r, readSum());
1469        }
1470        return r;
1471    }
1472
1473    private Expression readSum() throws SQLException JavaDoc {
1474        Expression r = readFactor();
1475        while(true) {
1476            if(readIf("+")) {
1477                r = new Operation(Operation.PLUS, r, readFactor());
1478            } else if(readIf("-")) {
1479                r = new Operation(Operation.MINUS, r, readFactor());
1480            } else {
1481                return r;
1482            }
1483        }
1484    }
1485
1486    private Expression readFactor() throws SQLException JavaDoc {
1487        Expression r = readTerm();
1488        while(true) {
1489            if(readIf("*")) {
1490                r = new Operation(Operation.MULTIPLY, r, readTerm());
1491            } else if(readIf("/")) {
1492                r = new Operation(Operation.DIVIDE, r, readTerm());
1493            } else {
1494                return r;
1495            }
1496        }
1497    }
1498
1499    private Expression readAggregate(int aggregateType) throws SQLException JavaDoc {
1500        if(currentSelect == null) {
1501            // TODO exception: function only allowed in a query
1502
throw getSyntaxError();
1503        }
1504        currentSelect.setGroupQuery();
1505        Expression r;
1506        if(aggregateType == Aggregate.COUNT) {
1507            if(readIf("*")) {
1508                r = new Aggregate(database, Aggregate.COUNT_ALL, null, currentSelect, false);
1509            } else {
1510                boolean distinct = readIf("DISTINCT");
1511                r = new Aggregate(database, Aggregate.COUNT, readExpression(), currentSelect, distinct);
1512            }
1513        } else if(aggregateType == Aggregate.GROUP_CONCAT) {
1514            boolean distinct = readIf("DISTINCT");
1515            Aggregate agg = new Aggregate(database, Aggregate.GROUP_CONCAT, readExpression(), currentSelect, distinct);
1516            if(readIf("ORDER")) {
1517                read("BY");
1518                agg.setOrder(parseSimpleOrderList());
1519            }
1520            if(readIf("SEPARATOR")) {
1521                agg.setSeparator(readExpression());
1522            }
1523            r = agg;
1524        } else {
1525            boolean distinct = readIf("DISTINCT");
1526            r = new Aggregate(database, aggregateType, readExpression(), currentSelect, distinct);
1527        }
1528        read(")");
1529        return r;
1530    }
1531
1532    private ObjectArray parseSimpleOrderList() throws SQLException JavaDoc {
1533        ObjectArray orderList = new ObjectArray();
1534        do {
1535            SelectOrderBy order = new SelectOrderBy();
1536            Expression expr = readExpression();
1537            order.expression = expr;
1538            if(readIf("DESC")) {
1539                order.descending = true;
1540            } else {
1541                readIf("ASC");
1542            }
1543            orderList.add(order);
1544        } while(readIf(","));
1545        return orderList;
1546    }
1547
1548    private JavaFunction readJavaFunction(String JavaDoc name) throws SQLException JavaDoc {
1549        FunctionAlias functionAlias = database.findFunctionAlias(name);
1550        if(functionAlias == null) {
1551            // TODO compatiblity: maybe support 'on the fly java functions' as HSQLDB ( CALL "java.lang.Math.sqrt"(2.0) )
1552
throw Message.getSQLException(Message.FUNCTION_NOT_FOUND_1, name);
1553        }
1554        int paramCount = functionAlias.getParameterCount();
1555        Expression[] args = new Expression[paramCount];
1556        for(int i=0; i<args.length; i++) {
1557            if(i>0) {
1558                read(",");
1559            }
1560            args[i] = readExpression();
1561        }
1562        read(")");
1563        JavaFunction func = new JavaFunction(functionAlias, args);
1564        return func;
1565    }
1566
1567    private Expression readFunction(String JavaDoc name) throws SQLException JavaDoc {
1568        int agg = Aggregate.getAggregateType(name);
1569        if(agg >= 0) {
1570            return readAggregate(agg);
1571        }
1572        Function function = Function.getFunction(database, name);
1573        if(function==null) {
1574            return readJavaFunction(name);
1575        }
1576        switch(function.getFunctionType()) {
1577        case Function.CAST: {
1578            function.setParameter(0, readExpression());
1579            read("AS");
1580            Column type = parseColumn(null);
1581            function.setDataType(type);
1582            read(")");
1583            break;
1584        }
1585        case Function.CONVERT: {
1586            function.setParameter(0, readExpression());
1587            read(",");
1588            Column type = parseColumn(null);
1589            function.setDataType(type);
1590            read(")");
1591            break;
1592        }
1593        case Function.EXTRACT: {
1594            function.setParameter(0, ValueExpression.get(ValueString.get(currentToken)));
1595            read();
1596            read("FROM");
1597            function.setParameter(1, readExpression());
1598            read(")");
1599            break;
1600        }
1601        case Function.SUBSTRING: {
1602            function.setParameter(0, readExpression());
1603            if(!readIf(",")) {
1604                read("FROM");
1605            }
1606            function.setParameter(1, readExpression());
1607            if(readIf("FOR") || readIf(",")) {
1608                function.setParameter(2, readExpression());
1609            }
1610            read(")");
1611            break;
1612        }
1613        case Function.POSITION: {
1614            // can't read expession because IN would be read too early
1615
function.setParameter(0, readConcat());
1616            if(!readIf(",")) {
1617                read("IN");
1618            }
1619            function.setParameter(1, readExpression());
1620            read(")");
1621            break;
1622        }
1623        case Function.TRIM: {
1624            Expression space = null;
1625            if(readIf("LEADING")) {
1626                function = Function.getFunction(database, "LTRIM");
1627                if(!readIf("FROM")) {
1628                    space = readExpression();
1629                    read("FROM");
1630                }
1631            } else if(readIf("TRAILING")) {
1632                function = Function.getFunction(database, "RTRIM");
1633                if(!readIf("FROM")) {
1634                    space = readExpression();
1635                    read("FROM");
1636                }
1637            } else if(readIf("BOTH")) {
1638                if(!readIf("FROM")) {
1639                    space = readExpression();
1640                    read("FROM");
1641                }
1642            }
1643            Expression p0 = readExpression();
1644            if(readIf(",")) {
1645                space = readExpression();
1646            } else if(readIf("FROM")) {
1647                space = p0;
1648                p0 = readExpression();
1649            }
1650            function.setParameter(0, p0);
1651            if(space != null) {
1652                function.setParameter(1, space);
1653            }
1654            read(")");
1655            break;
1656        }
1657        default:
1658            if(!readIf(")")) {
1659                int i=0;
1660                do {
1661                    function.setParameter(i++, readExpression());
1662                } while(readIf(","));
1663                read(")");
1664            }
1665        }
1666        function.doneWithParameters();
1667        return function;
1668    }
1669
1670    private Function readFunctionWithoutParameters(String JavaDoc name) throws SQLException JavaDoc {
1671        if(readIf("(")) {
1672            read(")");
1673        }
1674        Function function = Function.getFunction(database, name);
1675        function.doneWithParameters();
1676        return function;
1677    }
1678    
1679    private Expression readWildcardOrSequenceValue(String JavaDoc schemaName, String JavaDoc objectName) throws SQLException JavaDoc {
1680        if(readIf("*")) {
1681            return new Wildcard(schemaName, objectName);
1682        }
1683        if(schemaName == null) {
1684            schemaName = session.getCurrentSchemaName();
1685        }
1686        if(readIf("NEXTVAL")) {
1687            Sequence sequence = database.getSchema(schemaName).findSequence(objectName);
1688            if(sequence != null) {
1689                return new SequenceValue(sequence);
1690            }
1691        } else if(readIf("CURRVAL")) {
1692            Sequence sequence = database.getSchema(schemaName).findSequence(objectName);
1693            if(sequence != null) {
1694                Function function = Function.getFunction(database, "CURRVAL");
1695                function.setParameter(0, ValueExpression.get(ValueString.get(objectName)));
1696                return function;
1697            }
1698        }
1699        return null;
1700    }
1701
1702    private Expression readTermObjectDot(String JavaDoc objectName) throws SQLException JavaDoc {
1703        Expression expr = readWildcardOrSequenceValue(null, objectName);
1704        if(expr != null) {
1705            return expr;
1706        }
1707        String JavaDoc name = readColumnIdentifier();
1708        if(readIf(".")) {
1709            String JavaDoc schemaName = objectName;
1710            objectName = name;
1711            expr = readWildcardOrSequenceValue(schemaName, objectName);
1712            if(expr != null) {
1713                return expr;
1714            }
1715            name = readColumnIdentifier();
1716            return new ExpressionColumn(database, currentSelect, schemaName, objectName, name);
1717        }
1718        return new ExpressionColumn(database, currentSelect, null, objectName, name);
1719    }
1720    
1721    private Expression readTerm() throws SQLException JavaDoc {
1722        Expression r = null;
1723        switch (currentTokenType) {
1724        case PARAMETER:
1725            // there must be no space between ? and the number
1726
boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
1727            read();
1728            if(indexed && currentTokenType == VALUE && currentValue.getType() == Value.INT) {
1729                if(indexedParameterList == null) {
1730                    if(parameters.size()>0) {
1731                        throw Message.getSQLException(Message.CANT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
1732                    }
1733                    indexedParameterList = new ObjectArray();
1734                }
1735                int index = currentValue.getInt() - 1;
1736                if(index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
1737                    throw Message.getInvalidValueException("" + index, "Parameter Index");
1738                }
1739                if(indexedParameterList.size() <= index) {
1740                    indexedParameterList.setSize(index + 1);
1741                }
1742                r = (Parameter) indexedParameterList.get(index);
1743                if(r == null) {
1744                    r = new Parameter(index);
1745                    indexedParameterList.set(index, r);
1746                }
1747                read();
1748            } else {
1749                if(indexedParameterList != null) {
1750                    throw Message.getSQLException(Message.CANT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
1751                }
1752                r = new Parameter(parameters.size());
1753            }
1754            parameters.add(r);
1755            break;
1756        case KEYWORD:
1757            if(isToken("SELECT") || isToken("FROM")) {
1758                Query query = parseQueryWithParams();
1759                return new Subquery(query);
1760            }
1761            throw getSyntaxError();
1762        case IDENTIFIER:
1763            String JavaDoc name = currentToken;
1764            if(currentTokenQuoted) {
1765                read();
1766                if(readIf(".")) {
1767                    return readTermObjectDot(name);
1768                }
1769                return new ExpressionColumn(database, currentSelect, null, null, name);
1770            }
1771            read();
1772            if("X".equals(name) && currentTokenType == VALUE && currentValue.getType() == Value.STRING) {
1773                read();
1774                byte[] buffer = ByteUtils.convertStringToBytes(currentValue.getString());
1775                r = ValueExpression.get(ValueBytes.getNoCopy(buffer));
1776            } else if(readIf(".")) {
1777                return readTermObjectDot(name);
1778            } else if (readIf("(")) {
1779                return readFunction(name);
1780            } else if("CURRENT".equals(name)) {
1781                if(readIf("TIMESTAMP")) {
1782                    return readFunctionWithoutParameters("CURRENT_TIMESTAMP");
1783                } else if(readIf("TIME")) {
1784                    return readFunctionWithoutParameters("CURRENT_TIME");
1785                } else if(readIf("DATE")) {
1786                    return readFunctionWithoutParameters("CURRENT_DATE");
1787                } else {
1788                    return new ExpressionColumn(database, currentSelect, null, null, name);
1789                }
1790            } else if("NEXT".equals(name) && readIf("VALUE")) {
1791                read("FOR");
1792                String JavaDoc sequenceName = readIdentifierWithSchema();
1793                Sequence sequence = getSchema().getSequence(sequenceName);
1794                return new SequenceValue(sequence);
1795            } else if("DATE".equals(name) && currentTokenType == VALUE && currentValue.getType() == Value.STRING) {
1796                String JavaDoc date = currentValue.getString();
1797                read();
1798                return ValueExpression.get(ValueDate.get(ValueDate.parseDate(date)));
1799            } else if("TIME".equals(name) && currentTokenType == VALUE && currentValue.getType() == Value.STRING) {
1800                String JavaDoc time = currentValue.getString();
1801                read();
1802                return ValueExpression.get(ValueTime.get(ValueTime.parseTime(time)));
1803            } else if("TIMESTAMP".equals(name) && currentTokenType == VALUE && currentValue.getType() == Value.STRING) {
1804                String JavaDoc timestamp = currentValue.getString();
1805                read();
1806                return ValueExpression.get(ValueTimestamp.getNoCopy(ValueTimestamp.parseTimestamp(timestamp)));
1807            } else if("CASE".equals(name)) {
1808                if(isToken("WHEN")) {
1809                    return readWhen(null);
1810                } else {
1811                    Expression left = readExpression();
1812                    return readWhen(left);
1813                }
1814            } else {
1815                return new ExpressionColumn(database, currentSelect, null, null, name);
1816            }
1817            break;
1818        case MINUS:
1819            read();
1820            if (currentTokenType == VALUE) {
1821                Expression e = ValueExpression.get(currentValue.negate());
1822                // convert Integer.MIN_VALUE to int (-Integer.MIN_VALUE needed to be a long)
1823
if(e.getType() == Value.LONG && e.getValue(session).getLong() == Integer.MIN_VALUE) {
1824                    e = ValueExpression.get(ValueInt.get(Integer.MIN_VALUE));
1825                }
1826                // TODO parser: maybe convert Long.MIN_VALUE from decimal to long?
1827
read();
1828                return e;
1829            }
1830            return new Operation(Operation.NEGATE, readTerm(), null);
1831        case PLUS:
1832            read();
1833            return readTerm();
1834        case OPEN:
1835            read();
1836            r = readExpression();
1837            if(readIf(",")) {
1838                ObjectArray list = new ObjectArray();
1839                list.add(r);
1840                do {
1841                    r = readExpression();
1842                    list.add(r);
1843                } while(readIf(","));
1844                Expression[] array = new Expression[list.size()];
1845                list.toArray(array);
1846                r = new ExpressionList(array);
1847            }
1848            read(")");
1849            break;
1850        case TRUE:
1851            read();
1852            return ValueExpression.get(ValueBoolean.get(true));
1853        case FALSE:
1854            read();
1855            return ValueExpression.get(ValueBoolean.get(false));
1856        case CURRENT_TIME:
1857            read();
1858            return readFunctionWithoutParameters("CURRENT_TIME");
1859        case CURRENT_DATE:
1860            read();
1861            return readFunctionWithoutParameters("CURRENT_DATE");
1862        case CURRENT_TIMESTAMP: {
1863            Function function = Function.getFunction(database, "CURRENT_TIMESTAMP");
1864            read();
1865            if(readIf("(")) {
1866                if(!readIf(")")) {
1867                    function.setParameter(0, readExpression());
1868                    read(")");
1869                }
1870            }
1871            function.doneWithParameters();
1872            return function;
1873        }
1874        case ROWNUM:
1875            read();
1876            if(readIf("(")) {
1877                read(")");
1878            }
1879            return new Rownum(currentSelect == null ? currentPrepared : currentSelect);
1880        case NULL:
1881            read();
1882            if(readIf("::")) {
1883                // PostgreSQL compatibility
1884
parseColumn(null);
1885            }
1886            return ValueExpression.NULL;
1887        case VALUE:
1888            r = ValueExpression.get(currentValue);
1889            read();
1890            break;
1891        default:
1892            // TODO exception: expected a term
1893
throw getSyntaxError();
1894        }
1895        return r;
1896    }
1897
1898    private Expression readWhen(Expression left) throws SQLException JavaDoc {
1899        if(readIf("END")) {
1900            readIf("CASE");
1901            return ValueExpression.NULL;
1902        }
1903        if(readIf("ELSE")) {
1904            Expression elsePart = readExpression();
1905            read("END");
1906            readIf("CASE");
1907            return elsePart;
1908        }
1909        readIf("WHEN");
1910        Expression when = readExpression();
1911        if(left != null) {
1912            when = new Comparison(session, Comparison.EQUAL, left, when);
1913        }
1914        read("THEN");
1915        Expression then = readExpression();
1916        Expression elsePart = readWhen(left);
1917        Function function = Function.getFunction(session.getDatabase(), "CASEWHEN");
1918        function.setParameter(0, when);
1919        function.setParameter(1, then);
1920        function.setParameter(2, elsePart);
1921        return function;
1922    }
1923
1924    private int getPositiveInt() throws SQLException JavaDoc {
1925        int v = getInt();
1926        if(v < 0) {
1927            throw Message.getInvalidValueException("" + v, "positive integer");
1928        }
1929        return v;
1930    }
1931
1932    private int getInt() throws SQLException JavaDoc {
1933        boolean minus = false;
1934        if(currentTokenType == MINUS) {
1935            minus = true;
1936            read();
1937        } else if(currentTokenType == PLUS) {
1938            read();
1939        }
1940        if (currentTokenType != VALUE || currentValue.getType() != Value.INT) {
1941            throw Message.getSyntaxError(sqlCommand, parseIndex, "integer");
1942        }
1943        int i = currentValue.getInt();
1944        read();
1945        return minus ? -i : i;
1946    }
1947
1948    private long readLong() throws SQLException JavaDoc {
1949        boolean minus = false;
1950        if(currentTokenType == MINUS) {
1951            minus = true;
1952            read();
1953        }
1954        if (currentTokenType != VALUE || (currentValue.getType() != Value.INT && currentValue.getType() != Value.DECIMAL)) {
1955            throw Message.getSyntaxError(sqlCommand, parseIndex, "long");
1956        }
1957        long i = currentValue.getLong();
1958        read();
1959        return minus ? -i : i;
1960    }
1961
1962    private boolean readBooleanSetting() throws SQLException JavaDoc {
1963        if(currentTokenType==VALUE) {
1964            boolean result = currentValue.getBoolean().booleanValue();
1965            read();
1966            return result;
1967        }
1968        if(readIf("TRUE") || readIf("ON")) {
1969            return true;
1970        } else if(readIf("FALSE") || readIf("OFF")) {
1971            return false;
1972        } else {
1973            throw getSyntaxError();
1974        }
1975    }
1976
1977    private String JavaDoc readString() throws SQLException JavaDoc {
1978        // TODO parser: readInt/Long could maybe use readExpression as well
1979
Expression expr = readExpression().optimize(session);
1980        if(!(expr instanceof ValueExpression)) {
1981            throw Message.getSyntaxError(sqlCommand, parseIndex, "string");
1982        }
1983        String JavaDoc s = expr.getValue(session).getString();
1984        return s;
1985    }
1986
1987    private String JavaDoc readIdentifierWithSchema(String JavaDoc defaultSchemaName) throws SQLException JavaDoc {
1988        if (currentTokenType != IDENTIFIER) {
1989            throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
1990        }
1991        String JavaDoc s = currentToken;
1992        read();
1993        schemaName = defaultSchemaName;
1994        if (readIf(".")) {
1995            schemaName = s;
1996            if (currentTokenType != IDENTIFIER) {
1997                throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
1998            }
1999            s = currentToken;
2000            read();
2001        }
2002        return s;
2003    }
2004
2005    private String JavaDoc readIdentifierWithSchema() throws SQLException JavaDoc {
2006        return readIdentifierWithSchema(session.getCurrentSchemaName());
2007    }
2008
2009    private String JavaDoc readAliasIdentifier() throws SQLException JavaDoc {
2010        return readColumnIdentifier();
2011    }
2012
2013    private String JavaDoc readUniqueIdentifier() throws SQLException JavaDoc {
2014        return readColumnIdentifier();
2015    }
2016
2017    private String JavaDoc readColumnIdentifier() throws SQLException JavaDoc {
2018        if (currentTokenType != IDENTIFIER) {
2019            throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
2020        }
2021        String JavaDoc s = currentToken;
2022        read();
2023        return s;
2024    }
2025
2026    private void read(String JavaDoc expected) throws SQLException JavaDoc {
2027        if (!expected.equals(currentToken) || currentTokenQuoted) {
2028            throw Message.getSyntaxError(sqlCommand, parseIndex, expected);
2029        }
2030        read();
2031    }
2032
2033    private boolean readIf(String JavaDoc token) throws SQLException JavaDoc {
2034        if(token.equals(currentToken) && !currentTokenQuoted) {
2035            read();
2036            return true;
2037        }
2038        addExpected(token);
2039        return false;
2040    }
2041
2042    private boolean isToken(String JavaDoc token) {
2043        boolean result = token.equals(currentToken) && !currentTokenQuoted;
2044        if(result) {
2045            return true;
2046        }
2047        addExpected(token);
2048        return false;
2049    }
2050
2051    private void addExpected(String JavaDoc token) {
2052        if(expected != null) {
2053            expected.add(token);
2054        }
2055    }
2056
2057    private void read() throws SQLException JavaDoc {
2058        currentTokenQuoted = false;
2059        if(expected != null) {
2060            expected.clear();
2061        }
2062        int[] types = characterTypes;
2063        lastParseIndex = parseIndex;
2064        int i = parseIndex;
2065        int type = types[i];
2066        while (type == 0) {
2067            type = types[++i];
2068        }
2069        int start = i;
2070        char[] chars = sqlCommandChars;
2071        char c = chars[i++];
2072        currentToken = "";
2073        switch (type) {
2074        case CHAR_NAME:
2075            while (true) {
2076                type = types[i];
2077                if (type != CHAR_NAME && type != CHAR_VALUE) {
2078                    c = chars[i];
2079                    break;
2080                }
2081                i++;
2082            }
2083            currentToken = StringCache.getNew(sqlCommand.substring(start, i));
2084            currentTokenType = getTokenType(currentToken);
2085            parseIndex = i;
2086            return;
2087        case CHAR_QUOTED: {
2088            String JavaDoc result = null;
2089            while(true) {
2090                for(int begin=i; ; i++) {
2091                    if(chars[i]=='\"') {
2092                        if(result == null) {
2093                            result = sqlCommand.substring(begin, i);
2094                        } else {
2095                            result += sqlCommand.substring(begin-1, i);
2096                        }
2097                        break;
2098                    }
2099                }
2100                if(chars[++i] != '\"') {
2101                    break;
2102                }
2103                i++;
2104            }
2105            currentToken = StringCache.getNew(result);
2106            parseIndex = i;
2107            currentTokenQuoted = true;
2108            currentTokenType = IDENTIFIER;
2109            return;
2110        }
2111        case CHAR_SPECIAL_2:
2112            if (types[i] == CHAR_SPECIAL_2) {
2113                i++;
2114            }
2115            // fall through
2116
case CHAR_SPECIAL_1:
2117            currentToken = sqlCommand.substring(start, i);
2118            currentTokenType = getSpecialType(currentToken);
2119            parseIndex = i;
2120            return;
2121        case CHAR_VALUE:
2122            if(c == '0' && chars[i] == 'X') {
2123                // hex number
2124
long number = 0;
2125                start += 2;
2126                i++;
2127                while (true) {
2128                    c = chars[i];
2129                    if ((c < '0' || c > '9') && (c<'A' || c>'F')) {
2130                        checkLiterals(false);
2131                        currentValue = ValueInt.get((int) number);
2132                        currentTokenType = VALUE;
2133                        currentToken = "0";
2134                        parseIndex = i;
2135                        return;
2136                    }
2137                    number = (number << 4) + c - (c >= 'A' ? ('A' - 0xa) : ('0'));
2138                    if (number > Integer.MAX_VALUE) {
2139                        readHexDecimal(start, i);
2140                        return;
2141                    }
2142                    i++;
2143                }
2144            }
2145            long number = c - '0';
2146            while (true) {
2147                c = chars[i];
2148                if (c < '0' || c > '9') {
2149                    if (c == '.') {
2150                        readDecimal(start, i);
2151                        break;
2152                    }
2153                    if (c == 'E') {
2154                        readDecimal(start, i);
2155                        break;
2156                    }
2157                    checkLiterals(false);
2158                    currentValue = ValueInt.get((int) number);
2159                    currentTokenType = VALUE;
2160                    currentToken = "0";
2161                    parseIndex = i;
2162                    break;
2163                }
2164                number = number * 10 + (c - '0');
2165                if (number > Integer.MAX_VALUE) {
2166                    readDecimal(start, i);
2167                    break;
2168                }
2169                i++;
2170            }
2171            return;
2172        case CHAR_DECIMAL:
2173            if (types[i] != CHAR_VALUE) {
2174                currentTokenType = KEYWORD;
2175                currentToken = ".";
2176                parseIndex = i;
2177                return;
2178            }
2179            readDecimal(i - 1, i);
2180            return;
2181        case CHAR_STRING: {
2182            String JavaDoc result = null;
2183            while(true) {
2184                for(int begin=i; ; i++) {
2185                    if(chars[i]=='\'') {
2186                        if(result == null) {
2187                            result = sqlCommand.substring(begin, i);
2188                        } else {
2189                            result += sqlCommand.substring(begin-1, i);
2190                        }
2191                        break;
2192                    }
2193                }
2194                if(chars[++i] != '\'') {
2195                    break;
2196                }
2197                i++;
2198            }
2199            currentToken = "'";
2200            checkLiterals(false);
2201            currentValue = ValueString.get(StringCache.getNew(result));
2202            parseIndex = i;
2203            currentTokenType = VALUE;
2204            return;
2205        }
2206        case CHAR_END:
2207            currentToken = "";
2208            currentTokenType = END;
2209            parseIndex = i;
2210            return;
2211        default:
2212            // TODO exception: unsupported character
2213
throw getSyntaxError();
2214        }
2215    }
2216
2217    private void checkLiterals(boolean text) throws SQLException JavaDoc {
2218        if(!session.getAllowLiterals()) {
2219            int allowed = database.getAllowLiterals();
2220            if(allowed == Constants.ALLOW_LITERALS_NONE || (text && allowed != Constants.ALLOW_LITERALS_ALL)) {
2221                throw Message.getSQLException(Message.LITERALS_ARE_NOT_ALLOWED);
2222            }
2223        }
2224    }
2225
2226    private void readHexDecimal(int start, int i) throws SQLException JavaDoc {
2227        char[] chars = sqlCommandChars;
2228        char c;
2229        do {
2230            c = chars[++i];
2231        } while ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) ;
2232        parseIndex = i;
2233        String JavaDoc sub = sqlCommand.substring(start, i);
2234        BigDecimal JavaDoc bd = new BigDecimal JavaDoc(new BigInteger JavaDoc(sub, 16));
2235        checkLiterals(false);
2236        currentValue = ValueDecimal.get(bd);
2237        currentTokenType = VALUE;
2238    }
2239
2240    private void readDecimal(int start, int i) throws SQLException JavaDoc {
2241        char[] chars = sqlCommandChars;
2242        int[] types = characterTypes;
2243        // go until the first non-number
2244
while(true) {
2245            int t = types[i];
2246            if(t != CHAR_DECIMAL && t != CHAR_VALUE) {
2247                break;
2248            }
2249            i++;
2250        }
2251        if (chars[i] == 'E') {
2252            i++;
2253            if (chars[i] == '+' || chars[i] == '-') {
2254                i++;
2255            }
2256            if (types[i] != CHAR_VALUE) {
2257                // TODO exception: error reading value
2258
throw getSyntaxError();
2259            }
2260            while (types[++i] == CHAR_VALUE) {
2261                // go until the first non-number
2262
}
2263        }
2264        parseIndex = i;
2265        String JavaDoc sub = sqlCommand.substring(start, i);
2266        BigDecimal JavaDoc bd;
2267        try {
2268            bd = new BigDecimal JavaDoc(sub);
2269        } catch (NumberFormatException JavaDoc e) {
2270            throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, new String JavaDoc[] { sub }, e);
2271        }
2272        checkLiterals(false);
2273        currentValue = ValueDecimal.get(bd);
2274        currentTokenType = VALUE;
2275    }
2276
2277    public String JavaDoc getOriginalSQL() {
2278        return originalSQL;
2279    }
2280
2281    public Session getSession() {
2282        return session;
2283    }
2284
2285    private void initialize(String JavaDoc sql) throws SQLException JavaDoc {
2286        if(sql == null) {
2287            sql = "";
2288        }
2289        originalSQL = sql;
2290        sqlCommand = sql;
2291        int len = sql.length() + 1;
2292        char[] command = new char[len];
2293        int[] types = new int[len];
2294        len--;
2295        sql.getChars(0, len, command, 0);
2296        boolean changed = false;
2297        command[len] = ' ';
2298        int startLoop = 0;
2299        // TODO optimization in parser: could remember the length of each token
2300
for (int i = 0; i < len; i++) {
2301            char c = command[i];
2302            int type = 0;
2303            switch (c) {
2304            case '/':
2305                if (command[i + 1] == '*') {
2306                    // block comment
2307
changed = true;
2308                    command[i] = ' ';
2309                    command[i + 1] = ' ';
2310                    startLoop = i;
2311                    i += 2;
2312                    checkRunOver(i, len, startLoop);
2313                    while (command[i] != '*' || command[i + 1] != '/') {
2314                        command[i++] = ' ';
2315                        checkRunOver(i, len, startLoop);
2316                    }
2317                    command[i] = ' ';
2318                    command[i + 1] = ' ';
2319                    i++;
2320                    break;
2321                } else if(command[i + 1] == '/') {
2322                    // single line comment
2323
changed = true;
2324                    startLoop = i;
2325                    while (true) {
2326                        c = command[i];
2327                        if (c == '\n' || c == '\r' || i >= len-1) {
2328                            break;
2329                        }
2330                        command[i++] = ' ';
2331                        checkRunOver(i, len, startLoop);
2332                    }
2333                    break;
2334                }
2335                // fallthrough
2336
case '-':
2337                if (command[i + 1] == '-') {
2338                    // single line comment
2339
changed = true;
2340                    startLoop = i;
2341                    while (true) {
2342                        c = command[i];
2343                        if (c == '\n' || c == '\r' || i >= len-1) {
2344                            break;
2345                        }
2346                        command[i++] = ' ';
2347                        checkRunOver(i, len, startLoop);
2348                    }
2349                    break;
2350                }
2351                // fallthrough
2352
case '(':
2353            case ')':
2354            case '{':
2355            case '}':
2356            case '*':
2357            case ',':
2358            case ';':
2359            case '+':
2360            case '%':
2361            case '?':
2362                type = CHAR_SPECIAL_1;
2363                break;
2364            case '!':
2365            case '<':
2366            case '>':
2367            case '|':
2368            case '=':
2369            case ':':
2370                type = CHAR_SPECIAL_2;
2371                break;
2372            case '.':
2373                type = CHAR_DECIMAL;
2374                break;
2375            case '\'':
2376                type = types[i] = CHAR_STRING;
2377                startLoop = i;
2378                while (command[++i] != '\'') {
2379                    checkRunOver(i, len, startLoop);
2380                }
2381                break;
2382            case '[':
2383                // SQL Server alias for "
2384
command[i] = '"';
2385                changed = true;
2386                type = types[i] = CHAR_QUOTED;
2387                startLoop = i;
2388                while (command[++i] != ']') {
2389                    checkRunOver(i, len, startLoop);
2390                }
2391                command[i] = '"';
2392                break;
2393            case '`':
2394                // MySQL alias for ", but not case sensitive
2395
command[i] = '"';
2396                changed = true;
2397                type = types[i] = CHAR_QUOTED;
2398                startLoop = i;
2399                while (command[++i] != '`') {
2400                    checkRunOver(i, len, startLoop);
2401                    c = command[i];
2402                    command[i] = Character.toUpperCase(c);
2403                }
2404                command[i] = '"';
2405                break;
2406            case '\"':
2407                type = types[i] = CHAR_QUOTED;
2408                startLoop = i;
2409                while (command[++i] != '\"') {
2410                    checkRunOver(i, len, startLoop);
2411                }
2412                break;
2413            case '_':
2414                type = CHAR_NAME;
2415                break;
2416            default:
2417                if (c >= 'a' && c <= 'z') {
2418                    command[i] = (char) (c - ('a' - 'A'));
2419                    changed = true;
2420                    type = CHAR_NAME;
2421                } else if (c >= 'A' && c <= 'Z') {
2422                    type = CHAR_NAME;
2423                } else if (c >= '0' && c <= '9') {
2424                    type = CHAR_VALUE;
2425                } else {
2426                    if(Character.isLetterOrDigit(c)) {
2427                        type = CHAR_NAME;
2428                        char u = Character.toUpperCase(c);
2429                        if(u != c) {
2430                            command[i] = u;
2431                            changed = true;
2432                        }
2433                    }
2434                }
2435            }
2436            types[i] = (byte)type;
2437        }
2438        sqlCommandChars = command;
2439        types[len] = CHAR_END;
2440        characterTypes = types;
2441        if(changed) {
2442            sqlCommand = new String JavaDoc(command);
2443        }
2444        parseIndex = 0;
2445    }
2446
2447    private void checkRunOver(int i, int len, int startLoop) throws SQLException JavaDoc {
2448        if(i >= len) {
2449            parseIndex = startLoop;
2450            // TODO exception: unexpected end
2451
throw getSyntaxError();
2452        }
2453    }
2454
2455    private int getSpecialType(String JavaDoc s) throws SQLException JavaDoc {
2456        char c0 = s.charAt(0);
2457        if(s.length()==1) {
2458            switch(c0) {
2459            case '?':
2460                return PARAMETER;
2461            case '+':
2462                return PLUS;
2463            case '-':
2464                return MINUS;
2465            case '{':
2466            case '}':
2467            case '*':
2468            case '/':
2469            case ';':
2470            case ',':
2471            case ':':
2472                return KEYWORD;
2473            case '(':
2474                return OPEN;
2475            case ')':
2476                return CLOSE;
2477            case '<':
2478                return SMALLER;
2479            case '>':
2480                return BIGGER;
2481            case '=':
2482                return EQUAL;
2483            }
2484        } else if(s.length()==2) {
2485            switch (c0) {
2486            case ':':
2487                if(s.equals("::")) {
2488                    return KEYWORD;
2489                }
2490                break;
2491            case '>':
2492                if(s.equals(">=")) {
2493                    return BIGGER_EQUAL;
2494                }
2495                break;
2496            case '<':
2497                if (s.equals("<=")) {
2498                    return SMALLER_EQUAL;
2499                } else if (s.equals("<>")) {
2500                    return NOT_EQUAL;
2501                }
2502                break;
2503            case '!':
2504                if (s.equals("!=")) {
2505                    return NOT_EQUAL;
2506                }
2507                break;
2508            case '|':
2509                if(s.equals("||")) {
2510                    return STRINGCONCAT;
2511                }
2512            }
2513        }
2514        throw getSyntaxError();
2515    }
2516
2517    private int getTokenType(String JavaDoc s) throws SQLException JavaDoc {
2518        // TODO the list of keywords is in the documentation! should be a hash map!
2519
int len = s.length();
2520        if(len == 0) {
2521            throw getSyntaxError();
2522        }
2523        return getSaveTokenType(s);
2524    }
2525
2526    public static boolean isKeyword(String JavaDoc s) {
2527        if(s == null || s.length() == 0) {
2528            return false;
2529        }
2530        return getSaveTokenType(s) != IDENTIFIER;
2531    }
2532
2533    private static int getSaveTokenType(String JavaDoc s) {
2534        switch (s.charAt(0)) {
2535        case 'C':
2536            if(s.endsWith("CURRENT_TIMESTAMP")) {
2537                return CURRENT_TIMESTAMP;
2538            } else if(s.endsWith("CURRENT_TIME")) {
2539                return CURRENT_TIME;
2540            } else if(s.endsWith("CURRENT_DATE")) {
2541                return CURRENT_DATE;
2542            }
2543            return getKeywordOrIdentifier(s, "CROSS", KEYWORD);
2544        case 'D':
2545            return getKeywordOrIdentifier(s, "DISTINCT", KEYWORD);
2546        case 'E':
2547            if(s.equals("EXCEPT")) {
2548                return KEYWORD;
2549            }
2550            return getKeywordOrIdentifier(s, "EXISTS", KEYWORD);
2551        case 'F':
2552            if(s.equals("FROM")) {
2553                return KEYWORD;
2554            } else if(s.equals("FOR")) {
2555                return KEYWORD;
2556            } else if(s.equals("FULL")) {
2557                return KEYWORD;
2558            }
2559            return getKeywordOrIdentifier(s, "FALSE", FALSE);
2560        case 'G':
2561            return getKeywordOrIdentifier(s, "GROUP", KEYWORD);
2562        case 'H':
2563            return getKeywordOrIdentifier(s, "HAVING", KEYWORD);
2564        case 'I':
2565            if (s.equals("INNER")) {
2566                return KEYWORD;
2567            } else if(s.equals("INTERSECT")) {
2568                return KEYWORD;
2569            }
2570            return getKeywordOrIdentifier(s, "IS", KEYWORD);
2571        case 'J':
2572            return getKeywordOrIdentifier(s, "JOIN", KEYWORD);
2573        case 'L':
2574            if (s.equals("LIMIT")) {
2575                return KEYWORD;
2576            }
2577            return getKeywordOrIdentifier(s, "LIKE", KEYWORD);
2578        case 'M':
2579            if(s.equals("MINUS")) {
2580                return KEYWORD;
2581            }
2582            break;
2583        case 'N':
2584            if(s.equals("NOT")) {
2585                return KEYWORD;
2586            } else if(s.equals("NATURAL")) {
2587                return KEYWORD;
2588            }
2589            return getKeywordOrIdentifier(s, "NULL", NULL);
2590        case 'O':
2591            if (s.equals("ON")) {
2592                return KEYWORD;
2593            }
2594            return getKeywordOrIdentifier(s, "ORDER", KEYWORD);
2595        case 'P':
2596            return getKeywordOrIdentifier(s, "PRIMARY", KEYWORD);
2597        case 'R':
2598            return getKeywordOrIdentifier(s, "ROWNUM", ROWNUM);
2599        case 'S':
2600            if(s.endsWith("SYSTIMESTAMP")) {
2601                return CURRENT_TIMESTAMP;
2602            } else if(s.endsWith("SYSTIME")) {
2603                return CURRENT_TIME;
2604            } else if(s.endsWith("SYSDATE")) {
2605                return CURRENT_DATE;
2606            }
2607            return getKeywordOrIdentifier(s, "SELECT", KEYWORD);
2608        case 'T':
2609            if(s.equals("TODAY")) {
2610                return CURRENT_DATE;
2611            }
2612            return getKeywordOrIdentifier(s, "TRUE", TRUE);
2613        case 'U':
2614            return getKeywordOrIdentifier(s, "UNION", KEYWORD);
2615        case 'W':
2616            return getKeywordOrIdentifier(s, "WHERE", KEYWORD);
2617        }
2618        return IDENTIFIER;
2619    }
2620
2621    private static int getKeywordOrIdentifier(String JavaDoc s1, String JavaDoc s2, int keywordType) {
2622        if (s1.equals(s2)) {
2623            return keywordType;
2624        }
2625        return IDENTIFIER;
2626    }
2627
2628    private Column parseColumnForTable(String JavaDoc columnName) throws SQLException JavaDoc {
2629        Column column;
2630        if(readIf("IDENTITY")) {
2631            column = new Column(columnName, Value.LONG, ValueLong.PRECISION, 0);
2632            column.setOriginalSQL("IDENTITY");
2633            long start = 1, increment = 1;
2634            if(readIf("(")) {
2635                start = readLong();
2636                if(readIf(",")) {
2637                    increment = readLong();
2638                }
2639                read(")");
2640            }
2641            column.setAutoIncrement(true, start, increment);
2642        } else {
2643            column = parseColumn(columnName);
2644        }
2645        if (readIf("NOT")) {
2646            read("NULL");
2647            column.setNullable(false);
2648        } else {
2649            readIf("NULL");
2650            column.setNullable(true);
2651        }
2652        if(readIf("AS")) {
2653            Expression expr = readExpression();
2654            column.setComputed(true, expr);
2655        } else if (readIf("DEFAULT")) {
2656            Expression defaultExpression = readExpression();
2657            column.setDefaultExpression(session, defaultExpression);
2658        } else if(readIf("GENERATED")) {
2659            read("BY");
2660            read("DEFAULT");
2661            read("AS");
2662            read("IDENTITY");
2663            long start = 1, increment = 1;
2664            if(readIf("(")) {
2665                read("START");
2666                read("WITH");
2667                start = readLong();
2668                readIf(",");
2669                if(readIf("INCREMENT")) {
2670                    read("BY");
2671                    increment = readLong();
2672                }
2673                read(")");
2674            }
2675            column.setAutoIncrement(true, start, increment);
2676        }
2677        if (readIf("NOT")) {
2678            read("NULL");
2679            column.setNullable(false);
2680        } else {
2681            readIf("NULL");
2682        }
2683        if(readIf("AUTO_INCREMENT") || readIf("IDENTITY")) {
2684            long start = 1, increment = 1;
2685            if(readIf("(")) {
2686                start = readLong();
2687                if(readIf(",")) {
2688                    increment = readLong();
2689                }
2690                read(")");
2691            }
2692            column.setAutoIncrement(true, start, increment);
2693            if (readIf("NOT")) {
2694                read("NULL");
2695            }
2696        }
2697        if(readIf("NULL_TO_DEFAULT")) {
2698            column.setConvertNullToDefault(true);
2699        }
2700        if(readIf("SEQUENCE")) {
2701            String JavaDoc sequenceName = readIdentifierWithSchema();
2702            Sequence sequence = getSchema().getSequence(sequenceName);
2703            column.setSequence(sequence);
2704        }
2705        if(readIf("SELECTIVITY")) {
2706            int sel = getPositiveInt();
2707            column.setSelectivity(sel);
2708        }
2709        column.setComment(readCommentIf());
2710        return column;
2711    }
2712    
2713    private String JavaDoc readCommentIf() throws SQLException JavaDoc {
2714        if(readIf("COMMENT")) {
2715            readIf("IS");
2716            return readString();
2717        }
2718        return null;
2719    }
2720
2721    private Column parseColumn(String JavaDoc columnName) throws SQLException JavaDoc {
2722        String JavaDoc original = currentToken;
2723        boolean regular = false;
2724        if(readIf("LONG")) {
2725            if(readIf("RAW")) {
2726                original += " RAW";
2727            }
2728        } else if(readIf("DOUBLE")) {
2729            if(readIf("PRECISION")) {
2730                original += " PRECISION";
2731            }
2732        } else {
2733            regular = true;
2734        }
2735        DataType dataType = DataType.getTypeByName(original);
2736        long precision = -1;
2737        int scale = -1;
2738        Column templateColumn = null;
2739        if(dataType==null) {
2740            UserDataType userDataType = database.findUserDataType(original);
2741            if(userDataType == null) {
2742                throw Message.getSQLException(Message.UNKNOWN_DATA_TYPE_1, currentToken);
2743            } else {
2744                templateColumn = userDataType.getColumn();
2745                dataType = DataType.getDataType(templateColumn.getType());
2746                original = templateColumn.getOriginalSQL();
2747                precision = templateColumn.getPrecision();
2748                scale = templateColumn.getScale();
2749            }
2750        }
2751        if(database.getIgnoreCase() && dataType.type == Value.STRING && !"VARCHAR_CASESENSITIVE".equals(original)) {
2752            original = "VARCHAR_IGNORECASE";
2753            dataType = DataType.getTypeByName(original);
2754        }
2755        if(regular) {
2756            read();
2757        }
2758        precision = precision == -1 ? dataType.defaultPrecision : precision;
2759        scale = scale == -1 ? dataType.defaultScale : scale;
2760        if(dataType.supportsPrecision || dataType.supportsScale) {
2761            if(readIf("(")) {
2762                precision = getPositiveInt();
2763                if(readIf("K")) {
2764                    precision *= 1024;
2765                } else if(readIf("M")) {
2766                    precision *= 1024 * 1024;
2767                } else if(readIf("G")) {
2768                    precision *= 1024 * 1024 * 1024;
2769                }
2770                if(precision > Integer.MAX_VALUE) {
2771                    precision = Integer.MAX_VALUE;
2772                }
2773                original += "(" + precision;
2774                // oracle syntax
2775
readIf("CHAR");
2776                if(dataType.supportsScale) {
2777                    if(readIf(",")) {
2778                        scale = getPositiveInt();
2779                        original += ", " + scale;
2780                    } else {
2781                        scale = 0;
2782                    }
2783                }
2784                original += ")";
2785                read(")");
2786            }
2787        } else if(readIf("(")) {
2788            // support for MySQL: INT(11), MEDIUMINT(8) and so on. Just ignore the precision.
2789
getPositiveInt();
2790            read(")");
2791        }
2792        if(readIf("FOR")) {
2793            read("BIT");
2794            read("DATA");
2795            if(dataType.type == Value.STRING) {
2796                dataType = DataType.getTypeByName("BINARY");
2797            }
2798        }
2799        int type = dataType.type;
2800        Column column = new Column(columnName, type, precision, scale);
2801        if(templateColumn != null) {
2802            column.setNullable(templateColumn.getNullable());
2803            column.setDefaultExpression(session, templateColumn.getDefaultExpression());
2804            int selectivity = templateColumn.getSelectivity();
2805            if(selectivity != Constants.SELECTIVITY_DEFAULT) {
2806                column.setSelectivity(selectivity);
2807            }
2808            Expression checkConstraint = templateColumn.getCheckConstraint(session, columnName);
2809            if(checkConstraint != null) {
2810                column.addCheckConstraint(session, checkConstraint);
2811            }
2812        }
2813        column.setOriginalSQL(original);
2814        return column;
2815    }
2816
2817    private Prepared parseCreate() throws SQLException JavaDoc {
2818        boolean force = readIf("FORCE");
2819        if(readIf("LOCAL")) {
2820            read("TEMPORARY");
2821            read("TABLE");
2822            return parseCreateTable(true, false, false);
2823        } else if(readIf("GLOBAL")) {
2824            read("TEMPORARY");
2825            read("TABLE");
2826            return parseCreateTable(true, true, false);
2827        } else if(readIf("TEMP") || readIf("TEMPORARY")) {
2828            read("TABLE");
2829            return parseCreateTable(true, true, false);
2830        } else if (readIf("MEMORY")) {
2831            read("TABLE");
2832            return parseCreateTable(false, false, false);
2833        } else if(readIf("LINKED")) {
2834            return parseCreateLinkedTable();
2835        } else if (readIf("CACHED")) {
2836            read("TABLE");
2837            return parseCreateTable(false, false, true);
2838        } else if (readIf("TABLE")) {
2839            int defaultMode;
2840            Setting setting = database.findSetting(SetTypes.getTypeName(SetTypes.DEFAULT_TABLE_TYPE));
2841            defaultMode = setting == null ? Constants.DEFAULT_TABLE_TYPE : setting.getIntValue();
2842            return parseCreateTable(false, false, defaultMode==Table.TYPE_CACHED);
2843        } else if(readIf("VIEW")) {
2844            return parseCreateView(force);
2845        } else if (readIf("ALIAS")) {
2846             return parseCreateFunctionAlias();
2847        } else if (readIf("SEQUENCE")) {
2848            return parseCreateSequence();
2849        } else if (readIf("USER")) {
2850            return parseCreateUser();
2851        } else if (readIf("TRIGGER")) {
2852            return parseCreateTrigger();
2853        } else if (readIf("ROLE")) {
2854            return parseCreateRole();
2855        } else if(readIf("SCHEMA")) {
2856            return parseCreateSchema();
2857        } else if(readIf("CONSTANT")) {
2858            return parseCreateConstant();
2859        } else if(readIf("DOMAIN")) {
2860            return parseCreateUserDataType();
2861        } else if(readIf("TYPE")) {
2862            return parseCreateUserDataType();
2863        } else if(readIf("DATATYPE")) {
2864            return parseCreateUserDataType();
2865        } else {
2866            boolean hash = false, primaryKey = false, unique = false;
2867            String JavaDoc indexName = null;
2868            Schema oldSchema = null;
2869            boolean ifNotExists = false;
2870            if(readIf("PRIMARY")) {
2871                read("KEY");
2872                if(readIf("HASH")) {
2873                    hash = true;
2874                }
2875                primaryKey = true;
2876            } else {
2877                if (readIf("UNIQUE")) {
2878                    unique = true;
2879                    if(readIf("HASH")) {
2880                        hash = true;
2881                    }
2882                } if(readIf("INDEX")) {
2883                    if(!isToken("ON")) {
2884                        ifNotExists = readIfNoExists();
2885                        indexName = readIdentifierWithSchema(null);
2886                        oldSchema = getSchema();
2887                    }
2888                } else {
2889                    throw getSyntaxError();
2890                }
2891            }
2892            read("ON");
2893            String JavaDoc tableName = readIdentifierWithSchema();
2894            checkSchema(oldSchema);
2895            CreateIndex command = new CreateIndex(session, getSchema());
2896            command.setIfNotExists(ifNotExists);
2897            command.setHash(hash);
2898            command.setPrimaryKey(primaryKey);
2899            command.setTableName(tableName);
2900            command.setUnique(unique);
2901            command.setIndexName(indexName);
2902            command.setComment(readCommentIf());
2903            read("(");
2904            command.setColumnNames(parseColumnList(true));
2905            return command;
2906        }
2907    }
2908
2909    private boolean addRoleOrRight(GrantRevoke command) throws SQLException JavaDoc {
2910        if(readIf("SELECT")) {
2911            command.addRight(Right.SELECT);
2912            return false;
2913        } else if(readIf("DELETE")) {
2914            command.addRight(Right.DELETE);
2915            return false;
2916        } else if(readIf("INSERT")) {
2917            command.addRight(Right.INSERT);
2918            return false;
2919        } else if(readIf("UPDATE")) {
2920            command.addRight(Right.UPDATE);
2921            return false;
2922        } else if(readIf("ALL")) {
2923            command.addRight(Right.ALL);
2924            return false;
2925        } else if(readIf("CONNECT")) {
2926            // ignore this right
2927
return false;
2928        } else if(readIf("RESOURCE")) {
2929            // ignore this right
2930
return false;
2931        } else {
2932            command.addRoleName(readUniqueIdentifier());
2933            return true;
2934        }
2935    }
2936
2937    private GrantRevoke parseGrantRevoke(int operationType) throws SQLException JavaDoc {
2938        GrantRevoke command = new GrantRevoke(session);
2939        command.setOperationType(operationType);
2940        boolean isRoleBased = addRoleOrRight(command);
2941        while(readIf(",")) {
2942            boolean next = addRoleOrRight(command);
2943            if(next != isRoleBased) {
2944                throw Message.getSQLException(Message.ROLES_AND_RIGHT_CANNOT_BE_MIXED);
2945            }
2946        }
2947        if(!isRoleBased) {
2948            if(readIf("ON")) {
2949                do {
2950                    String JavaDoc tableName = readIdentifierWithSchema();
2951                    Table table = getSchema().getTableOrView(session, tableName);
2952                    command.addTable(table);
2953                } while(readIf(","));
2954            }
2955        }
2956        if(operationType == GrantRevoke.GRANT) {
2957            read("TO");
2958        } else {
2959            read("FROM");
2960        }
2961        command.setGranteeName(readUniqueIdentifier());
2962        return command;
2963    }
2964
2965    private Call parserCall() throws SQLException JavaDoc {
2966        Call command = new Call(session);
2967        currentPrepared = command;
2968        command.setValue(readExpression());
2969        return command;
2970    }
2971
2972    private CreateRole parseCreateRole() throws SQLException JavaDoc {
2973        CreateRole command = new CreateRole(session);
2974        command.setIfNotExists(readIfNoExists());
2975        command.setRoleName(readUniqueIdentifier());
2976        return command;
2977    }
2978
2979    private CreateSchema parseCreateSchema() throws SQLException JavaDoc {
2980        CreateSchema command = new CreateSchema(session);
2981        command.setIfNotExists(readIfNoExists());
2982        command.setSchemaName(readUniqueIdentifier());
2983        if(readIf("AUTHORIZATION")) {
2984            command.setAuthorization(readUniqueIdentifier());
2985        } else {
2986            command.setAuthorization(session.getUser().getName());
2987        }
2988        return command;
2989    }
2990
2991    private CreateSequence parseCreateSequence() throws SQLException JavaDoc {
2992        boolean ifNotExists = readIfNoExists();
2993        String JavaDoc sequenceName = readIdentifierWithSchema();
2994        CreateSequence command = new CreateSequence(session, getSchema());
2995        command.setIfNotExists(ifNotExists);
2996        command.setSequenceName(sequenceName);
2997        if(readIf("START")) {
2998            read("WITH");
2999            long start = readLong();
3000            command.setStartWith(start);
3001        }
3002        if(readIf("INCREMENT")) {
3003            read("BY");
3004            long increment = readLong();
3005            command.setIncrement(increment);
3006        }
3007        if(readIf("BELONGS_TO_TABLE")) {
3008            command.setBelongsToTable(true);
3009        }
3010        return command;
3011    }
3012
3013    private boolean readIfNoExists() throws SQLException JavaDoc {
3014        if(readIf("IF")) {
3015            read("NOT");
3016            read("EXISTS");
3017            return true;
3018        }
3019        return false;
3020    }
3021
3022    private CreateConstant parseCreateConstant() throws SQLException JavaDoc {
3023        boolean ifNotExists = readIfNoExists();
3024        String JavaDoc constantName = readIdentifierWithSchema();
3025        Schema schema = getSchema();
3026        read("VALUE");
3027        Expression expr = readExpression();
3028        CreateConstant command = new CreateConstant(session, schema);
3029        command.setConstantName(constantName);
3030        command.setExpression(expr);
3031        command.setIfNotExists(ifNotExists);
3032        return command;
3033    }
3034
3035    private CreateUserDataType parseCreateUserDataType() throws SQLException JavaDoc {
3036        boolean ifNotExists = readIfNoExists();
3037        CreateUserDataType command = new CreateUserDataType(session);
3038        command.setTypeName(readUniqueIdentifier());
3039        read("AS");
3040        Column col = parseColumnForTable("VALUE");
3041        if(readIf("CHECK")) {
3042            Expression expr = readExpression();
3043            col.addCheckConstraint(session, expr);
3044        }
3045        col.rename(null);
3046        command.setColumn(col);
3047        command.setIfNotExists(ifNotExists);
3048        return command;
3049    }
3050
3051    private CreateTrigger parseCreateTrigger() throws SQLException JavaDoc {
3052        boolean ifNotExists = readIfNoExists();
3053        String JavaDoc triggerName = readIdentifierWithSchema(null);
3054        Schema schema = getSchema();
3055        boolean isBefore;
3056        if(readIf("BEFORE")) {
3057            isBefore = true;
3058        } else {
3059            read("AFTER");
3060            isBefore = false;
3061        }
3062        int typeMask = 0;
3063        do {
3064            if(readIf("INSERT")) {
3065                typeMask |= TriggerObject.INSERT;
3066            } else if(readIf("UPDATE")) {
3067                typeMask |= TriggerObject.UPDATE;
3068            } else if(readIf("DELETE")) {
3069                typeMask |= TriggerObject.DELETE;
3070            } else {
3071                throw getSyntaxError();
3072            }
3073        } while(readIf(","));
3074        read("ON");
3075        String JavaDoc tableName = readIdentifierWithSchema();
3076        checkSchema(schema);
3077        CreateTrigger command = new CreateTrigger(session, getSchema());
3078        command.setTriggerName(triggerName);
3079        command.setIfNotExists(ifNotExists);
3080        command.setBefore(isBefore);
3081        command.setTypeMask(typeMask);
3082        command.setTableName(tableName);
3083        if(readIf("FOR")) {
3084            read("EACH");
3085            read("ROW");
3086            command.setRowBased(true);
3087        } else {
3088            command.setRowBased(false);
3089        }
3090        if(readIf("QUEUE")) {
3091            command.setQueueSize(getPositiveInt());
3092        }
3093        command.setNoWait(readIf("NOWAIT"));
3094        read("CALL");
3095        command.setTriggerClassName(readUniqueIdentifier());
3096        return command;
3097    }
3098
3099    private CreateUser parseCreateUser() throws SQLException JavaDoc {
3100        CreateUser command = new CreateUser(session);
3101        command.setIfNotExists(readIfNoExists());
3102        command.setUserName(readUniqueIdentifier());
3103        command.setComment(readCommentIf());
3104        if(readIf("PASSWORD")) {
3105            command.setPassword(readString());
3106        } else if(readIf("SALT")) {
3107            command.setSalt(readString());
3108            read("HASH");
3109            command.setHash(readString());
3110        } else if(readIf("IDENTIFIED")) {
3111            read("BY");
3112            // uppercase if not quoted
3113
command.setPassword(readColumnIdentifier());
3114        } else {
3115            throw getSyntaxError();
3116        }
3117        if(readIf("ADMIN")) {
3118            command.setAdmin(true);
3119        }
3120        return command;
3121    }
3122
3123    private CreateFunctionAlias parseCreateFunctionAlias() throws SQLException JavaDoc {
3124        boolean ifNotExists = readIfNoExists();
3125        CreateFunctionAlias command = new CreateFunctionAlias(session);
3126        command.setAliasName(readUniqueIdentifier());
3127        command.setIfNotExists(ifNotExists);
3128        read("FOR");
3129        command.setJavaClassMethod(readUniqueIdentifier());
3130        return command;
3131    }
3132
3133    private CreateView parseCreateView(boolean force) throws SQLException JavaDoc {
3134        boolean ifNotExists = readIfNoExists();
3135        String JavaDoc viewName = readIdentifierWithSchema();
3136        CreateView command = new CreateView(session, getSchema());
3137        command.setViewName(viewName);
3138        command.setIfNotExists(ifNotExists);
3139        String JavaDoc select = StringCache.getNew(sqlCommand.substring(parseIndex));
3140        command.setComment(readCommentIf());
3141        if(readIf("(")) {
3142            String JavaDoc[] cols = parseColumnList(false);
3143            command.setColumnNames(cols);
3144        }
3145        read("AS");
3146        try {
3147            Query query = parseSelect();
3148            query.prepare();
3149            command.setSelect(query);
3150        } catch(SQLException JavaDoc e) {
3151            if(force) {
3152                command.setSelectSQL(select);
3153            } else {
3154                throw e;
3155            }
3156        }
3157        return command;
3158    }
3159
3160    private TransactionCommand parseCheckpoint() throws SQLException JavaDoc {
3161        TransactionCommand command;
3162        if(readIf("SYNC")) {
3163            command = new TransactionCommand(session, TransactionCommand.CHECKPOINT_SYNC);
3164        } else {
3165            command = new TransactionCommand(session, TransactionCommand.CHECKPOINT);
3166        }
3167        return command;
3168    }
3169
3170    private Prepared parseAlter() throws SQLException JavaDoc {
3171        if(readIf("TABLE")) {
3172            return parseAlterTable();
3173        } else if(readIf("USER")) {
3174            return parseAlterUser();
3175        } else if(readIf("INDEX")) {
3176            return parseAlterIndex();
3177        } else if(readIf("SEQUENCE")) {
3178            return parseAlterSequence();
3179        } else if(readIf("VIEW")) {
3180            return parseAlterView();
3181        }
3182        throw getSyntaxError();
3183    }
3184
3185    private void checkSchema(Schema old) throws SQLException JavaDoc {
3186        if(old != null && getSchema() != old) {
3187            throw Message.getSQLException(Message.SCHEMA_NAME_MUST_MATCH);
3188        }
3189    }
3190
3191    private AlterIndexRename parseAlterIndex() throws SQLException JavaDoc {
3192        String JavaDoc indexName = readIdentifierWithSchema();
3193        Schema old = getSchema();
3194        AlterIndexRename command = new AlterIndexRename(session, getSchema());
3195        command.setOldIndex(getSchema().getIndex(indexName));
3196        read("RENAME");
3197        read("TO");
3198        String JavaDoc newName = readIdentifierWithSchema(old.getSQL());
3199        checkSchema(old);
3200        command.setNewName(newName);
3201        return command;
3202    }
3203
3204    private AlterView parseAlterView() throws SQLException JavaDoc {
3205        AlterView command = new AlterView(session);
3206        String JavaDoc viewName = readIdentifierWithSchema();
3207        Table tableView = getSchema().findTableOrView(session, viewName);
3208        if(!(tableView instanceof TableView)) {
3209            throw Message.getSQLException(Message.VIEW_NOT_FOUND_1, viewName);
3210        }
3211        TableView view = (TableView)tableView;
3212        command.setView(view);
3213        read("RECOMPILE");
3214        return command;
3215    }
3216
3217    private AlterSequence parseAlterSequence() throws SQLException JavaDoc {
3218        AlterSequence command = new AlterSequence(session);
3219        String JavaDoc sequenceName = readIdentifierWithSchema();
3220        command.setSequence(getSchema().getSequence(sequenceName));
3221        if(readIf("RESTART")) {
3222            read("WITH");
3223            long start = readLong();
3224            command.setStartWith(start);
3225        }
3226        if(readIf("INCREMENT")) {
3227            read("BY");
3228            long increment = readLong();
3229            command.setIncrement(increment);
3230        }
3231        return command;
3232    }
3233
3234    private AlterUser parseAlterUser() throws SQLException JavaDoc {
3235        String JavaDoc userName = readUniqueIdentifier();
3236        if(readIf("SET")) {
3237            AlterUser command = new AlterUser(session);
3238            command.setType(AlterUser.SET_PASSWORD);
3239            User user = database.getUser(userName);
3240            command.setUser(user);
3241            if(readIf("PASSWORD")) {
3242                command.setPassword(readString());
3243            } else if(readIf("SALT")) {
3244                command.setSalt(readString());
3245                read("HASH");
3246                command.setHash(readString());
3247            } else {
3248                throw getSyntaxError();
3249            }
3250            return command;
3251        } else if(readIf("RENAME")) {
3252            read("TO");
3253            AlterUser command = new AlterUser(session);
3254            command.setType(AlterUser.RENAME);
3255            command.setUser(database.getUser(userName));
3256            String JavaDoc newName = readUniqueIdentifier();
3257            command.setNewName(newName);
3258            return command;
3259        } else if(readIf("ADMIN")) {
3260            AlterUser command = new AlterUser(session);
3261            command.setType(AlterUser.ADMIN);
3262            User user = database.getUser(userName);
3263            command.setUser(user);
3264            if(readIf("TRUE")) {
3265                command.setAdmin(true);
3266            } else if(readIf("FALSE")) {
3267                command.setAdmin(false);
3268            } else {
3269                throw getSyntaxError();
3270            }
3271            return command;
3272        }
3273        throw getSyntaxError();
3274    }
3275
3276    private Prepared parseSet() throws SQLException JavaDoc {
3277        if(readIf("AUTOCOMMIT")) {
3278            boolean value = readBooleanSetting();
3279            int setting = value ? TransactionCommand.AUTOCOMMIT_TRUE : TransactionCommand.AUTOCOMMIT_FALSE;
3280            return new TransactionCommand(session, setting);
3281        } else if(readIf("IGNORECASE")) {
3282            boolean value = readBooleanSetting();
3283            Set command = new Set(session, SetTypes.IGNORECASE);
3284            command.setInt(value ? 1 : 0);
3285            return command;
3286        } else if(readIf("PASSWORD")) {
3287            AlterUser command = new AlterUser(session);
3288            command.setType(AlterUser.SET_PASSWORD);
3289            command.setUser(session.getUser());
3290            command.setPassword(readString());
3291            return command;
3292        } else if(readIf("SALT")) {
3293            AlterUser command = new AlterUser(session);
3294            command.setType(AlterUser.SET_PASSWORD);
3295            command.setUser(session.getUser());
3296            command.setSalt(readString());
3297            read("HASH");
3298            command.setHash(readString());
3299            return command;
3300        } else if(readIf("MODE")) {
3301            Set command = new Set(session, SetTypes.MODE);
3302            command.setString(readAliasIdentifier());
3303            return command;
3304        } else if(readIf("COMPRESS_LOB")) {
3305            Set command = new Set(session, SetTypes.COMPRESS_LOB);
3306            if(currentTokenType == VALUE) {
3307                command.setString(readString());
3308            } else {
3309                command.setString(readUniqueIdentifier());
3310            }
3311            return command;
3312        } else if(readIf("DATABASE")) {
3313            read("COLLATION");
3314            return parseSetCollation();
3315        } else if(readIf("COLLATION")) {
3316            return parseSetCollation();
3317        } else if(readIf("CLUSTER")) {
3318            Set command = new Set(session, SetTypes.CLUSTER);
3319            command.setString(readString());
3320            return command;
3321        } else if(readIf("DATABASE_EVENT_LISTENER")) {
3322            Set command = new Set(session, SetTypes.DATABASE_EVENT_LISTENER);
3323            command.setString(readString());
3324            return command;
3325        } else if(readIf("ALLOW_LITERALS")) {
3326            Set command = new Set(session, SetTypes.ALLOW_LITERALS);
3327            if(readIf("NONE")) {
3328                command.setInt(Constants.ALLOW_LITERALS_NONE);
3329            } else if(readIf("ALL")) {
3330                command.setInt(Constants.ALLOW_LITERALS_ALL);
3331            } else if(readIf("NUMBERS")){
3332                command.setInt(Constants.ALLOW_LITERALS_NUMBERS);
3333            } else {
3334                command.setInt(getPositiveInt());
3335            }
3336            return command;
3337        } else if(readIf("DEFAULT_TABLE_TYPE")) {
3338            Set command = new Set(session, SetTypes.DEFAULT_TABLE_TYPE);
3339            if(readIf("MEMORY")) {
3340                command.setInt(Table.TYPE_MEMORY);
3341            } else if(readIf("CACHED")) {
3342                command.setInt(Table.TYPE_CACHED);
3343            } else {
3344                command.setInt(getPositiveInt());
3345            }
3346            return command;
3347        } else if(readIf("CREATE")) {
3348            // Derby compatibility (CREATE=TRUE in the database URL)
3349
read();
3350            return new NoOperation(session);
3351        } else if(readIf("HSQLDB.DEFAULT_TABLE_TYPE")) {
3352            read();
3353            return new NoOperation(session);
3354        } else if(readIf("CACHE_TYPE")) {
3355            read();
3356            return new NoOperation(session);
3357        } else if(readIf("FILE_LOCK")) {
3358            read();
3359            return new NoOperation(session);
3360        } else if(readIf("STORAGE")) {
3361            read();
3362            return new NoOperation(session);
3363        } else if(readIf("DB_CLOSE_ON_EXIT")) {
3364            read();
3365            return new NoOperation(session);
3366        } else if(readIf("RECOVER")) {
3367            read();
3368            return new NoOperation(session);
3369        } else if(readIf("SCHEMA")) {
3370            Set command = new Set(session, SetTypes.SCHEMA);
3371            command.setString(readAliasIdentifier());
3372            return command;
3373        } else {
3374            if(isToken("LOGSIZE")) {
3375                // HSQLDB compatibility
3376
currentToken = SetTypes.getTypeName(SetTypes.MAX_LOG_SIZE);
3377            }
3378            int type = SetTypes.getType(currentToken);
3379            if(type >= 0) {
3380                read();
3381                Set command = new Set(session, type);
3382                command.setExpression(readExpression());
3383                return command;
3384            } else {
3385                throw getSyntaxError();
3386            }
3387        }
3388    }
3389
3390    private Set parseSetCollation() throws SQLException JavaDoc {
3391        Set command = new Set(session, SetTypes.COLLATION);
3392        String JavaDoc name = readAliasIdentifier();
3393        command.setString(name);
3394        if(name.equals(CompareMode.OFF)) {
3395            return command;
3396        }
3397        Collator JavaDoc coll = CompareMode.getCollator(name);
3398        if(coll == null) {
3399            throw getSyntaxError();
3400        }
3401        if(readIf("STRENGTH")) {
3402            if(readIf("PRIMARY")) {
3403                command.setInt(Collator.PRIMARY);
3404            } else if(readIf("SECONDARY")) {
3405                command.setInt(Collator.SECONDARY);
3406            } else if(readIf("TERTIARY")) {
3407                command.setInt(Collator.TERTIARY);
3408            } else if(readIf("IDENTICAL")) {
3409                command.setInt(Collator.IDENTICAL);
3410            }
3411        } else {
3412            command.setInt(coll.getStrength());
3413        }
3414        return command;
3415    }
3416
3417    private RunScript parseRunScript() throws SQLException JavaDoc {
3418        RunScript command = new RunScript(session);
3419        read("FROM");
3420        command.setFileName(readString());
3421        if(readIf("COMPRESSION")) {
3422            command.setCompressionAlgorithm(readUniqueIdentifier());
3423        }
3424        if(readIf("CIPHER")) {
3425            command.setCipher(readUniqueIdentifier());
3426            if(readIf("PASSWORD")) {
3427                command.setPassword(readString().toCharArray());
3428            }
3429        }
3430        if(readIf("CHARSET")) {
3431            command.setCharset(readString());
3432        }
3433        return command;
3434    }
3435
3436    private Script parseScript() throws SQLException JavaDoc {
3437        Script command = new Script(session);
3438        boolean data = true, passwords = true, settings = true, dropTables = false;
3439        if(readIf("NODATA")) {
3440            data = false;
3441        }
3442        if(readIf("NOPASSWORDS")) {
3443            passwords = false;
3444        }
3445        if(readIf("NOSETTINGS")) {
3446            settings = false;
3447        }
3448        if(readIf("DROP")) {
3449            dropTables = true;
3450        }
3451        if(readIf("BLOCKSIZE")) {
3452            long blockSize = readLong();
3453            command.setLobBlockSize(blockSize);
3454        }
3455        command.setData(data);
3456        command.setPasswords(passwords);
3457        command.setSettings(settings);
3458        command.setDrop(dropTables);
3459        if(readIf("TO")) {
3460            command.setFileName(readString());
3461            if(readIf("COMPRESSION")) {
3462                command.setCompressionAlgorithm(readUniqueIdentifier());
3463            }
3464            if(readIf("CIPHER")) {
3465                command.setCipher(readUniqueIdentifier());
3466                if(readIf("PASSWORD")) {
3467                    command.setPassword(readString().toCharArray());
3468                }
3469            }
3470        }
3471        return command;
3472    }
3473
3474    private Prepared parseAlterTable() throws SQLException JavaDoc {
3475        String JavaDoc tableName = readIdentifierWithSchema();
3476        Schema tableSchema = getSchema();
3477        Table table = getSchema().getTableOrView(session, tableName);
3478        if(readIf("ADD")) {
3479            Prepared command = parseAlterTableAddConstraintIf(getSchema(), tableName);
3480            if(command != null) {
3481                return command;
3482            }
3483            return parseAlterTableAddColumn(table);
3484        } else if(readIf("SET")) {
3485            read("REFERENTIAL_INTEGRITY");
3486            int type;
3487            if(readIf("TRUE")) {
3488                type = AlterTableAddConstraint.REFERENTIAL_INTEGRITY_TRUE;
3489            } else {
3490                read("FALSE");
3491                type = AlterTableAddConstraint.REFERENTIAL_INTEGRITY_FALSE;
3492            }
3493            AlterTableAddConstraint command = new AlterTableAddConstraint(session, getSchema());
3494            command.setTableName(tableName);
3495            command.setType(type);
3496            return command;
3497        } else if(readIf("RENAME")) {
3498            read("TO");
3499            String JavaDoc newName = readIdentifierWithSchema(tableSchema.getSQL());
3500            checkSchema(tableSchema);
3501            AlterTableRename command = new AlterTableRename(session, getSchema());
3502            command.setOldTable(table);
3503            command.setNewTableName(newName);
3504            return command;
3505        } else if(readIf("DROP")) {
3506            if(readIf("CONSTRAINT")) {
3507                String JavaDoc constraintName = readIdentifierWithSchema(tableSchema.getSQL());
3508                checkSchema(tableSchema);
3509                AlterTableDropConstraint command = new AlterTableDropConstraint(session, getSchema());
3510                command.setConstraintName(constraintName);
3511                return command;
3512            } else if(readIf("PRIMARY")) {
3513                read("KEY");
3514                Index idx = table.getPrimaryKey();
3515                DropIndex command = new DropIndex(session, tableSchema);
3516                command.setIndexName(idx.getName());
3517                return command;
3518            } else {
3519                readIf("COLUMN");
3520                AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3521                command.setType(AlterTableAlterColumn.DROP);
3522                String JavaDoc columnName = readColumnIdentifier();
3523                command.setTable(table);
3524                command.setOldColumn(table.getColumn(columnName));
3525                return command;
3526            }
3527        } else if(readIf("ALTER")) {
3528            readIf("COLUMN");
3529            String JavaDoc columnName = readColumnIdentifier();
3530            Column column = table.getColumn(columnName);
3531            if(readIf("RENAME")) {
3532                read("TO");
3533                AlterTableRenameColumn command = new AlterTableRenameColumn(session);
3534                command.setTable(table);
3535                command.setColumn(column);
3536                String JavaDoc newName = readColumnIdentifier();
3537                command.setNewColumnName(newName);
3538                return command;
3539            } else if(readIf("SET")) {
3540                if(readIf("DATA")) {
3541                    // Derby compatibility
3542
read("TYPE");
3543                    Column newColumn = parseColumnForTable(columnName);
3544                    AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3545                    command.setTable(table);
3546                    command.setType(AlterTableAlterColumn.CHANGE_TYPE);
3547                    command.setOldColumn(column);
3548                    command.setNewColumn(newColumn);
3549                    return command;
3550                }
3551                AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3552                command.setTable(table);
3553                command.setOldColumn(column);
3554                if(readIf("NULL")) {
3555                    command.setType(AlterTableAlterColumn.NULL);
3556                    return command;
3557                } else if(readIf("NOT")) {
3558                    read("NULL");
3559                    command.setType(AlterTableAlterColumn.NOT_NULL);
3560                    return command;
3561                } else if(readIf("DEFAULT")) {
3562                    Expression defaultExpression = readExpression();
3563                    command.setType(AlterTableAlterColumn.DEFAULT);
3564                    command.setDefaultExpression(defaultExpression);
3565                    return command;
3566                }
3567            } else if(readIf("RESTART")) {
3568                readIf("WITH");
3569                long start = readLong();
3570                AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3571                command.setTable(table);
3572                command.setType(AlterTableAlterColumn.RESTART);
3573                command.setOldColumn(column);
3574                command.setStartWith(start);
3575                return command;
3576            } else if(readIf("SELECTIVITY")) {
3577                int sel = getPositiveInt();
3578                AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3579                command.setTable(table);
3580                command.setType(AlterTableAlterColumn.SELECTIVITY);
3581                command.setOldColumn(column);
3582                command.setStartWith(sel);
3583                return command;
3584            } else {
3585                Column newColumn = parseColumnForTable(columnName);
3586                AlterTableAlterColumn command = new AlterTableAlterColumn(session, tableSchema);
3587                command.setTable(table);
3588                command.setType(AlterTableAlterColumn.CHANGE_TYPE);
3589                command.setOldColumn(column);
3590                command.setNewColumn(newColumn);
3591                return command;
3592            }
3593        }
3594        throw getSyntaxError();
3595    }
3596
3597    private AlterTableAlterColumn parseAlterTableAddColumn(Table table) throws SQLException JavaDoc {
3598        readIf("COLUMN");
3599        Schema schema = table.getSchema();
3600        AlterTableAlterColumn command = new AlterTableAlterColumn(session, schema);
3601        command.setType(AlterTableAlterColumn.ADD);
3602        command.setTable(table);
3603        String JavaDoc columnName = readColumnIdentifier();
3604        Column column = parseColumnForTable(columnName);
3605        command.setNewColumn(column);
3606        if(readIf("BEFORE")) {
3607            command.setAddBefore(readColumnIdentifier());
3608        }
3609        return command;
3610    }
3611
3612    private int parseAction() throws SQLException JavaDoc {
3613        if(readIf("CASCADE")) {
3614            return ConstraintReferential.CASCADE;
3615        } else if(readIf("RESTRICT")) {
3616            return ConstraintReferential.RESTRICT;
3617        } else if(readIf("NO")) {
3618            read("ACTION");
3619            return ConstraintReferential.RESTRICT;
3620        } else {
3621            read("SET");
3622            if(readIf("NULL")) {
3623                return ConstraintReferential.SET_NULL;
3624            } else {
3625                read("DEFAULT");
3626                return ConstraintReferential.SET_DEFAULT;
3627            }
3628        }
3629    }
3630
3631    private Prepared parseAlterTableAddConstraintIf(Schema schema, String JavaDoc tableName) throws SQLException JavaDoc {
3632        String JavaDoc name = null, comment = null;
3633        if(readIf("CONSTRAINT")) {
3634            name = readIdentifierWithSchema(schema.getName());
3635            checkSchema(schema);
3636            comment = readCommentIf();
3637        }
3638        if(readIf("PRIMARY")) {
3639            read("KEY");
3640            CreateIndex command = new CreateIndex(session, schema);
3641            command.setComment(comment);
3642            command.setTableName(tableName);
3643            command.setPrimaryKey(true);
3644            if(readIf("HASH")) {
3645                command.setHash(true);
3646            }
3647            read("(");
3648            command.setColumnNames(parseColumnList(true));
3649            return command;
3650        } else if(Mode.getCurrentMode().indexDefinitionInCreateTable && (readIf("INDEX") || readIf("KEY"))) {
3651            // MySQL
3652
CreateIndex command = new CreateIndex(session, schema);
3653            command.setComment(comment);
3654            command.setTableName(tableName);
3655            if(!readIf("(")) {
3656                command.setIndexName(readUniqueIdentifier());
3657                read("(");
3658            }
3659            command.setColumnNames(parseColumnList(true));
3660            return command;
3661        }
3662        AlterTableAddConstraint command;
3663        if(readIf("CHECK")) {
3664            command = new AlterTableAddConstraint(session, schema);
3665            command.setType(AlterTableAddConstraint.CHECK);
3666            command.setCheckExpression(readExpression());
3667        } else if(readIf("UNIQUE")) {
3668            readIf("INDEX");
3669            command = new AlterTableAddConstraint(session, schema);
3670            command.setType(AlterTableAddConstraint.UNIQUE);
3671            if(!readIf("(")) {
3672                name = readUniqueIdentifier();
3673                read("(");
3674            }
3675            command.setColumnNames(parseColumnList(true));
3676            if(readIf("INDEX")) {
3677                String JavaDoc indexName = readIdentifierWithSchema();
3678                command.setIndex(getSchema().findIndex(indexName));
3679            }
3680        } else if(readIf("FOREIGN")) {
3681            command = new AlterTableAddConstraint(session, schema);
3682            command.setType(AlterTableAddConstraint.REFERENTIAL);
3683            read("KEY");
3684            read("(");
3685            String JavaDoc[] cols = parseColumnList(true);
3686            command.setColumnNames(cols);
3687            if(readIf("INDEX")) {
3688                String JavaDoc indexName = readIdentifierWithSchema();
3689                command.setIndex(schema.findIndex(indexName));
3690            }
3691            read("REFERENCES");
3692            if(readIf("(")) {
3693                command.setRefTableName(schema, tableName);
3694                cols = parseColumnList(false);
3695                command.setRefColumnNames(cols);
3696            } else {
3697                String JavaDoc refTableName = readIdentifierWithSchema(schema.getName());
3698                command.setRefTableName(getSchema(), refTableName);
3699                if(readIf("(")) {
3700                    cols = parseColumnList(false);
3701                    command.setRefColumnNames(cols);
3702                }
3703            }
3704            if(readIf("INDEX")) {
3705                String JavaDoc indexName = readIdentifierWithSchema();
3706                command.setRefIndex(getSchema().findIndex(indexName));
3707            }
3708            while(readIf("ON")) {
3709                if(readIf("DELETE")) {
3710                    command.setDeleteAction(parseAction());
3711                } else {
3712                    read("UPDATE");
3713                    command.setUpdateAction(parseAction());
3714                }
3715            }
3716            if(readIf("NOT")) {
3717                read("DEFERRABLE");
3718            } else {
3719                readIf("DEFERRABLE");
3720            }
3721        } else {
3722            if(name != null) {
3723                throw getSyntaxError();
3724            }
3725            return null;
3726        }
3727        command.setTableName(tableName);
3728        command.setConstraintName(name);
3729        command.setComment(comment);
3730        return command;
3731    }
3732
3733    private CreateLinkedTable parseCreateLinkedTable() throws SQLException JavaDoc {
3734        read("TABLE");
3735        boolean ifNotExists = readIfNoExists();
3736        String JavaDoc tableName = readIdentifierWithSchema();
3737        CreateLinkedTable command = new CreateLinkedTable(session, getSchema());
3738        command.setIfNotExists(ifNotExists);
3739        command.setTableName(tableName);
3740        command.setComment(readCommentIf());
3741        read("(");
3742        command.setDriver(readString());
3743        read(",");
3744        command.setUrl(readString());
3745        read(",");
3746        command.setUser(readString());
3747        read(",");
3748        command.setPassword(readString());
3749        read(",");
3750        command.setOriginalTable(readString());
3751        read(")");
3752        return command;
3753    }
3754
3755    private CreateTable parseCreateTable(boolean temp, boolean globalTemp, boolean persistent) throws SQLException JavaDoc {
3756        boolean ifNotExists = readIfNoExists();
3757        String JavaDoc tableName = readIdentifierWithSchema();
3758        if(temp && globalTemp && "SESSION".equals(schemaName)) {
3759            // support weird syntax: declare global temporary table session.xy (...) not logged
3760
schemaName = session.getCurrentSchemaName();
3761            globalTemp = false;
3762        }
3763        Schema schema = getSchema();
3764        CreateTable command = new CreateTable(session, schema);
3765        command.setPersistent(persistent);
3766        command.setTemporary(temp);
3767        command.setGlobalTemporary(globalTemp);
3768        command.setIfNotExists(ifNotExists);
3769        command.setTableName(tableName);
3770        command.setComment(readCommentIf());
3771        if(readIf("AS")) {
3772            Query query = parseQueryWithParams();
3773            command.setQuery(query);
3774        } else {
3775            read("(");
3776            if(!readIf(")")) {
3777                do {
3778                    Prepared c = parseAlterTableAddConstraintIf(schema, tableName);
3779                    if(c != null) {
3780                        command.addConstraintCommand(c);
3781                    } else {
3782                        String JavaDoc columnName = readColumnIdentifier();
3783                        Column column = parseColumnForTable(columnName);
3784                        if(column.getAutoIncrement()) {
3785                            command.setPrimaryKeyColumnNames(new String JavaDoc[]{column.getName()});
3786                        }
3787                        command.addColumn(column);
3788                        String JavaDoc constraintName = null;
3789                        if(readIf("CONSTRAINT")) {
3790                            constraintName = readColumnIdentifier();
3791                        }
3792                        if (readIf("PRIMARY")) {
3793                            read("KEY");
3794                            if(readIf("HASH")) {
3795                                command.setHashPrimaryKey(true);
3796                            }
3797                            command.setPrimaryKeyColumnNames(new String JavaDoc[]{column.getName()});
3798                        } else if(readIf("UNIQUE")) {
3799                            AlterTableAddConstraint unique = new AlterTableAddConstraint(session, schema);
3800                            unique.setConstraintName(constraintName);
3801                            unique.setType(AlterTableAddConstraint.UNIQUE);
3802                            unique.setColumnNames(new String JavaDoc[]{columnName});
3803                            unique.setTableName(tableName);
3804                            command.addConstraintCommand(unique);
3805                        } else if(readIf("CHECK")) {
3806                            Expression expr = readExpression();
3807                            column.addCheckConstraint(session, expr);
3808                        } else if(readIf("REFERENCES")) {
3809                            AlterTableAddConstraint ref = new AlterTableAddConstraint(session, schema);
3810                            ref.setConstraintName(constraintName);
3811                            ref.setType(AlterTableAddConstraint.REFERENTIAL);
3812                            ref.setColumnNames(new String JavaDoc[]{columnName});
3813                            ref.setTableName(tableName);
3814                            String JavaDoc refTableName = readIdentifierWithSchema(schema.getName());
3815                            ref.setRefTableName(getSchema(), refTableName);
3816                            if(readIf("(")) {
3817                                String JavaDoc[] cols = parseColumnList(false);
3818                                ref.setRefColumnNames(cols);
3819                            }
3820                            command.addConstraintCommand(ref);
3821                        }
3822                    }
3823                } while(readIf(","));
3824                read(")");
3825            }
3826        }
3827        if(temp) {
3828            if(readIf("ON")) {
3829                read("COMMIT");
3830                if(readIf("DROP")) {
3831                    command.setOnCommitDrop();
3832                } else if(readIf("DELETE")) {
3833                    read("ROWS");
3834                    command.setOnCommitTruncate();
3835                }
3836            } else if(readIf("NOT")) {
3837                read("LOGGED");
3838            }
3839        }
3840        return command;
3841    }
3842
3843    private int getCompareType(int tokenType) {
3844        switch (tokenType) {
3845        case EQUAL:
3846            return Comparison.EQUAL;
3847        case BIGGER_EQUAL:
3848            return Comparison.BIGGER_EQUAL;
3849        case BIGGER:
3850            return Comparison.BIGGER;
3851        case SMALLER:
3852            return Comparison.SMALLER;
3853        case SMALLER_EQUAL:
3854            return Comparison.SMALLER_EQUAL;
3855        case NOT_EQUAL:
3856            return Comparison.NOT_EQUAL;
3857        default:
3858            return -1;
3859        }
3860    }
3861
3862    public static String JavaDoc quoteIdentifier(String JavaDoc s) {
3863        if(s == null || s.length()==0) {
3864            return "\"\"";
3865        }
3866        char c = s.charAt(0);
3867        // lowercase a-z is quoted as well
3868
if((!Character.isLetter(c) && c != '_') || Character.isLowerCase(c)) {
3869            return StringUtils.quoteIdentifier(s);
3870        }
3871        for(int i=0; i<s.length(); i++) {
3872            c = s.charAt(i);
3873            if((!Character.isLetterOrDigit(c) && c != '_') || Character.isLowerCase(c)) {
3874                return StringUtils.quoteIdentifier(s);
3875            }
3876        }
3877        if(Parser.isKeyword(s)) {
3878            return StringUtils.quoteIdentifier(s);
3879        }
3880        return s;
3881    }
3882
3883    public void setRightsChecked(boolean rightsChecked) {
3884        this.rightsChecked = rightsChecked;
3885    }
3886
3887    public Expression parseExpression(String JavaDoc sql) throws SQLException JavaDoc {
3888        initialize(sql);
3889        read();
3890        return readExpression();
3891    }
3892
3893}
3894
Popular Tags