Unverified 提交 131734ba authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1678 from katzyn/values

Parse table value constructor in more places
...@@ -21,6 +21,10 @@ Change Log ...@@ -21,6 +21,10 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1677: Unable to use VALUES keyword in WHERE clause
</li>
<li>Issue #1672: Deadlock on MVStore close in TestOutOfMemory
</li>
<li>Issue #1665: TestCrashAPI: NPE with ENUM in MINUS operator <li>Issue #1665: TestCrashAPI: NPE with ENUM in MINUS operator
</li> </li>
<li>Issue #1602: Combine type, precision, scale, display size and extTypeInfo into one object <li>Issue #1602: Combine type, precision, scale, display size and extTypeInfo into one object
......
...@@ -415,11 +415,13 @@ To keep the content of an in-memory database as long as the virtual machine is a ...@@ -415,11 +415,13 @@ To keep the content of an in-memory database as long as the virtual machine is a
<p> <p>
The database files can be encrypted. The database files can be encrypted.
Three encryption algorithms are supported: Three encryption algorithms are supported:
</p>
<ul> <ul>
<li>"AES" - also known as Rijndael, only AES-128 is implemented.</li> <li>"AES" - also known as Rijndael, only AES-128 is implemented.</li>
<li>"XTEA" - the 32 round version.</li> <li>"XTEA" - the 32 round version.</li>
<li>"FOG" - pseudo-encryption only useful for hiding data from a text editor.</li> <li>"FOG" - pseudo-encryption only useful for hiding data from a text editor.</li>
</ul> </ul>
<p>
To use file encryption, you need to specify the encryption algorithm (the 'cipher') To use file encryption, you need to specify the encryption algorithm (the 'cipher')
and the file password (in addition to the user password) when connecting to the database. and the file password (in addition to the user password) when connecting to the database.
</p> </p>
...@@ -906,8 +908,10 @@ or the SQL statement <code>SET MODE MySQL</code>. Use this mode for compatibilit ...@@ -906,8 +908,10 @@ or the SQL statement <code>SET MODE MySQL</code>. Use this mode for compatibilit
digits are not truncated, but the value is rounded. digits are not truncated, but the value is rounded.
</li><li>Concatenating <code>NULL</code> with another value </li><li>Concatenating <code>NULL</code> with another value
results in the other value. results in the other value.
</li><li>ON DUPLICATE KEY UPDATE is supported in INSERT statements. </li><li>ON DUPLICATE KEY UPDATE is supported in INSERT statements, due to this feature VALUES has special non-standard
</li><li>INSERT IGNORE is partially supported and may be used to skip rows with duplicate keys if ON DUPLICATE KEY UPDATE is not specified. meaning is some contexts.
</li><li>INSERT IGNORE is partially supported and may be used to skip rows with duplicate keys if ON DUPLICATE KEY
UPDATE is not specified.
</li><li>REGEXP_REPLACE() uses \ for back-references for compatibility with MariaDB. </li><li>REGEXP_REPLACE() uses \ for back-references for compatibility with MariaDB.
</li><li>Datetime value functions return the same value within a command. </li><li>Datetime value functions return the same value within a command.
</li></ul> </li></ul>
...@@ -1467,6 +1471,7 @@ For some examples, have a look at the code in <code>org.h2.test.db.TestTableEngi ...@@ -1467,6 +1471,7 @@ For some examples, have a look at the code in <code>org.h2.test.db.TestTableEngi
<p> <p>
In order to create your own TableEngine, you need to implement the <code>org.h2.api.TableEngine</code> interface e.g. In order to create your own TableEngine, you need to implement the <code>org.h2.api.TableEngine</code> interface e.g.
something like this: something like this:
</p>
<pre> <pre>
package acme; package acme;
public static class MyTableEngine implements org.h2.api.TableEngine { public static class MyTableEngine implements org.h2.api.TableEngine {
...@@ -1480,12 +1485,13 @@ public static class MyTableEngine implements org.h2.api.TableEngine { ...@@ -1480,12 +1485,13 @@ public static class MyTableEngine implements org.h2.api.TableEngine {
} }
} }
</pre> </pre>
<p>
and then create the table from SQL like this: and then create the table from SQL like this:
</p>
<pre> <pre>
CREATE TABLE TEST(ID INT, NAME VARCHAR) CREATE TABLE TEST(ID INT, NAME VARCHAR)
ENGINE "acme.MyTableEngine"; ENGINE "acme.MyTableEngine";
</pre> </pre>
</p>
<p> <p>
It is also possible to pass in parameters to the table engine, like so: It is also possible to pass in parameters to the table engine, like so:
</p> </p>
......
...@@ -129,6 +129,7 @@ import org.h2.command.ddl.TruncateTable; ...@@ -129,6 +129,7 @@ import org.h2.command.ddl.TruncateTable;
import org.h2.command.dml.AlterTableSet; import org.h2.command.dml.AlterTableSet;
import org.h2.command.dml.BackupCommand; import org.h2.command.dml.BackupCommand;
import org.h2.command.dml.Call; import org.h2.command.dml.Call;
import org.h2.command.dml.CommandWithValues;
import org.h2.command.dml.Delete; import org.h2.command.dml.Delete;
import org.h2.command.dml.ExecuteProcedure; import org.h2.command.dml.ExecuteProcedure;
import org.h2.command.dml.Explain; import org.h2.command.dml.Explain;
...@@ -253,7 +254,8 @@ import org.h2.value.ValueTimestampTimeZone; ...@@ -253,7 +254,8 @@ import org.h2.value.ValueTimestampTimeZone;
public class Parser { public class Parser {
private static final String WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS = private static final String WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS =
"WITH statement supports only SELECT, TABLE, CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements"; "WITH statement supports only SELECT, TABLE, VALUES, " +
"CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements";
// used during the tokenizer phase // used during the tokenizer phase
private static final int CHAR_END = 1, CHAR_VALUE = 2, CHAR_QUOTED = 3; private static final int CHAR_END = 1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
...@@ -755,11 +757,8 @@ public class Parser { ...@@ -755,11 +757,8 @@ public class Parser {
case FROM: case FROM:
case SELECT: case SELECT:
case TABLE: case TABLE:
c = parseSelect();
break;
case VALUES: case VALUES:
read(); c = parseSelect();
c = parseValues();
break; break;
case WITH: case WITH:
read(); read();
...@@ -1450,6 +1449,7 @@ public class Parser { ...@@ -1450,6 +1449,7 @@ public class Parser {
switch (currentTokenType) { switch (currentTokenType) {
case FROM: case FROM:
case SELECT: case SELECT:
case VALUES:
case WITH: case WITH:
select = true; select = true;
break; break;
...@@ -1492,10 +1492,7 @@ public class Parser { ...@@ -1492,10 +1492,7 @@ public class Parser {
command.setKeys(keys); command.setKeys(keys);
} }
if (readIf(VALUES)) { if (readIf(VALUES)) {
do { parseValuesForCommand(command);
read(OPEN_PAREN);
command.addRow(parseValuesForInsert());
} while (readIf(COMMA));
} else { } else {
command.setQuery(parseSelect()); command.setQuery(parseSelect());
} }
...@@ -1682,11 +1679,7 @@ public class Parser { ...@@ -1682,11 +1679,7 @@ public class Parser {
Expression[] expr = {}; Expression[] expr = {};
command.addRow(expr); command.addRow(expr);
} else if (readIf(VALUES)) { } else if (readIf(VALUES)) {
read(OPEN_PAREN); parseValuesForCommand(command);
do {
command.addRow(parseValuesForInsert());
// the following condition will allow (..),; and (..);
} while (readIf(COMMA) && readIf(OPEN_PAREN));
} else if (readIf("SET")) { } else if (readIf("SET")) {
if (columns != null) { if (columns != null) {
throw getSyntaxError(); throw getSyntaxError();
...@@ -1725,28 +1718,35 @@ public class Parser { ...@@ -1725,28 +1718,35 @@ public class Parser {
command.setColumns(columns); command.setColumns(columns);
} }
if (readIf(VALUES)) { if (readIf(VALUES)) {
do { parseValuesForCommand(command);
read(OPEN_PAREN);
command.addRow(parseValuesForInsert());
} while (readIf(COMMA));
} else { } else {
command.setQuery(parseSelect()); command.setQuery(parseSelect());
} }
return command; return command;
} }
private Expression[] parseValuesForInsert() { private void parseValuesForCommand(CommandWithValues command) {
ArrayList<Expression> values = Utils.newSmallArrayList(); ArrayList<Expression> values = Utils.newSmallArrayList();
if (!readIf(CLOSE_PAREN)) {
do { do {
if (readIf("DEFAULT")) { values.clear();
values.add(null); boolean multiColumn;
if (readIf(ROW)) {
read(OPEN_PAREN);
multiColumn = true;
} else { } else {
values.add(readExpression()); multiColumn = readIf(OPEN_PAREN);
} }
if (multiColumn) {
if (!readIf(CLOSE_PAREN)) {
do {
values.add(readIf("DEFAULT") ? null : readExpression());
} while (readIfMore(false)); } while (readIfMore(false));
} }
return values.toArray(new Expression[0]); } else {
values.add(readIf("DEFAULT") ? null : readExpression());
}
command.addRow(values.toArray(new Expression[0]));
} while (readIf(COMMA));
} }
private TableFilter readTableFilter() { private TableFilter readTableFilter() {
...@@ -2350,6 +2350,7 @@ public class Parser { ...@@ -2350,6 +2350,7 @@ public class Parser {
case FROM: case FROM:
case SELECT: case SELECT:
case TABLE: case TABLE:
case VALUES:
case WITH: case WITH:
case OPEN_PAREN: case OPEN_PAREN:
Query query = parseSelect(); Query query = parseSelect();
...@@ -2696,6 +2697,8 @@ public class Parser { ...@@ -2696,6 +2697,8 @@ public class Parser {
command.setExpressions(expressions); command.setExpressions(expressions);
setSQL(command, "TABLE", start); setSQL(command, "TABLE", start);
return command; return command;
} else if (readIf(VALUES)) {
return parseValues();
} else { } else {
throw getSyntaxError(); throw getSyntaxError();
} }
...@@ -3936,8 +3939,12 @@ public class Parser { ...@@ -3936,8 +3939,12 @@ public class Parser {
read(); read();
break; break;
case VALUES: case VALUES:
if (database.getMode().onDuplicateKeyUpdate) {
read(); read();
r = readKeywordFunction("VALUES"); r = readKeywordFunction("VALUES");
} else {
r = new Subquery(parseSelect());
}
break; break;
case CASE: case CASE:
read(); read();
...@@ -5823,20 +5830,24 @@ public class Parser { ...@@ -5823,20 +5830,24 @@ public class Parser {
TableFilter filter = parseValuesTable(0); TableFilter filter = parseValuesTable(0);
command.setWildcard(); command.setWildcard();
command.addTableFilter(filter, true); command.addTableFilter(filter, true);
command.init();
return command; return command;
} }
private TableFilter parseValuesTable(int orderInFrom) { private TableFilter parseValuesTable(int orderInFrom) {
Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN); Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
TableFunction tf = (TableFunction) Function.getFunction(database, TableFunction tf = (TableFunction) Function.getFunction(database, "TABLE");
"TABLE");
ArrayList<Column> columns = Utils.newSmallArrayList(); ArrayList<Column> columns = Utils.newSmallArrayList();
ArrayList<ArrayList<Expression>> rows = Utils.newSmallArrayList(); ArrayList<ArrayList<Expression>> rows = Utils.newSmallArrayList();
do { do {
int i = 0; int i = 0;
ArrayList<Expression> row = Utils.newSmallArrayList(); ArrayList<Expression> row = Utils.newSmallArrayList();
boolean multiColumn = readIf(OPEN_PAREN); boolean multiColumn;
if (readIf(ROW)) {
read(OPEN_PAREN);
multiColumn = true;
} else {
multiColumn = readIf(OPEN_PAREN);
}
do { do {
Expression expr = readExpression(); Expression expr = readExpression();
expr = expr.optimize(session); expr = expr.optimize(session);
...@@ -5885,9 +5896,7 @@ public class Parser { ...@@ -5885,9 +5896,7 @@ public class Parser {
tf.setColumns(columns); tf.setColumns(columns);
tf.doneWithParameters(); tf.doneWithParameters();
Table table = new FunctionTable(mainSchema, session, tf, tf); Table table = new FunctionTable(mainSchema, session, tf, tf);
return new TableFilter(session, table, null, return new TableFilter(session, table, null, rightsChecked, currentSelect, orderInFrom, null);
rightsChecked, currentSelect, orderInFrom,
null);
} }
private Call parseCall() { private Call parseCall() {
...@@ -6163,7 +6172,7 @@ public class Parser { ...@@ -6163,7 +6172,7 @@ public class Parser {
while (readIf(OPEN_PAREN)) { while (readIf(OPEN_PAREN)) {
parentheses++; parentheses++;
} }
if (isToken(SELECT)) { if (isToken(SELECT) || isToken(VALUES)) {
p = parseWithQuery(); p = parseWithQuery();
} else if (isToken(TABLE)) { } else if (isToken(TABLE)) {
int index = lastParseIndex; int index = lastParseIndex;
......
/*
* Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command.dml;
import java.util.ArrayList;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.util.Utils;
/**
* Command that supports VALUES clause.
*/
public abstract class CommandWithValues extends Prepared {
protected final ArrayList<Expression[]> valuesExpressionList = Utils.newSmallArrayList();
/**
* Creates new instance of command with VALUES clause.
*
* @param session
* the session
*/
protected CommandWithValues(Session session) {
super(session);
}
/**
* Add a row to this command.
*
* @param expr
* the list of values
*/
public void addRow(Expression[] expr) {
valuesExpressionList.add(expr);
}
}
...@@ -12,7 +12,6 @@ import org.h2.api.ErrorCode; ...@@ -12,7 +12,6 @@ import org.h2.api.ErrorCode;
import org.h2.api.Trigger; import org.h2.api.Trigger;
import org.h2.command.Command; import org.h2.command.Command;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
import org.h2.engine.GeneratedKeys; import org.h2.engine.GeneratedKeys;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.engine.Right; import org.h2.engine.Right;
...@@ -35,7 +34,6 @@ import org.h2.table.Column; ...@@ -35,7 +34,6 @@ import org.h2.table.Column;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.Utils;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -43,11 +41,10 @@ import org.h2.value.ValueNull; ...@@ -43,11 +41,10 @@ import org.h2.value.ValueNull;
* This class represents the statement * This class represents the statement
* INSERT * INSERT
*/ */
public class Insert extends Prepared implements ResultTarget { public class Insert extends CommandWithValues implements ResultTarget {
private Table table; private Table table;
private Column[] columns; private Column[] columns;
private final ArrayList<Expression[]> list = Utils.newSmallArrayList();
private Query query; private Query query;
private boolean sortedInsertMode; private boolean sortedInsertMode;
private int rowNumber; private int rowNumber;
...@@ -117,15 +114,6 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -117,15 +114,6 @@ public class Insert extends Prepared implements ResultTarget {
duplicateKeyAssignmentMap.put(column, expression); duplicateKeyAssignmentMap.put(column, expression);
} }
/**
* Add a row to this merge statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
list.add(expr);
}
@Override @Override
public int update() { public int update() {
Index index = null; Index index = null;
...@@ -157,14 +145,14 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -157,14 +145,14 @@ public class Insert extends Prepared implements ResultTarget {
rowNumber = 0; rowNumber = 0;
GeneratedKeys generatedKeys = session.getGeneratedKeys(); GeneratedKeys generatedKeys = session.getGeneratedKeys();
generatedKeys.initialize(table); generatedKeys.initialize(table);
int listSize = list.size(); int listSize = valuesExpressionList.size();
if (listSize > 0) { if (listSize > 0) {
Mode mode = session.getDatabase().getMode(); Mode mode = session.getDatabase().getMode();
int columnLen = columns.length; int columnLen = columns.length;
for (int x = 0; x < listSize; x++) { for (int x = 0; x < listSize; x++) {
generatedKeys.nextRow(); generatedKeys.nextRow();
Row newRow = table.getTemplateRow(); Row newRow = table.getTemplateRow();
Expression[] expr = list.get(x); Expression[] expr = valuesExpressionList.get(x);
setCurrentRowNumber(x + 1); setCurrentRowNumber(x + 1);
for (int i = 0; i < columnLen; i++) { for (int i = 0; i < columnLen; i++) {
Column c = columns[i]; Column c = columns[i];
...@@ -294,13 +282,13 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -294,13 +282,13 @@ public class Insert extends Prepared implements ResultTarget {
if (sortedInsertMode) { if (sortedInsertMode) {
buff.append("SORTED "); buff.append("SORTED ");
} }
if (!list.isEmpty()) { if (!valuesExpressionList.isEmpty()) {
buff.append("VALUES "); buff.append("VALUES ");
int row = 0; int row = 0;
if (list.size() > 1) { if (valuesExpressionList.size() > 1) {
buff.append('\n'); buff.append('\n');
} }
for (Expression[] expr : list) { for (Expression[] expr : valuesExpressionList) {
if (row++ > 0) { if (row++ > 0) {
buff.append(",\n"); buff.append(",\n");
} }
...@@ -317,15 +305,15 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -317,15 +305,15 @@ public class Insert extends Prepared implements ResultTarget {
@Override @Override
public void prepare() { public void prepare() {
if (columns == null) { if (columns == null) {
if (!list.isEmpty() && list.get(0).length == 0) { if (!valuesExpressionList.isEmpty() && valuesExpressionList.get(0).length == 0) {
// special case where table is used as a sequence // special case where table is used as a sequence
columns = new Column[0]; columns = new Column[0];
} else { } else {
columns = table.getColumns(); columns = table.getColumns();
} }
} }
if (!list.isEmpty()) { if (!valuesExpressionList.isEmpty()) {
for (Expression[] expr : list) { for (Expression[] expr : valuesExpressionList) {
if (expr.length != columns.length) { if (expr.length != columns.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
} }
...@@ -400,7 +388,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -400,7 +388,7 @@ public class Insert extends Prepared implements ResultTarget {
ArrayList<String> variableNames = new ArrayList<>( ArrayList<String> variableNames = new ArrayList<>(
duplicateKeyAssignmentMap.size()); duplicateKeyAssignmentMap.size());
Expression[] row = (currentRow == null) ? list.get((int) getCurrentRowNumber() - 1) Expression[] row = (currentRow == null) ? valuesExpressionList.get((int) getCurrentRowNumber() - 1)
: new Expression[columns.length]; : new Expression[columns.length];
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
String key = table.getSchema().getName() + "." + String key = table.getSchema().getName() + "." +
......
...@@ -27,20 +27,18 @@ import org.h2.table.Column; ...@@ -27,20 +27,18 @@ import org.h2.table.Column;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.Utils;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
* This class represents the statement * This class represents the statement
* MERGE * MERGE
*/ */
public class Merge extends Prepared { public class Merge extends CommandWithValues {
private Table targetTable; private Table targetTable;
private TableFilter targetTableFilter; private TableFilter targetTableFilter;
private Column[] columns; private Column[] columns;
private Column[] keys; private Column[] keys;
private final ArrayList<Expression[]> valuesExpressionList = Utils.newSmallArrayList();
private Query query; private Query query;
private Prepared update; private Prepared update;
...@@ -72,15 +70,6 @@ public class Merge extends Prepared { ...@@ -72,15 +70,6 @@ public class Merge extends Prepared {
this.query = query; this.query = query;
} }
/**
* Add a row to this merge statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
valuesExpressionList.add(expr);
}
@Override @Override
public int update() { public int update() {
int count; int count;
......
...@@ -25,18 +25,16 @@ import org.h2.result.Row; ...@@ -25,18 +25,16 @@ import org.h2.result.Row;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.Utils;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
* This class represents the MySQL-compatibility REPLACE statement * This class represents the MySQL-compatibility REPLACE statement
*/ */
public class Replace extends Prepared { public class Replace extends CommandWithValues {
private Table table; private Table table;
private Column[] columns; private Column[] columns;
private Column[] keys; private Column[] keys;
private final ArrayList<Expression[]> list = Utils.newSmallArrayList();
private Query query; private Query query;
private Prepared update; private Prepared update;
...@@ -68,15 +66,6 @@ public class Replace extends Prepared { ...@@ -68,15 +66,6 @@ public class Replace extends Prepared {
this.query = query; this.query = query;
} }
/**
* Add a row to this replace statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
list.add(expr);
}
@Override @Override
public int update() { public int update() {
int count = 0; int count = 0;
...@@ -84,10 +73,10 @@ public class Replace extends Prepared { ...@@ -84,10 +73,10 @@ public class Replace extends Prepared {
session.getUser().checkRight(table, Right.UPDATE); session.getUser().checkRight(table, Right.UPDATE);
setCurrentRowNumber(0); setCurrentRowNumber(0);
Mode mode = session.getDatabase().getMode(); Mode mode = session.getDatabase().getMode();
if (!list.isEmpty()) { if (!valuesExpressionList.isEmpty()) {
for (int x = 0, size = list.size(); x < size; x++) { for (int x = 0, size = valuesExpressionList.size(); x < size; x++) {
setCurrentRowNumber(x + 1); setCurrentRowNumber(x + 1);
Expression[] expr = list.get(x); Expression[] expr = valuesExpressionList.get(x);
Row newRow = table.getTemplateRow(); Row newRow = table.getTemplateRow();
for (int i = 0, len = columns.length; i < len; i++) { for (int i = 0, len = columns.length; i < len; i++) {
Column c = columns[i]; Column c = columns[i];
...@@ -214,10 +203,10 @@ public class Replace extends Prepared { ...@@ -214,10 +203,10 @@ public class Replace extends Prepared {
} }
buff.append(')'); buff.append(')');
buff.append('\n'); buff.append('\n');
if (!list.isEmpty()) { if (!valuesExpressionList.isEmpty()) {
buff.append("VALUES "); buff.append("VALUES ");
int row = 0; int row = 0;
for (Expression[] expr : list) { for (Expression[] expr : valuesExpressionList) {
if (row++ > 0) { if (row++ > 0) {
buff.append(", "); buff.append(", ");
} }
...@@ -234,15 +223,15 @@ public class Replace extends Prepared { ...@@ -234,15 +223,15 @@ public class Replace extends Prepared {
@Override @Override
public void prepare() { public void prepare() {
if (columns == null) { if (columns == null) {
if (!list.isEmpty() && list.get(0).length == 0) { if (!valuesExpressionList.isEmpty() && valuesExpressionList.get(0).length == 0) {
// special case where table is used as a sequence // special case where table is used as a sequence
columns = new Column[0]; columns = new Column[0];
} else { } else {
columns = table.getColumns(); columns = table.getColumns();
} }
} }
if (!list.isEmpty()) { if (!valuesExpressionList.isEmpty()) {
for (Expression[] expr : list) { for (Expression[] expr : valuesExpressionList) {
if (expr.length != columns.length) { if (expr.length != columns.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH); throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
} }
......
...@@ -23,8 +23,7 @@ INSERT INTO VERSION VALUES ...@@ -23,8 +23,7 @@ INSERT INTO VERSION VALUES
(134, '1.4.184', '2014-12-19'), (134, '1.4.184', '2014-12-19'),
(133, '1.4.183', '2014-12-13'), (133, '1.4.183', '2014-12-13'),
(132, '1.4.182', '2014-10-17'), (132, '1.4.182', '2014-10-17'),
(131, '1.4.181', '2014-08-06'), (131, '1.4.181', '2014-08-06');
;
CREATE TABLE CHANNEL(TITLE VARCHAR, LINK VARCHAR, DESC VARCHAR, CREATE TABLE CHANNEL(TITLE VARCHAR, LINK VARCHAR, DESC VARCHAR,
LANGUAGE VARCHAR, PUB TIMESTAMP, LAST TIMESTAMP, AUTHOR VARCHAR); LANGUAGE VARCHAR, PUB TIMESTAMP, LAST TIMESTAMP, AUTHOR VARCHAR);
......
...@@ -154,8 +154,8 @@ public class TestScript extends TestDb { ...@@ -154,8 +154,8 @@ public class TestScript extends TestDb {
"dropDomain", "dropIndex", "dropSchema", "truncateTable" }) { "dropDomain", "dropIndex", "dropSchema", "truncateTable" }) {
testScript("ddl/" + s + ".sql"); testScript("ddl/" + s + ".sql");
} }
for (String s : new String[] { "delete", "error_reporting", "insertIgnore", "merge", "mergeUsing", "replace", for (String s : new String[] { "delete", "error_reporting", "insert", "insertIgnore", "merge", "mergeUsing",
"script", "select", "show", "table", "with" }) { "replace", "script", "select", "show", "table", "with" }) {
testScript("dml/" + s + ".sql"); testScript("dml/" + s + ".sql");
} }
for (String s : new String[] { "help" }) { for (String s : new String[] { "help" }) {
......
-- Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE TEST(A INT, B INT);
> ok
INSERT INTO TEST VALUES ROW (1, 2), (3, 4), ROW (5, 6);
> update count: 3
INSERT INTO TEST(a) VALUES 7;
> update count: 1
INSERT INTO TEST(a) VALUES 8, 9;
> update count: 2
TABLE TEST;
> A B
> - ----
> 1 2
> 3 4
> 5 6
> 7 null
> 8 null
> 9 null
> rows: 6
DROP TABLE TEST;
> ok
...@@ -509,3 +509,44 @@ SELECT RAND() A, RAND() + 1 B, RAND() + 1 C, RAND() D, RAND() + 2 E, RAND() + 3 ...@@ -509,3 +509,44 @@ SELECT RAND() A, RAND() + 1 B, RAND() + 1 C, RAND() D, RAND() + 2 E, RAND() + 3
> rows: 1 > rows: 1
@reconnect on @reconnect on
CREATE TABLE TEST (A INT, B INT, C INT);
> ok
INSERT INTO TEST VALUES (11, 12, 13), (21, 22, 23), (31, 32, 33);
> update count: 3
SELECT * FROM TEST WHERE (A, B) IN (VALUES (11, 12), (21, 22), (41, 42));
> A B C
> -- -- --
> 11 12 13
> 21 22 23
> rows: 2
SELECT * FROM TEST WHERE (A, B) = (VALUES (11, 12));
> A B C
> -- -- --
> 11 12 13
> rows: 1
DROP TABLE TEST;
> ok
VALUES (1, 2);
> C1 C2
> -- --
> 1 2
> rows: 1
VALUES ROW (1, 2);
> C1 C2
> -- --
> 1 2
> rows: 1
VALUES 1, 2;
> C1
> --
> 1
> 2
> rows: 2
...@@ -146,6 +146,12 @@ WITH CTE_TEST AS (TABLE TEST) ((TABLE CTE_TEST)); ...@@ -146,6 +146,12 @@ WITH CTE_TEST AS (TABLE TEST) ((TABLE CTE_TEST));
> 1 2 > 1 2
> rows: 1 > rows: 1
WITH CTE_TEST AS (VALUES (1, 2)) ((SELECT * FROM CTE_TEST));
> C1 C2
> -- --
> 1 2
> rows: 1
WITH CTE_TEST AS (TABLE TEST) ((SELECT A, B FROM CTE_TEST2)); WITH CTE_TEST AS (TABLE TEST) ((SELECT A, B FROM CTE_TEST2));
> exception TABLE_OR_VIEW_NOT_FOUND_1 > exception TABLE_OR_VIEW_NOT_FOUND_1
......
...@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest ...@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest
analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan
corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes
preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr
presorted inclusion presorted inclusion contexts
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论