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

Issue#589 Support standard MERGE USING statement

上级 b06a515c
......@@ -13,6 +13,7 @@ import java.math.BigInteger;
import java.nio.charset.Charset;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
......@@ -742,6 +743,26 @@ public class Parser {
int start = lastParseIndex;
TableFilter filter = readSimpleTableFilter(0);
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");
if (readIf("(")) {
ArrayList<Column> columns = New.arrayList();
......@@ -778,22 +799,6 @@ public class Parser {
command.setAssignment(column, expression);
} while (readIf(","));
}
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 TableFilter readSimpleTableFilter(int orderInFrom) {
......@@ -811,6 +816,21 @@ public class Parser {
currentSelect, orderInFrom, null);
}
private TableFilter readSimpleTableFilterWithAliasExcludes(int orderInFrom,List<String> excludeTokens) {
Table table = readTableOrView();
String alias = null;
if (readIf("AS")) {
alias = readAliasIdentifier();
} else if (currentTokenType == IDENTIFIER) {
if (!equalsToken("SET", currentToken) && !excludeTokens.contains(currentToken)) {
// SET is not a keyword (PostgreSQL supports it as a table name)
alias = readAliasIdentifier();
}
}
return new TableFilter(session, table, alias, rightsChecked,
currentSelect, orderInFrom, null);
}
private Delete parseDelete() {
Delete command = new Delete(session);
Expression limit = null;
......@@ -1020,13 +1040,20 @@ public class Parser {
read();
return select;
}
private Merge parseMerge() {
Merge command = new Merge(session);
currentPrepared = command;
read("INTO");
Table table = readTableOrView();
command.setTable(table);
List<String> excludeIdentifiers = Arrays.asList("USING","KEY","VALUES");
TableFilter targetTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
command.setTargetTableFilter(targetTableFilter);
Table table = command.getTargetTable();
if (readIf("USING")){
return parseMergeUsing(command);
}
if (readIf("(")) {
if (isSelect()) {
command.setQuery(parseSelect());
......@@ -1061,6 +1088,60 @@ public class Parser {
}
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() {
Insert command = new Insert(session);
......@@ -1068,6 +1149,35 @@ public class Parser {
read("INTO");
Table table = readTableOrView();
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;
if (readIf("(")) {
if (isSelect()) {
......@@ -1126,28 +1236,7 @@ public class Parser {
} else {
command.setQuery(parseSelect());
}
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;
return null;
}
/**
......
......@@ -6,7 +6,6 @@
package org.h2.command.dml;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.api.Trigger;
import org.h2.command.Command;
......@@ -23,6 +22,7 @@ import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
import org.h2.value.Value;
......@@ -33,12 +33,17 @@ import org.h2.value.Value;
*/
public class Merge extends Prepared {
private Table table;
private Table targetTable;
private TableFilter targetTableFilter;
private Column[] columns;
private Column[] keys;
private final ArrayList<Expression[]> list = New.arrayList();
private final ArrayList<Expression[]> valuesExpressionList = New.arrayList();
private Query query;
private Prepared update;
private TableFilter sourceTableFilter;
private ArrayList<Expression> conditions = new ArrayList<Expression>();
private Prepared updateOrDeleteCommand;
private Insert insertCommand;
public Merge(Session session) {
super(session);
......@@ -52,8 +57,8 @@ public class Merge extends Prepared {
}
}
public void setTable(Table table) {
this.table = table;
public void setTargetTable(Table targetTable) {
this.targetTable = targetTable;
}
public void setColumns(Column[] columns) {
......@@ -67,28 +72,29 @@ public class Merge extends Prepared {
public void setQuery(Query query) {
this.query = query;
}
/**
* Add a row to this merge statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
list.add(expr);
valuesExpressionList.add(expr);
}
@Override
public int update() {
int count;
session.getUser().checkRight(table, Right.INSERT);
session.getUser().checkRight(table, Right.UPDATE);
session.getUser().checkRight(targetTable, Right.INSERT);
session.getUser().checkRight(targetTable, Right.UPDATE);
setCurrentRowNumber(0);
if (list.size() > 0) {
if (valuesExpressionList.size() > 0) {
// process values in list
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);
Expression[] expr = list.get(x);
Row newRow = table.getTemplateRow();
Expression[] expr = valuesExpressionList.get(x);
Row newRow = targetTable.getTemplateRow();
for (int i = 0, len = columns.length; i < len; i++) {
Column c = columns[i];
int index = c.getColumnId();
......@@ -107,14 +113,15 @@ public class Merge extends Prepared {
count++;
}
} else {
// process select data for list
ResultInterface rows = query.query(0);
count = 0;
table.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
table.lock(session, true, false);
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
targetTable.lock(session, true, false);
while (rows.next()) {
count++;
Value[] r = rows.currentRow();
Row newRow = table.getTemplateRow();
Row newRow = targetTable.getTemplateRow();
setCurrentRowNumber(count);
for (int j = 0; j < columns.length; j++) {
Column c = columns[j];
......@@ -129,7 +136,7 @@ public class Merge extends Prepared {
merge(newRow);
}
rows.close();
table.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
}
return count;
}
......@@ -151,16 +158,20 @@ public class Merge extends Prepared {
Parameter p = k.get(columns.length + i);
p.setValue(v);
}
// try and update
int count = update.update();
// if update fails try an insert
if (count == 0) {
try {
table.validateConvertUpdateSequence(session, row);
boolean done = table.fireBeforeRow(session, null, row);
targetTable.validateConvertUpdateSequence(session, row);
boolean done = targetTable.fireBeforeRow(session, null, row);
if (!done) {
table.lock(session, true, false);
table.addRow(session, row);
session.log(table, UndoLogRecord.INSERT, row);
table.fireAfterRow(session, null, row, false);
targetTable.lock(session, true, false);
targetTable.addRow(session, row);
session.log(targetTable, UndoLogRecord.INSERT, row);
targetTable.fireAfterRow(session, null, row, false);
}
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
......@@ -179,21 +190,21 @@ public class Merge extends Prepared {
}
}
if (indexMatchesKeys) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, targetTable.getName());
}
}
}
throw e;
}
} else if (count != 1) {
throw DbException.get(ErrorCode.DUPLICATE_KEY_1, table.getSQL());
throw DbException.get(ErrorCode.DUPLICATE_KEY_1, targetTable.getSQL());
}
}
@Override
public String getPlanSQL() {
StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(table.getSQL()).append('(');
buff.append(targetTable.getSQL()).append('(');
for (Column c : columns) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL());
......@@ -209,10 +220,10 @@ public class Merge extends Prepared {
buff.append(')');
}
buff.append('\n');
if (list.size() > 0) {
if (valuesExpressionList.size() > 0) {
buff.append("VALUES ");
int row = 0;
for (Expression[] expr : list) {
for (Expression[] expr : valuesExpressionList) {
if (row++ > 0) {
buff.append(", ");
}
......@@ -237,15 +248,15 @@ public class Merge extends Prepared {
@Override
public void prepare() {
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
columns = new Column[0];
} else {
columns = table.getColumns();
columns = targetTable.getColumns();
}
}
if (list.size() > 0) {
for (Expression[] expr : list) {
if (valuesExpressionList.size() > 0) {
for (Expression[] expr : valuesExpressionList) {
if (expr.length != columns.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
......@@ -263,14 +274,14 @@ public class Merge extends Prepared {
}
}
if (keys == null) {
Index idx = table.getPrimaryKey();
Index idx = targetTable.getPrimaryKey();
if (idx == null) {
throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY");
}
keys = idx.getColumns();
}
StatementBuilder buff = new StatementBuilder("UPDATE ");
buff.append(table.getSQL()).append(" SET ");
buff.append(targetTable.getSQL()).append(" SET ");
for (Column c : columns) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL()).append("=?");
......@@ -305,4 +316,41 @@ public class Merge extends Prepared {
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论