提交 6f0b2338 authored 作者: Owner's avatar Owner

Issue#589 Support standard MERGE USING statement

上级 b06a515c
...@@ -13,6 +13,7 @@ import java.math.BigInteger; ...@@ -13,6 +13,7 @@ import java.math.BigInteger;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.Collator; import java.text.Collator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
...@@ -742,6 +743,26 @@ public class Parser { ...@@ -742,6 +743,26 @@ public class Parser {
int start = lastParseIndex; int start = lastParseIndex;
TableFilter filter = readSimpleTableFilter(0); TableFilter filter = readSimpleTableFilter(0);
command.setTableFilter(filter); command.setTableFilter(filter);
parseUpdateSetClause(command, filter);
if (readIf("WHERE")) {
Expression condition = readExpression();
command.setCondition(condition);
}
if (readIf("ORDER")) {
// for MySQL compatibility
// (this syntax is supported, but ignored)
read("BY");
parseSimpleOrderList();
}
if (readIf("LIMIT")) {
Expression limit = readTerm().optimize(session);
command.setLimit(limit);
}
setSQL(command, "UPDATE", start);
return command;
}
private void parseUpdateSetClause(Update command, TableFilter filter) {
read("SET"); read("SET");
if (readIf("(")) { if (readIf("(")) {
ArrayList<Column> columns = New.arrayList(); ArrayList<Column> columns = New.arrayList();
...@@ -778,31 +799,30 @@ public class Parser { ...@@ -778,31 +799,30 @@ public class Parser {
command.setAssignment(column, expression); command.setAssignment(column, expression);
} while (readIf(",")); } while (readIf(","));
} }
if (readIf("WHERE")) {
Expression condition = readExpression();
command.setCondition(condition);
} }
if (readIf("ORDER")) {
// for MySQL compatibility private TableFilter readSimpleTableFilter(int orderInFrom) {
// (this syntax is supported, but ignored) Table table = readTableOrView();
read("BY"); String alias = null;
parseSimpleOrderList(); if (readIf("AS")) {
alias = readAliasIdentifier();
} else if (currentTokenType == IDENTIFIER) {
if (!equalsToken("SET", currentToken)) {
// SET is not a keyword (PostgreSQL supports it as a table name)
alias = readAliasIdentifier();
} }
if (readIf("LIMIT")) {
Expression limit = readTerm().optimize(session);
command.setLimit(limit);
} }
setSQL(command, "UPDATE", start); return new TableFilter(session, table, alias, rightsChecked,
return command; currentSelect, orderInFrom, null);
} }
private TableFilter readSimpleTableFilter(int orderInFrom) { private TableFilter readSimpleTableFilterWithAliasExcludes(int orderInFrom,List<String> excludeTokens) {
Table table = readTableOrView(); Table table = readTableOrView();
String alias = null; String alias = null;
if (readIf("AS")) { if (readIf("AS")) {
alias = readAliasIdentifier(); alias = readAliasIdentifier();
} else if (currentTokenType == IDENTIFIER) { } else if (currentTokenType == IDENTIFIER) {
if (!equalsToken("SET", currentToken)) { if (!equalsToken("SET", currentToken) && !excludeTokens.contains(currentToken)) {
// SET is not a keyword (PostgreSQL supports it as a table name) // SET is not a keyword (PostgreSQL supports it as a table name)
alias = readAliasIdentifier(); alias = readAliasIdentifier();
} }
...@@ -1021,12 +1041,19 @@ public class Parser { ...@@ -1021,12 +1041,19 @@ public class Parser {
return select; return select;
} }
private Merge parseMerge() { private Merge parseMerge() {
Merge command = new Merge(session); Merge command = new Merge(session);
currentPrepared = command; currentPrepared = command;
read("INTO"); read("INTO");
Table table = readTableOrView(); List<String> excludeIdentifiers = Arrays.asList("USING","KEY","VALUES");
command.setTable(table); TableFilter targetTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
command.setTargetTableFilter(targetTableFilter);
Table table = command.getTargetTable();
if (readIf("USING")){
return parseMergeUsing(command);
}
if (readIf("(")) { if (readIf("(")) {
if (isSelect()) { if (isSelect()) {
command.setQuery(parseSelect()); command.setQuery(parseSelect());
...@@ -1061,6 +1088,60 @@ public class Parser { ...@@ -1061,6 +1088,60 @@ public class Parser {
} }
return command; return command;
} }
/*
MERGE INTO targetTableName [t_alias] USING table_reference [s_alias] ON (condition)
WHEN MATCHED THEN
[UPDATE SET column1 = value1 [, column2 = value2 ...] | DELETE]
WHEN NOT MATCHED THEN
INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);
table_reference ::= table / view / sub-query
*/
/* TODO Finish coding*/
private Merge parseMergeUsing(Merge command) {
if (readIf("(")) {
if (isSelect()) {
command.setQuery(parseSelect());
read(")");
}
}
else{
List<String> excludeIdentifiers = Arrays.asList("ON");
TableFilter sourceTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
command.setSourceTableFilter(sourceTableFilter);
}
read("ON");
read("(");
Expression condition = readExpression();
command.addCondition(condition);
read(")");
if(readIf("WHEN")&&readIf("MATCHED")&&readIf("THEN")){
if (readIf("UPDATE")){
Update updateCommand = new Update(session);
//currentPrepared = updateCommand;
TableFilter filter = command.getTargetTableFilter();
updateCommand.setTableFilter(filter);
parseUpdateSetClause(updateCommand, filter);
command.setUpdateOrDeleteCommand(updateCommand);
}
if (readIf("DELETE")){
Delete deleteCommand = new Delete(session);
TableFilter filter = command.getTargetTableFilter();
deleteCommand.setTableFilter(filter);
command.setUpdateOrDeleteCommand(deleteCommand);
}
}
if(readIf("WHEN")&&readIf("NOT")&&readIf("MATCHED")&&readIf("THEN")){
Insert insertCommand = new Insert(session);
insertCommand.setTable(command.getTargetTable());
parseInsertGivenTable(insertCommand,command.getTargetTable());
command.setInsertCommand(insertCommand);
}
return command;
}
private Insert parseInsert() { private Insert parseInsert() {
Insert command = new Insert(session); Insert command = new Insert(session);
...@@ -1068,6 +1149,35 @@ public class Parser { ...@@ -1068,6 +1149,35 @@ public class Parser {
read("INTO"); read("INTO");
Table table = readTableOrView(); Table table = readTableOrView();
command.setTable(table); command.setTable(table);
Insert returnedCommand = parseInsertGivenTable(command, table);
if (returnedCommand!=null){
return returnedCommand;
}
if (database.getMode().onDuplicateKeyUpdate) {
if (readIf("ON")) {
read("DUPLICATE");
read("KEY");
read("UPDATE");
do {
Column column = parseColumn(table);
read("=");
Expression expression;
if (readIf("DEFAULT")) {
expression = ValueExpression.getDefault();
} else {
expression = readExpression();
}
command.addAssignmentForDuplicate(column, expression);
} while (readIf(","));
}
}
if (database.getMode().isolationLevelInSelectOrInsertStatement) {
parseIsolationClause();
}
return command;
}
private Insert parseInsertGivenTable(Insert command, Table table) {
Column[] columns = null; Column[] columns = null;
if (readIf("(")) { if (readIf("(")) {
if (isSelect()) { if (isSelect()) {
...@@ -1126,28 +1236,7 @@ public class Parser { ...@@ -1126,28 +1236,7 @@ public class Parser {
} else { } else {
command.setQuery(parseSelect()); command.setQuery(parseSelect());
} }
if (database.getMode().onDuplicateKeyUpdate) { return null;
if (readIf("ON")) {
read("DUPLICATE");
read("KEY");
read("UPDATE");
do {
Column column = parseColumn(table);
read("=");
Expression expression;
if (readIf("DEFAULT")) {
expression = ValueExpression.getDefault();
} else {
expression = readExpression();
}
command.addAssignmentForDuplicate(column, expression);
} while (readIf(","));
}
}
if (database.getMode().isolationLevelInSelectOrInsertStatement) {
parseIsolationClause();
}
return command;
} }
/** /**
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
package org.h2.command.dml; package org.h2.command.dml;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.api.ErrorCode; 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;
...@@ -23,6 +22,7 @@ import org.h2.result.ResultInterface; ...@@ -23,6 +22,7 @@ import org.h2.result.ResultInterface;
import org.h2.result.Row; 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.table.TableFilter;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -33,12 +33,17 @@ import org.h2.value.Value; ...@@ -33,12 +33,17 @@ import org.h2.value.Value;
*/ */
public class Merge extends Prepared { public class Merge extends Prepared {
private Table table; private Table targetTable;
private TableFilter targetTableFilter;
private Column[] columns; private Column[] columns;
private Column[] keys; private Column[] keys;
private final ArrayList<Expression[]> list = New.arrayList(); private final ArrayList<Expression[]> valuesExpressionList = New.arrayList();
private Query query; private Query query;
private Prepared update; private Prepared update;
private TableFilter sourceTableFilter;
private ArrayList<Expression> conditions = new ArrayList<Expression>();
private Prepared updateOrDeleteCommand;
private Insert insertCommand;
public Merge(Session session) { public Merge(Session session) {
super(session); super(session);
...@@ -52,8 +57,8 @@ public class Merge extends Prepared { ...@@ -52,8 +57,8 @@ public class Merge extends Prepared {
} }
} }
public void setTable(Table table) { public void setTargetTable(Table targetTable) {
this.table = table; this.targetTable = targetTable;
} }
public void setColumns(Column[] columns) { public void setColumns(Column[] columns) {
...@@ -74,21 +79,22 @@ public class Merge extends Prepared { ...@@ -74,21 +79,22 @@ public class Merge extends Prepared {
* @param expr the list of values * @param expr the list of values
*/ */
public void addRow(Expression[] expr) { public void addRow(Expression[] expr) {
list.add(expr); valuesExpressionList.add(expr);
} }
@Override @Override
public int update() { public int update() {
int count; int count;
session.getUser().checkRight(table, Right.INSERT); session.getUser().checkRight(targetTable, Right.INSERT);
session.getUser().checkRight(table, Right.UPDATE); session.getUser().checkRight(targetTable, Right.UPDATE);
setCurrentRowNumber(0); setCurrentRowNumber(0);
if (list.size() > 0) { if (valuesExpressionList.size() > 0) {
// process values in list
count = 0; count = 0;
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 = targetTable.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];
int index = c.getColumnId(); int index = c.getColumnId();
...@@ -107,14 +113,15 @@ public class Merge extends Prepared { ...@@ -107,14 +113,15 @@ public class Merge extends Prepared {
count++; count++;
} }
} else { } else {
// process select data for list
ResultInterface rows = query.query(0); ResultInterface rows = query.query(0);
count = 0; count = 0;
table.fire(session, Trigger.UPDATE | Trigger.INSERT, true); targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
table.lock(session, true, false); targetTable.lock(session, true, false);
while (rows.next()) { while (rows.next()) {
count++; count++;
Value[] r = rows.currentRow(); Value[] r = rows.currentRow();
Row newRow = table.getTemplateRow(); Row newRow = targetTable.getTemplateRow();
setCurrentRowNumber(count); setCurrentRowNumber(count);
for (int j = 0; j < columns.length; j++) { for (int j = 0; j < columns.length; j++) {
Column c = columns[j]; Column c = columns[j];
...@@ -129,7 +136,7 @@ public class Merge extends Prepared { ...@@ -129,7 +136,7 @@ public class Merge extends Prepared {
merge(newRow); merge(newRow);
} }
rows.close(); rows.close();
table.fire(session, Trigger.UPDATE | Trigger.INSERT, false); targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
} }
return count; return count;
} }
...@@ -151,16 +158,20 @@ public class Merge extends Prepared { ...@@ -151,16 +158,20 @@ public class Merge extends Prepared {
Parameter p = k.get(columns.length + i); Parameter p = k.get(columns.length + i);
p.setValue(v); p.setValue(v);
} }
// try and update
int count = update.update(); int count = update.update();
// if update fails try an insert
if (count == 0) { if (count == 0) {
try { try {
table.validateConvertUpdateSequence(session, row); targetTable.validateConvertUpdateSequence(session, row);
boolean done = table.fireBeforeRow(session, null, row); boolean done = targetTable.fireBeforeRow(session, null, row);
if (!done) { if (!done) {
table.lock(session, true, false); targetTable.lock(session, true, false);
table.addRow(session, row); targetTable.addRow(session, row);
session.log(table, UndoLogRecord.INSERT, row); session.log(targetTable, UndoLogRecord.INSERT, row);
table.fireAfterRow(session, null, row, false); targetTable.fireAfterRow(session, null, row, false);
} }
} catch (DbException e) { } catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) { if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
...@@ -179,21 +190,21 @@ public class Merge extends Prepared { ...@@ -179,21 +190,21 @@ public class Merge extends Prepared {
} }
} }
if (indexMatchesKeys) { if (indexMatchesKeys) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, targetTable.getName());
} }
} }
} }
throw e; throw e;
} }
} else if (count != 1) { } else if (count != 1) {
throw DbException.get(ErrorCode.DUPLICATE_KEY_1, table.getSQL()); throw DbException.get(ErrorCode.DUPLICATE_KEY_1, targetTable.getSQL());
} }
} }
@Override @Override
public String getPlanSQL() { public String getPlanSQL() {
StatementBuilder buff = new StatementBuilder("MERGE INTO "); StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(table.getSQL()).append('('); buff.append(targetTable.getSQL()).append('(');
for (Column c : columns) { for (Column c : columns) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(c.getSQL()); buff.append(c.getSQL());
...@@ -209,10 +220,10 @@ public class Merge extends Prepared { ...@@ -209,10 +220,10 @@ public class Merge extends Prepared {
buff.append(')'); buff.append(')');
} }
buff.append('\n'); buff.append('\n');
if (list.size() > 0) { if (valuesExpressionList.size() > 0) {
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(", ");
} }
...@@ -237,15 +248,15 @@ public class Merge extends Prepared { ...@@ -237,15 +248,15 @@ public class Merge extends Prepared {
@Override @Override
public void prepare() { public void prepare() {
if (columns == null) { if (columns == null) {
if (list.size() > 0 && list.get(0).length == 0) { if (valuesExpressionList.size() > 0 && 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 = targetTable.getColumns();
} }
} }
if (list.size() > 0) { if (valuesExpressionList.size() > 0) {
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);
} }
...@@ -263,14 +274,14 @@ public class Merge extends Prepared { ...@@ -263,14 +274,14 @@ public class Merge extends Prepared {
} }
} }
if (keys == null) { if (keys == null) {
Index idx = table.getPrimaryKey(); Index idx = targetTable.getPrimaryKey();
if (idx == null) { if (idx == null) {
throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY"); throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY");
} }
keys = idx.getColumns(); keys = idx.getColumns();
} }
StatementBuilder buff = new StatementBuilder("UPDATE "); StatementBuilder buff = new StatementBuilder("UPDATE ");
buff.append(table.getSQL()).append(" SET "); buff.append(targetTable.getSQL()).append(" SET ");
for (Column c : columns) { for (Column c : columns) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(c.getSQL()).append("=?"); buff.append(c.getSQL()).append("=?");
...@@ -305,4 +316,41 @@ public class Merge extends Prepared { ...@@ -305,4 +316,41 @@ public class Merge extends Prepared {
return true; return true;
} }
public void setSourceTableFilter(TableFilter sourceTableFilter) {
this.sourceTableFilter = sourceTableFilter;
}
public void addCondition(Expression condition) {
this.conditions .add(condition);
}
public Table getTargetTable() {
return targetTable;
}
public TableFilter getTargetTableFilter() {
return targetTableFilter;
}
public void setTargetTableFilter(TableFilter targetTableFilter) {
this.targetTableFilter = targetTableFilter;
setTargetTable(targetTableFilter.getTable());
}
public Prepared getUpdateOrDeleteCommand() {
return updateOrDeleteCommand;
}
public void setUpdateOrDeleteCommand(Prepared updateOrDelete) {
this.updateOrDeleteCommand = updateOrDelete;
}
public Insert getInsertCommand() {
return insertCommand;
}
public void setInsertCommand(Insert insertCommand) {
this.insertCommand = insertCommand;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论