Unverified 提交 47ff9145 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1164 from katzyn/parser

More fixes for parsing of MERGE USING and other changes in Parser
...@@ -45,11 +45,7 @@ SELECT * FROM (SELECT ID, COUNT(*) FROM TEST ...@@ -45,11 +45,7 @@ SELECT * FROM (SELECT ID, COUNT(*) FROM TEST
" "
"Commands (DML)","INSERT"," "Commands (DML)","INSERT","
INSERT INTO tableName INSERT INTO tableName insertColumnsAndSource
{ [ ( columnName [,...] ) ]
{ VALUES { ( { DEFAULT | expression } [,...] ) } [,...]
| [ DIRECT ] [ SORTED ] select } } |
{ SET { columnName = { DEFAULT | expression } } [,...] }
"," ","
Inserts a new row / new rows into a table. Inserts a new row / new rows into a table.
...@@ -61,9 +57,7 @@ INSERT INTO TEST VALUES(1, 'Hello') ...@@ -61,9 +57,7 @@ INSERT INTO TEST VALUES(1, 'Hello')
" "
"Commands (DML)","UPDATE"," "Commands (DML)","UPDATE","
UPDATE tableName [ [ AS ] newTableAlias ] SET UPDATE tableName [ [ AS ] newTableAlias ] SET setClauseList
{ { columnName = { DEFAULT | expression } } [,...] } |
{ ( columnName [,...] ) = ( select ) }
[ WHERE expression ] [ ORDER BY order [,...] ] [ LIMIT expression ] [ WHERE expression ] [ ORDER BY order [,...] ] [ LIMIT expression ]
"," ","
Updates data in a table. Updates data in a table.
...@@ -74,7 +68,7 @@ UPDATE PERSON P SET NAME=(SELECT A.NAME FROM ADDRESS A WHERE A.ID=P.ID); ...@@ -74,7 +68,7 @@ UPDATE PERSON P SET NAME=(SELECT A.NAME FROM ADDRESS A WHERE A.ID=P.ID);
" "
"Commands (DML)","DELETE"," "Commands (DML)","DELETE","
DELETE [ TOP term ] FROM tableName [ WHERE expression ] [ LIMIT term ] DELETE [ TOP term ] FROM tableName deleteSearchCondition
"," ","
Deletes rows form a table. Deletes rows form a table.
If TOP or LIMIT is specified, at most the specified number of rows are deleted (no limit if null or smaller than zero). If TOP or LIMIT is specified, at most the specified number of rows are deleted (no limit if null or smaller than zero).
...@@ -129,16 +123,19 @@ MERGE INTO TEST KEY(ID) VALUES(2, 'World') ...@@ -129,16 +123,19 @@ MERGE INTO TEST KEY(ID) VALUES(2, 'World')
MERGE INTO targetTableName [ [AS] targetAlias] MERGE INTO targetTableName [ [AS] targetAlias]
USING { ( select ) | sourceTableName }[ [AS] sourceAlias ] USING { ( select ) | sourceTableName }[ [AS] sourceAlias ]
ON ( expression ) ON ( expression )
[ WHEN MATCHED THEN [ update ] [ delete] ] [ WHEN MATCHED THEN
[ WHEN NOT MATCHED THEN insert ] [ UPDATE SET setClauseList ] [ DELETE deleteSearchCondition ] ]
[ WHEN NOT MATCHED THEN INSERT insertColumnsAndSource ]
"," ","
Updates or deletes existing rows, and insert rows that don't exist. The ON clause Updates or deletes existing rows, and insert rows that don't exist. The ON clause
specifies the matching column expression and must be specified. If more than one row specifies the matching column expression and must be specified. If more than one row
is updated per input row, an exception is thrown. is updated per input row, an exception is thrown.
If the source data contains duplicate rows (specifically those columns used in the If the source data contains duplicate rows (specifically those columns used in the
row matching ON clause), then an exception is thrown to prevent two updates applying row matching ON clause), then an exception is thrown to prevent two updates applying
to the same target row. The embedded update, delete or insert statements can not re-specify to the same target row.
the target table name. WHEN MATCHED THEN or WHEN NOT MATCHED THEN clauses or both of them in any order should be specified.
If WHEN MATCHED THEN is specified it should contain UPDATE or DELETE clauses of both of them.
If statement doesn't need a source table a DUAL table can be substituted.
"," ","
MERGE INTO TARGET_TABLE AS T USING SOURCE_TABLE AS S MERGE INTO TARGET_TABLE AS T USING SOURCE_TABLE AS S
ON (T.ID = S.ID) ON (T.ID = S.ID)
...@@ -154,6 +151,9 @@ MERGE INTO TARGET_TABLE AS T USING (SELECT * FROM SOURCE_TABLE) AS S ...@@ -154,6 +151,9 @@ MERGE INTO TARGET_TABLE AS T USING (SELECT * FROM SOURCE_TABLE) AS S
DELETE WHERE T.COL2='FINAL' DELETE WHERE T.COL2='FINAL'
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT (ID,COL1,COL2) VALUES(S.ID,S.COL1,S.COL2) INSERT (ID,COL1,COL2) VALUES(S.ID,S.COL1,S.COL2)
MERGE INTO TARGET_TABLE USING DUAL ON (ID = 1)
WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (1, 'Test')
WHEN MATCHED THEN UPDATE SET NAME = 'Test'
" "
"Commands (DML)","RUNSCRIPT"," "Commands (DML)","RUNSCRIPT","
...@@ -2252,6 +2252,14 @@ SELECT CAST(0 AS DOUBLE) ...@@ -2252,6 +2252,14 @@ SELECT CAST(0 AS DOUBLE)
SELECT -1.4e-10 SELECT -1.4e-10
" "
"Other Grammar","Delete search condition","
[ WHERE expression ] [ LIMIT term ]
","
Search condition for DELETE statement.
","
WHERE ID = 2
"
"Other Grammar","Digit"," "Other Grammar","Digit","
0-9 0-9
"," ","
...@@ -2313,6 +2321,17 @@ the column in the same way. ...@@ -2313,6 +2321,17 @@ the column in the same way.
NAME NAME
" "
"Other Grammar","Insert columns and source","
{ [ ( columnName [,...] ) ]
{ VALUES { ( { DEFAULT | expression } [,...] ) } [,...]
| [ DIRECT ] [ SORTED ] select } } |
{ SET { columnName = { DEFAULT | expression } } [,...] }
","
Names of columns and their values for INSERT statement.
","
(ID, NAME) VALUES (1, 'Test')
"
"Other Grammar","Int"," "Other Grammar","Int","
[ + | - ] number [ + | - ] number
"," ","
...@@ -2438,6 +2457,15 @@ An expression in a SELECT statement. ...@@ -2438,6 +2457,15 @@ An expression in a SELECT statement.
ID AS VALUE ID AS VALUE
" "
"Other Grammar","Set clause list","
{ { columnName = { DEFAULT | expression } } [,...] } |
{ ( columnName [,...] ) = ( select ) }
","
List of SET clauses.
","
NAME = 'Test', VALUE = 2
"
"Other Grammar","String"," "Other Grammar","String","
'anythingExceptSingleQuote' 'anythingExceptSingleQuote'
"," ","
......
...@@ -46,8 +46,8 @@ public abstract class Command implements CommandInterface { ...@@ -46,8 +46,8 @@ public abstract class Command implements CommandInterface {
private boolean canReuse; private boolean canReuse;
Command(Parser parser, String sql) { Command(Session session, String sql) {
this.session = parser.getSession(); this.session = session;
this.sql = sql; this.sql = sql;
trace = session.getDatabase().getTrace(Trace.COMMAND); trace = session.getDatabase().getTrace(Trace.COMMAND);
} }
......
...@@ -9,6 +9,7 @@ import java.util.ArrayList; ...@@ -9,6 +9,7 @@ import java.util.ArrayList;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.Explain; import org.h2.command.dml.Explain;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.engine.Session;
import org.h2.expression.Parameter; import org.h2.expression.Parameter;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
...@@ -26,8 +27,8 @@ public class CommandContainer extends Command { ...@@ -26,8 +27,8 @@ public class CommandContainer extends Command {
private boolean readOnlyKnown; private boolean readOnlyKnown;
private boolean readOnly; private boolean readOnly;
CommandContainer(Parser parser, String sql, Prepared prepared) { CommandContainer(Session session, String sql, Prepared prepared) {
super(parser, sql); super(session, sql);
prepared.setCommand(this); prepared.setCommand(this);
this.prepared = prepared; this.prepared = prepared;
} }
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
package org.h2.command; package org.h2.command;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.engine.Session;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
...@@ -17,8 +19,8 @@ class CommandList extends Command { ...@@ -17,8 +19,8 @@ class CommandList extends Command {
private final Command command; private final Command command;
private final String remaining; private final String remaining;
CommandList(Parser parser, String sql, Command c, String remaining) { CommandList(Session session, String sql, Command c, String remaining) {
super(parser, sql); super(session, sql);
this.command = c; this.command = c;
this.remaining = remaining; this.remaining = remaining;
} }
......
...@@ -8,6 +8,13 @@ ...@@ -8,6 +8,13 @@
*/ */
package org.h2.command; package org.h2.command;
import static org.h2.util.ParserUtil.FALSE;
import static org.h2.util.ParserUtil.IDENTIFIER;
import static org.h2.util.ParserUtil.KEYWORD;
import static org.h2.util.ParserUtil.NULL;
import static org.h2.util.ParserUtil.ROWNUM;
import static org.h2.util.ParserUtil.TRUE;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.Charset; import java.nio.charset.Charset;
...@@ -189,13 +196,7 @@ public class Parser { ...@@ -189,13 +196,7 @@ public class Parser {
private static final int CHAR_STRING = 7, CHAR_DOT = 8, private static final int CHAR_STRING = 7, CHAR_DOT = 8,
CHAR_DOLLAR_QUOTED_STRING = 9; CHAR_DOLLAR_QUOTED_STRING = 9;
// this are token types // this are token types, see also types in ParserUtil
private static final int KEYWORD = ParserUtil.KEYWORD;
private static final int IDENTIFIER = ParserUtil.IDENTIFIER;
private static final int NULL = ParserUtil.NULL;
private static final int TRUE = ParserUtil.TRUE;
private static final int FALSE = ParserUtil.FALSE;
private static final int ROWNUM = ParserUtil.ROWNUM;
private static final int PARAMETER = 10, END = 11, VALUE = 12; private static final int PARAMETER = 10, END = 11, VALUE = 12;
private static final int EQUAL = 13, BIGGER_EQUAL = 14, BIGGER = 15; private static final int EQUAL = 13, BIGGER_EQUAL = 14, BIGGER = 15;
private static final int SMALLER = 16, SMALLER_EQUAL = 17, NOT_EQUAL = 18; private static final int SMALLER = 16, SMALLER_EQUAL = 17, NOT_EQUAL = 18;
...@@ -285,11 +286,11 @@ public class Parser { ...@@ -285,11 +286,11 @@ public class Parser {
throw getSyntaxError(); throw getSyntaxError();
} }
p.prepare(); p.prepare();
Command c = new CommandContainer(this, sql, p); Command c = new CommandContainer(session, sql, p);
if (hasMore) { if (hasMore) {
String remaining = originalSQL.substring(parseIndex); String remaining = originalSQL.substring(parseIndex);
if (!StringUtils.isWhitespaceOrEmpty(remaining)) { if (!StringUtils.isWhitespaceOrEmpty(remaining)) {
c = new CommandList(this, sql, c, remaining); c = new CommandList(session, sql, c, remaining);
} }
} }
return c; return c;
...@@ -574,7 +575,7 @@ public class Parser { ...@@ -574,7 +575,7 @@ public class Parser {
command.setTable(table); command.setTable(table);
} }
if (readIf("SAMPLE_SIZE")) { if (readIf("SAMPLE_SIZE")) {
command.setTop(readPositiveInt()); command.setTop(readNonNegativeInt());
} }
return command; return command;
} }
...@@ -734,10 +735,9 @@ public class Parser { ...@@ -734,10 +735,9 @@ public class Parser {
} }
private Column readTableColumn(TableFilter filter) { private Column readTableColumn(TableFilter filter) {
String tableAlias = null;
String columnName = readColumnIdentifier(); String columnName = readColumnIdentifier();
if (readIf(".")) { if (readIf(".")) {
tableAlias = columnName; String tableAlias = columnName;
columnName = readColumnIdentifier(); columnName = readColumnIdentifier();
if (readIf(".")) { if (readIf(".")) {
String schema = tableAlias; String schema = tableAlias;
...@@ -1156,11 +1156,15 @@ public class Parser { ...@@ -1156,11 +1156,15 @@ public class Parser {
TableFilter sourceTableFilter = readSimpleTableFilter(0, excludeIdentifiers); TableFilter sourceTableFilter = readSimpleTableFilter(0, excludeIdentifiers);
command.setSourceTableFilter(sourceTableFilter); command.setSourceTableFilter(sourceTableFilter);
StringBuilder buff = new StringBuilder("SELECT * FROM "); Select preparedQuery = new Select(session);
appendTableWithSchemaAndAlias(buff, sourceTableFilter.getTable(), sourceTableFilter.getTableAlias()); ArrayList<Expression> expr = new ArrayList<>(1);
Prepared preparedQuery = prepare(session, buff.toString(), null/*paramValues*/); expr.add(new Wildcard(null, null));
command.setQuery((Select) preparedQuery); preparedQuery.setExpressions(expr);
TableFilter filter = new TableFilter(session, sourceTableFilter.getTable(),
sourceTableFilter.getTableAlias(), rightsChecked, preparedQuery, 0, null);
preparedQuery.addTableFilter(filter, true);
preparedQuery.init();
command.setQuery(preparedQuery);
} }
read("ON"); read("ON");
read("("); read("(");
...@@ -1168,9 +1172,20 @@ public class Parser { ...@@ -1168,9 +1172,20 @@ public class Parser {
command.setOnCondition(condition); command.setOnCondition(condition);
read(")"); read(")");
boolean matched = parseWhenMatched(command); read("WHEN");
if (parseWhenNotMatched(command) && !matched) { boolean matched = readIf("MATCHED");
if (matched) {
parseWhenMatched(command); parseWhenMatched(command);
} else {
parseWhenNotMatched(command);
}
if (readIf("WHEN")) {
if (matched) {
parseWhenNotMatched(command);
} else {
read("MATCHED");
parseWhenMatched(command);
}
} }
setSQL(command, "MERGE", start); setSQL(command, "MERGE", start);
...@@ -1188,17 +1203,17 @@ public class Parser { ...@@ -1188,17 +1203,17 @@ public class Parser {
return command; return command;
} }
private boolean parseWhenMatched(MergeUsing command) { private void parseWhenMatched(MergeUsing command) {
if (!readIfAll("WHEN", "MATCHED", "THEN")) { read("THEN");
return false;
}
int startMatched = lastParseIndex; int startMatched = lastParseIndex;
boolean ok = false;
if (readIf("UPDATE")) { if (readIf("UPDATE")) {
Update updateCommand = new Update(session); Update updateCommand = new Update(session);
TableFilter filter = command.getTargetTableFilter(); TableFilter filter = command.getTargetTableFilter();
updateCommand.setTableFilter(filter); updateCommand.setTableFilter(filter);
parseUpdateSetClause(updateCommand, filter, startMatched); parseUpdateSetClause(updateCommand, filter, startMatched);
command.setUpdateCommand(updateCommand); command.setUpdateCommand(updateCommand);
ok = true;
} }
startMatched = lastParseIndex; startMatched = lastParseIndex;
if (readIf("DELETE")) { if (readIf("DELETE")) {
...@@ -1207,21 +1222,25 @@ public class Parser { ...@@ -1207,21 +1222,25 @@ public class Parser {
deleteCommand.setTableFilter(filter); deleteCommand.setTableFilter(filter);
parseDeleteGivenTable(deleteCommand, null, startMatched); parseDeleteGivenTable(deleteCommand, null, startMatched);
command.setDeleteCommand(deleteCommand); command.setDeleteCommand(deleteCommand);
ok = true;
} }
return true; if (!ok) {
throw getSyntaxError();
} }
private boolean parseWhenNotMatched(MergeUsing command) {
if (!readIfAll("WHEN", "NOT", "MATCHED", "THEN")) {
return false;
} }
private void parseWhenNotMatched(MergeUsing command) {
read("NOT");
read("MATCHED");
read("THEN");
if (readIf("INSERT")) { if (readIf("INSERT")) {
Insert insertCommand = new Insert(session); Insert insertCommand = new Insert(session);
insertCommand.setTable(command.getTargetTable()); insertCommand.setTable(command.getTargetTable());
parseInsertGivenTable(insertCommand, command.getTargetTable()); parseInsertGivenTable(insertCommand, command.getTargetTable());
command.setInsertCommand(insertCommand); command.setInsertCommand(insertCommand);
} else {
throw getSyntaxError();
} }
return true;
} }
private static void appendTableWithSchemaAndAlias(StringBuilder buff, Table table, String alias) { private static void appendTableWithSchemaAndAlias(StringBuilder buff, Table table, String alias) {
...@@ -1964,9 +1983,8 @@ public class Parser { ...@@ -1964,9 +1983,8 @@ public class Parser {
} }
private Query parseSelect() { private Query parseSelect() {
Query command = null;
int paramIndex = parameters.size(); int paramIndex = parameters.size();
command = parseSelectUnion(); Query command = parseSelectUnion();
int size = parameters.size(); int size = parameters.size();
ArrayList<Parameter> params = new ArrayList<>(size); ArrayList<Parameter> params = new ArrayList<>(size);
for (int i = paramIndex; i < size; i++) { for (int i = paramIndex; i < size; i++) {
...@@ -2042,10 +2060,7 @@ public class Parser { ...@@ -2042,10 +2060,7 @@ public class Parser {
} }
ArrayList<SelectOrderBy> orderList = Utils.newSmallArrayList(); ArrayList<SelectOrderBy> orderList = Utils.newSmallArrayList();
do { do {
boolean canBeNumber = true; boolean canBeNumber = !readIf("=");
if (readIf("=")) {
canBeNumber = false;
}
SelectOrderBy order = new SelectOrderBy(); SelectOrderBy order = new SelectOrderBy();
Expression expr = readExpression(); Expression expr = readExpression();
if (canBeNumber && expr instanceof ValueExpression && if (canBeNumber && expr instanceof ValueExpression &&
...@@ -2159,7 +2174,7 @@ public class Parser { ...@@ -2159,7 +2174,7 @@ public class Parser {
return command; return command;
} }
if (readIf("WITH")) { if (readIf("WITH")) {
Query query = null; Query query;
try { try {
query = (Query) parseWith(); query = (Query) parseWith();
} catch (ClassCastException e) { } catch (ClassCastException e) {
...@@ -2691,7 +2706,7 @@ public class Parser { ...@@ -2691,7 +2706,7 @@ public class Parser {
} }
private JavaFunction readJavaFunction(Schema schema, String functionName, boolean throwIfNotFound) { private JavaFunction readJavaFunction(Schema schema, String functionName, boolean throwIfNotFound) {
FunctionAlias functionAlias = null; FunctionAlias functionAlias;
if (schema != null) { if (schema != null) {
functionAlias = schema.findFunction(functionName); functionAlias = schema.findFunction(functionName);
} else { } else {
...@@ -3403,10 +3418,10 @@ public class Parser { ...@@ -3403,10 +3418,10 @@ public class Parser {
return function; return function;
} }
private int readPositiveInt() { private int readNonNegativeInt() {
int v = readInt(); int v = readInt();
if (v < 0) { if (v < 0) {
throw DbException.getInvalidValueException("positive integer", v); throw DbException.getInvalidValueException("non-negative integer", v);
} }
return v; return v;
} }
...@@ -3452,14 +3467,21 @@ public class Parser { ...@@ -3452,14 +3467,21 @@ public class Parser {
} }
private boolean readBooleanSetting() { private boolean readBooleanSetting() {
if (currentTokenType == VALUE) { switch (currentTokenType) {
case TRUE:
read();
return true;
case FALSE:
read();
return false;
case VALUE:
boolean result = currentValue.getBoolean(); boolean result = currentValue.getBoolean();
read(); read();
return result; return result;
} }
if (readIf("TRUE") || readIf("ON")) { if (readIf("ON")) {
return true; return true;
} else if (readIf("FALSE") || readIf("OFF")) { } else if (readIf("OFF")) {
return false; return false;
} else { } else {
throw getSyntaxError(); throw getSyntaxError();
...@@ -3547,26 +3569,6 @@ public class Parser { ...@@ -3547,26 +3569,6 @@ public class Parser {
return false; return false;
} }
/*
* Reads every token in list, in order - returns true if all are found.
* If any are not found, returns false - AND resets parsing back to state when called.
*/
private boolean readIfAll(String... tokens) {
// save parse location in case we have to fail this test
int start = lastParseIndex;
for (String token: tokens) {
if (!currentTokenQuoted && equalsToken(token, currentToken)) {
read();
} else {
// read failed - revert parse location to before when called
parseIndex = start;
read();
return false;
}
}
return true;
}
private boolean isToken(String token) { private boolean isToken(String token) {
boolean result = equalsToken(token, currentToken) && boolean result = equalsToken(token, currentToken) &&
!currentTokenQuoted; !currentTokenQuoted;
...@@ -3753,12 +3755,11 @@ public class Parser { ...@@ -3753,12 +3755,11 @@ public class Parser {
return; return;
} }
case CHAR_DOLLAR_QUOTED_STRING: { case CHAR_DOLLAR_QUOTED_STRING: {
String result = null;
int begin = i - 1; int begin = i - 1;
while (types[i] == CHAR_DOLLAR_QUOTED_STRING) { while (types[i] == CHAR_DOLLAR_QUOTED_STRING) {
i++; i++;
} }
result = sqlCommand.substring(begin, i); String result = sqlCommand.substring(begin, i);
currentToken = "'"; currentToken = "'";
checkLiterals(true); checkLiterals(true);
currentValue = ValueString.get(StringUtils.cache(result), currentValue = ValueString.get(StringUtils.cache(result),
...@@ -3877,10 +3878,6 @@ public class Parser { ...@@ -3877,10 +3878,6 @@ public class Parser {
currentTokenType = VALUE; currentTokenType = VALUE;
} }
public Session getSession() {
return session;
}
private void initialize(String sql) { private void initialize(String sql) {
if (sql == null) { if (sql == null) {
sql = ""; sql = "";
...@@ -4306,7 +4303,7 @@ public class Parser { ...@@ -4306,7 +4303,7 @@ public class Parser {
column.setSequence(sequence); column.setSequence(sequence);
} }
if (readIf("SELECTIVITY")) { if (readIf("SELECTIVITY")) {
int value = readPositiveInt(); int value = readNonNegativeInt();
column.setSelectivity(value); column.setSelectivity(value);
} }
String comment = readCommentIf(); String comment = readCommentIf();
...@@ -4354,7 +4351,7 @@ public class Parser { ...@@ -4354,7 +4351,7 @@ public class Parser {
} }
} else if (readIf("TIME")) { } else if (readIf("TIME")) {
if (readIf("(")) { if (readIf("(")) {
originalScale = readPositiveInt(); originalScale = readNonNegativeInt();
if (originalScale > ValueTime.MAXIMUM_SCALE) { if (originalScale > ValueTime.MAXIMUM_SCALE) {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(originalScale)); throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(originalScale));
} }
...@@ -4367,10 +4364,10 @@ public class Parser { ...@@ -4367,10 +4364,10 @@ public class Parser {
} }
} else if (readIf("TIMESTAMP")) { } else if (readIf("TIMESTAMP")) {
if (readIf("(")) { if (readIf("(")) {
originalScale = readPositiveInt(); originalScale = readNonNegativeInt();
// Allow non-standard TIMESTAMP(..., ...) syntax // Allow non-standard TIMESTAMP(..., ...) syntax
if (readIf(",")) { if (readIf(",")) {
originalScale = readPositiveInt(); originalScale = readNonNegativeInt();
} }
if (originalScale > ValueTimestamp.MAXIMUM_SCALE) { if (originalScale > ValueTimestamp.MAXIMUM_SCALE) {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(originalScale)); throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(originalScale));
...@@ -4458,7 +4455,7 @@ public class Parser { ...@@ -4458,7 +4455,7 @@ public class Parser {
} }
} else if (original.equals("DATETIME") || original.equals("DATETIME2")) { } else if (original.equals("DATETIME") || original.equals("DATETIME2")) {
if (readIf("(")) { if (readIf("(")) {
originalScale = readPositiveInt(); originalScale = readNonNegativeInt();
if (originalScale > ValueTime.MAXIMUM_SCALE) { if (originalScale > ValueTime.MAXIMUM_SCALE) {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
Integer.toString(originalScale)); Integer.toString(originalScale));
...@@ -4506,7 +4503,7 @@ public class Parser { ...@@ -4506,7 +4503,7 @@ public class Parser {
} }
} else if (dataType.type == Value.DOUBLE && original.equals("FLOAT")) { } else if (dataType.type == Value.DOUBLE && original.equals("FLOAT")) {
if (readIf("(")) { if (readIf("(")) {
int p = readPositiveInt(); int p = readNonNegativeInt();
read(")"); read(")");
if (p > 53) { if (p > 53) {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(p)); throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION, Integer.toString(p));
...@@ -4540,7 +4537,7 @@ public class Parser { ...@@ -4540,7 +4537,7 @@ public class Parser {
} else if (readIf("(")) { } else if (readIf("(")) {
// Support for MySQL: INT(11), MEDIUMINT(8) and so on. // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
// Just ignore the precision. // Just ignore the precision.
readPositiveInt(); readNonNegativeInt();
read(")"); read(")");
} }
if (readIf("FOR")) { if (readIf("FOR")) {
...@@ -4793,7 +4790,7 @@ public class Parser { ...@@ -4793,7 +4790,7 @@ public class Parser {
Select command = new Select(session); Select command = new Select(session);
currentSelect = command; currentSelect = command;
TableFilter filter = parseValuesTable(0); TableFilter filter = parseValuesTable(0);
ArrayList<Expression> list = Utils.newSmallArrayList(); ArrayList<Expression> list = new ArrayList<>(1);
list.add(new Wildcard(null, null)); list.add(new Wildcard(null, null));
command.setExpressions(list); command.setExpressions(list);
command.addTableFilter(filter, true); command.addTableFilter(filter, true);
...@@ -5087,7 +5084,7 @@ public class Parser { ...@@ -5087,7 +5084,7 @@ public class Parser {
command.setRowBased(false); command.setRowBased(false);
} }
if (readIf("QUEUE")) { if (readIf("QUEUE")) {
command.setQueueSize(readPositiveInt()); command.setQueueSize(readNonNegativeInt());
} }
command.setNoWait(readIf("NOWAIT")); command.setNoWait(readIf("NOWAIT"));
if (readIf("AS")) { if (readIf("AS")) {
...@@ -5170,7 +5167,7 @@ public class Parser { ...@@ -5170,7 +5167,7 @@ public class Parser {
viewsCreated.add(parseSingleCommonTableExpression(isPersistent)); viewsCreated.add(parseSingleCommonTableExpression(isPersistent));
} while (readIf(",")); } while (readIf(","));
Prepared p = null; Prepared p;
// reverse the order of constructed CTE views - as the destruction order // reverse the order of constructed CTE views - as the destruction order
// (since later created view may depend on previously created views - // (since later created view may depend on previously created views -
// we preserve that dependency order in the destruction sequence ) // we preserve that dependency order in the destruction sequence )
...@@ -5218,7 +5215,6 @@ public class Parser { ...@@ -5218,7 +5215,6 @@ public class Parser {
private TableView parseSingleCommonTableExpression(boolean isPersistent) { private TableView parseSingleCommonTableExpression(boolean isPersistent) {
String cteViewName = readIdentifierWithSchema(); String cteViewName = readIdentifierWithSchema();
Schema schema = getSchema(); Schema schema = getSchema();
Table recursiveTable = null;
ArrayList<Column> columns = Utils.newSmallArrayList(); ArrayList<Column> columns = Utils.newSmallArrayList();
String[] cols = null; String[] cols = null;
...@@ -5233,7 +5229,7 @@ public class Parser { ...@@ -5233,7 +5229,7 @@ public class Parser {
} }
} }
Table oldViewFound = null; Table oldViewFound;
if (isPersistent) { if (isPersistent) {
oldViewFound = getSchema().findTableOrView(session, cteViewName); oldViewFound = getSchema().findTableOrView(session, cteViewName);
} else { } else {
...@@ -5265,7 +5261,7 @@ public class Parser { ...@@ -5265,7 +5261,7 @@ public class Parser {
* work (its removed after creation in this method). Only create table * work (its removed after creation in this method). Only create table
* data and table if we don't have a working CTE already. * data and table if we don't have a working CTE already.
*/ */
recursiveTable = TableView.createShadowTableForRecursiveTableExpression( Table recursiveTable = TableView.createShadowTableForRecursiveTableExpression(
isPersistent, session, cteViewName, schema, columns, database); isPersistent, session, cteViewName, schema, columns, database);
List<Column> columnTemplateList; List<Column> columnTemplateList;
String[] querySQLOutput = {null}; String[] querySQLOutput = {null};
...@@ -5653,7 +5649,7 @@ public class Parser { ...@@ -5653,7 +5649,7 @@ public class Parser {
} else if (readIf("NUMBERS")) { } else if (readIf("NUMBERS")) {
command.setInt(Constants.ALLOW_LITERALS_NUMBERS); command.setInt(Constants.ALLOW_LITERALS_NUMBERS);
} else { } else {
command.setInt(readPositiveInt()); command.setInt(readNonNegativeInt());
} }
return command; return command;
} else if (readIf("DEFAULT_TABLE_TYPE")) { } else if (readIf("DEFAULT_TABLE_TYPE")) {
...@@ -5664,7 +5660,7 @@ public class Parser { ...@@ -5664,7 +5660,7 @@ public class Parser {
} else if (readIf("CACHED")) { } else if (readIf("CACHED")) {
command.setInt(Table.TYPE_CACHED); command.setInt(Table.TYPE_CACHED);
} else { } else {
command.setInt(readPositiveInt()); command.setInt(readNonNegativeInt());
} }
return command; return command;
} else if (readIf("CREATE")) { } else if (readIf("CREATE")) {
...@@ -6163,7 +6159,7 @@ public class Parser { ...@@ -6163,7 +6159,7 @@ public class Parser {
// Oracle specifies (but will not require) an opening parenthesis // Oracle specifies (but will not require) an opening parenthesis
boolean hasOpeningBracket = readIf("("); boolean hasOpeningBracket = readIf("(");
String columnName = readColumnIdentifier(); String columnName = readColumnIdentifier();
AlterTableAlterColumn command = null; AlterTableAlterColumn command;
NullConstraintType nullConstraint = parseNotNullConstraint(); NullConstraintType nullConstraint = parseNotNullConstraint();
switch (nullConstraint) { switch (nullConstraint) {
case NULL_IS_ALLOWED: case NULL_IS_ALLOWED:
......
...@@ -390,31 +390,21 @@ public class MergeUsing extends Prepared { ...@@ -390,31 +390,21 @@ public class MergeUsing extends Prepared {
query.prepare(); query.prepare();
} }
int embeddedStatementsCount = 0;
// Prepare each of the sub-commands ready to aid in the MERGE // Prepare each of the sub-commands ready to aid in the MERGE
// collaboration // collaboration
if (updateCommand != null) { if (updateCommand != null) {
updateCommand.setSourceTableFilter(sourceTableFilter); updateCommand.setSourceTableFilter(sourceTableFilter);
updateCommand.setCondition(appendOnCondition(updateCommand)); updateCommand.setCondition(appendOnCondition(updateCommand));
updateCommand.prepare(); updateCommand.prepare();
embeddedStatementsCount++;
} }
if (deleteCommand != null) { if (deleteCommand != null) {
deleteCommand.setSourceTableFilter(sourceTableFilter); deleteCommand.setSourceTableFilter(sourceTableFilter);
deleteCommand.setCondition(appendOnCondition(deleteCommand)); deleteCommand.setCondition(appendOnCondition(deleteCommand));
deleteCommand.prepare(); deleteCommand.prepare();
embeddedStatementsCount++;
} }
if (insertCommand != null) { if (insertCommand != null) {
insertCommand.setSourceTableFilter(sourceTableFilter); insertCommand.setSourceTableFilter(sourceTableFilter);
insertCommand.prepare(); insertCommand.prepare();
embeddedStatementsCount++;
}
if (embeddedStatementsCount == 0) {
throw DbException.get(ErrorCode.SYNTAX_ERROR_1,
"At least UPDATE, DELETE or INSERT embedded statement must be supplied.");
} }
// setup the targetMatchQuery - for detecting if the target row exists // setup the targetMatchQuery - for detecting if the target row exists
......
...@@ -161,7 +161,7 @@ public class TestMergeUsing extends TestBase implements Trigger { ...@@ -161,7 +161,7 @@ public class TestMergeUsing extends TestBase implements Trigger {
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) WHERE X<0", "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) WHERE X<0",
0, 0,
"At least UPDATE, DELETE or INSERT embedded statement must be supplied."); "expected \"WHEN\"");
// Two updates to same row - update and delete together - emptying the // Two updates to same row - update and delete together - emptying the
// parent table // parent table
testMergeUsing( testMergeUsing(
......
...@@ -149,3 +149,29 @@ SELECT * FROM TEST ORDER BY C1, C2; ...@@ -149,3 +149,29 @@ SELECT * FROM TEST ORDER BY C1, C2;
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
CREATE TABLE TEST (ID INT, VALUE INT);
> ok
MERGE INTO TEST USING DUAL ON (ID = 1)
WHEN MATCHED THEN UPDATE SET VALUE = 1
WHEN;
> exception SYNTAX_ERROR_2
MERGE INTO TEST USING DUAL ON (ID = 1)
WHEN MATCHED THEN UPDATE SET VALUE = 1
WHEN NOT MATCHED THEN;
> exception SYNTAX_ERROR_2
MERGE INTO TEST USING DUAL ON (ID = 1)
WHEN NOT MATCHED THEN INSERT (ID, VALUE) VALUES (1, 1)
WHEN;
> exception SYNTAX_ERROR_2
MERGE INTO TEST USING DUAL ON (ID = 1)
WHEN NOT MATCHED THEN INSERT (ID, VALUE) VALUES (1, 1)
WHEN MATCHED THEN;
> exception SYNTAX_ERROR_2
DROP TABLE TEST;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论