提交 fde7c7b9 authored 作者: Owner's avatar Owner

Merge branch 'master' of https://github.com/h2database/h2database into Issue#589

......@@ -76,6 +76,7 @@ import org.h2.command.dml.ExecuteProcedure;
import org.h2.command.dml.Explain;
import org.h2.command.dml.Insert;
import org.h2.command.dml.Merge;
import org.h2.command.dml.MergeUsing;
import org.h2.command.dml.NoOperation;
import org.h2.command.dml.Query;
import org.h2.command.dml.Replace;
......@@ -845,6 +846,11 @@ public class Parser {
}
TableFilter filter = readSimpleTableFilter(0);
command.setTableFilter(filter);
parseDeleteGivenTable(command, limit, start);
return command;
}
private void parseDeleteGivenTable(Delete command, Expression limit, int start) {
if (readIf("WHERE")) {
Expression condition = readExpression();
command.setCondition(condition);
......@@ -854,7 +860,6 @@ public class Parser {
}
command.setLimit(limit);
setSQL(command, "DELETE", start);
return command;
}
private IndexColumn[] parseIndexColumnList() {
......@@ -1098,17 +1103,31 @@ public class Parser {
table_reference ::= table / view / sub-query
*/
/* TODO Finish coding*/
private Merge parseMergeUsing(Merge command) {
private MergeUsing parseMergeUsing(Merge oldCommand) {
MergeUsing command = new MergeUsing(oldCommand);
if (readIf("(")) {
if (isSelect()) {
command.setQuery(parseSelect());
read(")");
}
command.setQueryAlias(readFromAlias(null, Arrays.asList("ON")));
}
else{
List<String> excludeIdentifiers = Arrays.asList("ON");
TableFilter sourceTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
command.setSourceTableFilter(sourceTableFilter);
command.setSourceTableFilter(sourceTableFilter);
StringBuilder buff = new StringBuilder(
"SELECT * FROM "+sourceTableFilter.getTable().getName());
if(sourceTableFilter.getTableAlias()!=null){
buff.append(" AS "+sourceTableFilter.getTableAlias());
}
//ArrayList<Value> paramValues = New.arrayList();
Prepared preparedQuery = prepare(session, buff.toString(), null/*paramValues*/);
System.out.println("class="+preparedQuery.getClass());
command.setQuery((Select)preparedQuery);
}
read("ON");
read("(");
......@@ -1123,21 +1142,25 @@ public class Parser {
TableFilter filter = command.getTargetTableFilter();
updateCommand.setTableFilter(filter);
parseUpdateSetClause(updateCommand, filter);
command.setUpdateOrDeleteCommand(updateCommand);
command.setUpdateCommand(updateCommand);
}
if (readIf("DELETE")){
Delete deleteCommand = new Delete(session);
TableFilter filter = command.getTargetTableFilter();
deleteCommand.setTableFilter(filter);
command.setUpdateOrDeleteCommand(deleteCommand);
parseDeleteGivenTable(deleteCommand,null,lastParseIndex);
command.setDeleteCommand(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);
if (readIf("INSERT")){
Insert insertCommand = new Insert(session);
insertCommand.setTable(command.getTargetTable());
parseInsertGivenTable(insertCommand,command.getTargetTable());
command.setInsertCommand(insertCommand);
}
}
return command;
......@@ -1380,6 +1403,10 @@ public class Parser {
}
}
}
// inherit alias for temporary views (usually CTE's) from table name
if(table.isView() && table.isTemporary() && alias==null){
alias = table.getName();
}
return new TableFilter(session, table, alias, rightsChecked,
currentSelect, orderInFrom++, indexHints);
}
......@@ -1401,18 +1428,21 @@ public class Parser {
return IndexHints.createUseIndexHints(indexNames);
}
private String readFromAlias(String alias) {
private String readFromAlias(String alias, List<String> excludeIdentifiers) {
if (readIf("AS")) {
alias = readAliasIdentifier();
} else if (currentTokenType == IDENTIFIER) {
// left and right are not keywords (because they are functions as
// well)
if (!isToken("LEFT") && !isToken("RIGHT") && !isToken("FULL")) {
} else if (currentTokenType == IDENTIFIER && !excludeIdentifiers.contains(currentToken)) {
alias = readAliasIdentifier();
}
}
return alias;
}
private String readFromAlias(String alias) {
// left and right are not keywords (because they are functions as
// well)
List<String> excludeIdentifiers = Arrays.asList("LEFT","RIGHT","FULL");;
return readFromAlias(alias, excludeIdentifiers);
}
private Prepared parseTruncate() {
read("TABLE");
......
......@@ -454,4 +454,8 @@ public abstract class Prepared {
public void setCteCleanups(List<TableView> cteCleanups) {
this.cteCleanups = cteCleanups;
}
public Session getSession() {
return session;
}
}
......@@ -33,17 +33,13 @@ import org.h2.value.Value;
*/
public class Merge extends Prepared {
private Table targetTable;
private TableFilter targetTableFilter;
private Column[] columns;
private Column[] keys;
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;
protected Table targetTable;
protected TableFilter targetTableFilter;
protected Column[] columns;
protected Column[] keys;
protected final ArrayList<Expression[]> valuesExpressionList = New.arrayList();
protected Query query;
protected Prepared update;
public Merge(Session session) {
super(session);
......@@ -141,7 +137,7 @@ public class Merge extends Prepared {
return count;
}
private void merge(Row row) {
protected void merge(Row row) {
ArrayList<Parameter> k = update.getParameters();
for (int i = 0; i < columns.length; i++) {
Column col = columns[i];
......@@ -316,14 +312,6 @@ 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;
}
......@@ -337,20 +325,6 @@ public class Merge extends Prepared {
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;
}
}
/*
* Copyright 2004-2017 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.api.ErrorCode;
import org.h2.api.Trigger;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.index.Index;
import org.h2.message.DbException;
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.StatementBuilder;
import org.h2.value.Value;
/**
* This class represents the statement syntax
* MERGE table alias USING...
*/
public class MergeUsing extends Merge {
private TableFilter sourceTableFilter;
private ArrayList<Expression> conditions = new ArrayList<Expression>();
private Prepared updateCommand;
private Prepared deleteCommand;
private Insert insertCommand;
private String queryAlias;
public MergeUsing(Merge merge) {
super(merge.getSession());
// bring across only the already parsed data from Merge...
this.targetTable = merge.targetTable;
this.targetTableFilter = merge.targetTableFilter;
}
@Override
public int update() {
int count;
checkRights();
setCurrentRowNumber(0);
// process select query data for row creation
ResultInterface rows = query.query(0);
count = 0;
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
targetTable.lock(session, true, false);
while (rows.next()) {
count++;
Value[] r = rows.currentRow();
Row newRow = targetTable.getTemplateRow();
setCurrentRowNumber(count);
for (int j = 0; j < columns.length; j++) {
Column c = columns[j];
int index = c.getColumnId();
try {
Value v = c.convert(r[j]);
newRow.setValue(index, v);
} catch (DbException ex) {
throw setRow(ex, count, getSQL(r));
}
}
merge(newRow);
}
rows.close();
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
return count;
}
private void checkRights() {
if(insertCommand!=null){
session.getUser().checkRight(targetTable, Right.INSERT);
}
if(updateCommand!=null){
session.getUser().checkRight(targetTable, Right.UPDATE);
}
if(deleteCommand!=null){
session.getUser().checkRight(targetTable, Right.DELETE);
}
}
@Override
protected void merge(Row row) {
ArrayList<Parameter> k = update.getParameters();
for (int i = 0; i < columns.length; i++) {
Column col = columns[i];
Value v = row.getValue(col.getColumnId());
Parameter p = k.get(i);
p.setValue(v);
}
for (int i = 0; i < keys.length; i++) {
Column col = keys[i];
Value v = row.getValue(col.getColumnId());
if (v == null) {
throw DbException.get(ErrorCode.COLUMN_CONTAINS_NULL_VALUES_1, col.getSQL());
}
Parameter p = k.get(columns.length + i);
p.setValue(v);
}
// try and update
int count = 0;
if(updateCommand!=null){
count+=updateCommand.update();
}
if(deleteCommand!=null){
count+=deleteCommand.update();
}
// if update does nothing, try an insert
if (count == 0) {
try {
targetTable.validateConvertUpdateSequence(session, row);
boolean done = targetTable.fireBeforeRow(session, null, row);
if (!done) {
targetTable.lock(session, true, false);
//targetTable.addRow(session, row);
addRowByInsert(session,row);
session.log(targetTable, UndoLogRecord.INSERT, row);
targetTable.fireAfterRow(session, null, row, false);
}
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
// possibly a concurrent merge or insert
Index index = (Index) e.getSource();
if (index != null) {
// verify the index columns match the key
Column[] indexColumns = index.getColumns();
boolean indexMatchesKeys = true;
if (indexColumns.length <= keys.length) {
for (int i = 0; i < indexColumns.length; i++) {
if (indexColumns[i] != keys[i]) {
indexMatchesKeys = false;
break;
}
}
}
if (indexMatchesKeys) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, targetTable.getName());
}
}
}
throw e;
}
} else if (count != 1) {
throw DbException.get(ErrorCode.DUPLICATE_KEY_1, targetTable.getSQL());
}
}
private void addRowByInsert(Session session, Row row) {
// TODO Auto-generated method stub
targetTable.addRow(session, row);
}
@Override
public String getPlanSQL() {
StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(targetTable.getSQL()).append('(');
for (Column c : columns) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL());
}
buff.append(')');
if (keys != null) {
buff.append(" KEY(");
buff.resetCount();
for (Column c : keys) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL());
}
buff.append(')');
}
buff.append('\n');
if (valuesExpressionList.size() > 0) {
buff.append("VALUES ");
int row = 0;
for (Expression[] expr : valuesExpressionList) {
if (row++ > 0) {
buff.append(", ");
}
buff.append('(');
buff.resetCount();
for (Expression e : expr) {
buff.appendExceptFirst(", ");
if (e == null) {
buff.append("DEFAULT");
} else {
buff.append(e.getSQL());
}
}
buff.append(')');
}
} else {
buff.append(query.getPlanSQL());
}
return buff.toString();
}
@Override
public void prepare() {
if (columns == null) {
if (valuesExpressionList.size() > 0 && valuesExpressionList.get(0).length == 0) {
// special case where table is used as a sequence
columns = new Column[0];
} else {
columns = targetTable.getColumns();
}
}
if (valuesExpressionList.size() > 0) {
for (Expression[] expr : valuesExpressionList) {
if (expr.length != columns.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
for (int i = 0; i < expr.length; i++) {
Expression e = expr[i];
if (e != null) {
expr[i] = e.optimize(session);
}
}
}
} else {
query.prepare();
if (query.getColumnCount() != columns.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
}
if (keys == null) {
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(targetTable.getSQL()).append(" SET ");
for (Column c : columns) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL()).append("=?");
}
buff.append(" WHERE ");
buff.resetCount();
for (Column c : keys) {
buff.appendExceptFirst(" AND ");
buff.append(c.getSQL()).append("=?");
}
String sql = buff.toString();
update = session.prepare(sql);
}
public void setSourceTableFilter(TableFilter sourceTableFilter) {
this.sourceTableFilter = sourceTableFilter;
}
public void addCondition(Expression condition) {
this.conditions .add(condition);
}
public Prepared getUpdateCommand() {
return updateCommand;
}
public void setUpdateCommand(Prepared updateCommand) {
this.updateCommand = updateCommand;
}
public Prepared getDeleteCommand() {
return deleteCommand;
}
public void setDeleteCommand(Prepared deleteCommand) {
this.deleteCommand = deleteCommand;
}
public Insert getInsertCommand() {
return insertCommand;
}
public void setInsertCommand(Insert insertCommand) {
this.insertCommand = insertCommand;
}
public void setQueryAlias(String alias) {
this.queryAlias = alias;
}
}
......@@ -40,6 +40,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
testDelete();
testMerge();
testCreateTable();
testNestedSQL();
}
private void testSimpleSelect() throws Exception {
......@@ -388,4 +389,53 @@ public class TestGeneralCommonTableQueries extends TestBase {
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testNestedSQL() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
PreparedStatement prep;
ResultSet rs;
prep = conn.prepareStatement(
"WITH T1 AS ( "+
" SELECT * "+
" FROM TABLE ( "+
" K VARCHAR = ('a', 'b'), "+
" V INTEGER = (1, 2) "+
" ) "+
"), "+
" "+
" "+
"T2 AS ( "+
" SELECT * "+
" FROM TABLE ( "+
" K VARCHAR = ('a', 'b'), "+
" V INTEGER = (3, 4) "+
" ) "+
"), "+
" "+
" "+
"JOIN_CTE AS ( "+
" SELECT T1.* "+
" "+
" FROM "+
" T1 "+
" JOIN T2 ON ( "+
" T1.K = T2.K "+
" ) "+
") "+
" "+
"SELECT * FROM JOIN_CTE");
rs = prep.executeQuery();
for (String keyLetter : new String[] { "a", "b" }) {
assertTrue(rs.next());
assertContains("ab",rs.getString(1));
assertEquals(rs.getString(1),keyLetter);
assertTrue(rs.getInt(2)!=0);
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
\ No newline at end of file
......@@ -10333,8 +10333,8 @@ explain with recursive r(n) as (
)
select n from r;
> PLAN
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> WITH RECURSIVE R(N) AS ( (SELECT 1 FROM SYSTEM_RANGE(1, 1) /* PUBLIC.RANGE_INDEX */) UNION ALL (SELECT (N + 1) FROM PUBLIC.R /* PUBLIC.R.tableScan */ WHERE N < 3) ) SELECT N FROM R /* null */
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> WITH RECURSIVE R(N) AS ( (SELECT 1 FROM SYSTEM_RANGE(1, 1) /* PUBLIC.RANGE_INDEX */) UNION ALL (SELECT (N + 1) FROM PUBLIC.R /* PUBLIC.R.tableScan */ WHERE N < 3) ) SELECT N FROM R R /* null */
> rows: 1
select sum(n) from (
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论