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