File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\command\Parser.java.html

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