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

Asking for help at this point - not for merging

上级 687c103f
...@@ -159,6 +159,7 @@ import org.h2.value.ValueNull; ...@@ -159,6 +159,7 @@ import org.h2.value.ValueNull;
import org.h2.value.ValueString; import org.h2.value.ValueString;
import org.h2.value.ValueTime; import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp; import org.h2.value.ValueTimestamp;
import com.sun.tools.javac.util.Assert;
/** /**
* The parser is used to convert a SQL statement string to an command object. * The parser is used to convert a SQL statement string to an command object.
...@@ -738,6 +739,7 @@ public class Parser { ...@@ -738,6 +739,7 @@ public class Parser {
return filter.getTable().getColumn(columnName); return filter.getTable().getColumn(columnName);
} }
// TODO:(SM) parseUpdate
private Update parseUpdate() { private Update parseUpdate() {
Update command = new Update(session); Update command = new Update(session);
currentPrepared = command; currentPrepared = command;
...@@ -1050,6 +1052,7 @@ public class Parser { ...@@ -1050,6 +1052,7 @@ public class Parser {
private Merge parseMerge() { private Merge parseMerge() {
Merge command = new Merge(session); Merge command = new Merge(session);
currentPrepared = command; currentPrepared = command;
int start = lastParseIndex;
read("INTO"); read("INTO");
List<String> excludeIdentifiers = Arrays.asList("USING","KEY","VALUES"); List<String> excludeIdentifiers = Arrays.asList("USING","KEY","VALUES");
TableFilter targetTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers); TableFilter targetTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
...@@ -1057,7 +1060,7 @@ public class Parser { ...@@ -1057,7 +1060,7 @@ public class Parser {
Table table = command.getTargetTable(); Table table = command.getTargetTable();
if (readIf("USING")){ if (readIf("USING")){
return parseMergeUsing(command); return parseMergeUsing(command,start);
} }
if (readIf("(")) { if (readIf("(")) {
if (isSelect()) { if (isSelect()) {
...@@ -1094,17 +1097,32 @@ public class Parser { ...@@ -1094,17 +1097,32 @@ public class Parser {
return command; return command;
} }
/* /*
MERGE INTO targetTableName [t_alias] USING table_reference [s_alias] ON (condition) * TODO finish writing this MergeUsing
MERGE INTO targetTableName [[AS] t_alias] USING table_reference [[AS} s_alias] ON ( condition ,...)
WHEN MATCHED THEN WHEN MATCHED THEN
[UPDATE SET column1 = value1 [, column2 = value2 ...] | DELETE] [UPDATE SET column1 = value1 [, column2 = value2 ... WHERE ...]
[DELETE WHERE ...]
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]); INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);
table_reference ::= table / view / sub-query table_reference ::= table | view | ( sub-query )
The current implementation (for comparison) uses this syntax:
MERGE INTO tablename [(columnName1,...)]
[KEY (keyColumnName1,...)]
[ VALUES (expression1,...) | SELECT ...]
*/ */
/* TODO Finish coding*/ /* TODO: Finish coding parseMergeUsing */
private MergeUsing parseMergeUsing(Merge oldCommand) { private MergeUsing parseMergeUsing(Merge oldCommand, int start) {
if(schemaName==null){
schemaName = session.getCurrentSchemaName();
}
Schema schema = getSchema();
MergeUsing command = new MergeUsing(oldCommand); MergeUsing command = new MergeUsing(oldCommand);
currentPrepared = command;
if (readIf("(")) { if (readIf("(")) {
if (isSelect()) { if (isSelect()) {
...@@ -1112,6 +1130,10 @@ public class Parser { ...@@ -1112,6 +1130,10 @@ public class Parser {
read(")"); read(")");
} }
command.setQueryAlias(readFromAlias(null, Arrays.asList("ON"))); command.setQueryAlias(readFromAlias(null, Arrays.asList("ON")));
Table tableOrView = command.getQuery().getTables().toArray(new Table[]{null})[0];
TableFilter sourceTableFilter = new TableFilter(session, tableOrView, command.getQueryAlias(), rightsChecked,
(Select) command.getQuery(), 0, null);
command.setSourceTableFilter(sourceTableFilter);
} }
else{ else{
List<String> excludeIdentifiers = Arrays.asList("ON"); List<String> excludeIdentifiers = Arrays.asList("ON");
...@@ -1150,9 +1172,7 @@ public class Parser { ...@@ -1150,9 +1172,7 @@ public class Parser {
deleteCommand.setTableFilter(filter); deleteCommand.setTableFilter(filter);
parseDeleteGivenTable(deleteCommand,null,lastParseIndex); parseDeleteGivenTable(deleteCommand,null,lastParseIndex);
command.setDeleteCommand(deleteCommand); command.setDeleteCommand(deleteCommand);
} }
} }
if(readIf("WHEN")&&readIf("NOT")&&readIf("MATCHED")&&readIf("THEN")){ if(readIf("WHEN")&&readIf("NOT")&&readIf("MATCHED")&&readIf("THEN")){
if (readIf("INSERT")){ if (readIf("INSERT")){
...@@ -1163,6 +1183,19 @@ public class Parser { ...@@ -1163,6 +1183,19 @@ public class Parser {
} }
} }
if(command.getQueryAlias()!=null){
if(schema==null){
System.out.println("Why oh why is the schema null???");
System.out.println("schemaName="+schemaName);
throw DbException.getUnsupportedException("unexpected null schema");
}
String[] querySQLOutput = new String[]{null};
List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput);
System.out.println("pre:alias="+command.getQueryAlias()+",sql="+querySQLOutput[0]+",cte="+columnTemplateList);
TableView temporarySourceTableView = createTemporarySessionView(command.getQueryAlias(), querySQLOutput[0], columnTemplateList, false);
command.setTemporaryTableView(temporarySourceTableView);
}
setSQL(command, "MERGE", start);
return command; return command;
} }
...@@ -5102,7 +5135,7 @@ public class Parser { ...@@ -5102,7 +5135,7 @@ public class Parser {
if (readIf("(")) { if (readIf("(")) {
cols = parseColumnList(); cols = parseColumnList();
for (String c : cols) { for (String c : cols) {
// we dont really know the type of the column, so string will // we don't really know the type of the column, so string will
// have to do // have to do
columns.add(new Column(c, Value.STRING)); columns.add(new Column(c, Value.STRING));
} }
...@@ -5135,33 +5168,47 @@ public class Parser { ...@@ -5135,33 +5168,47 @@ public class Parser {
data.session = session; data.session = session;
recursiveTable = schema.createTable(data); recursiveTable = schema.createTable(data);
session.addLocalTempTable(recursiveTable); session.addLocalTempTable(recursiveTable);
String querySQL; List<Column> columnTemplateList;
List<Column> columnTemplateList = new ArrayList<Column>(); String[] querySQLOutput = new String[]{null};
try { try {
read("AS"); read("AS");
read("("); read("(");
Query withQuery = parseSelect(); Query withQuery = parseSelect();
read(")"); read(")");
withQuery.prepare(); columnTemplateList = createQueryColumnTemplateList(cols, withQuery, querySQLOutput);
querySQL = StringUtils.cache(withQuery.getPlanSQL());
ArrayList<Expression> withExpressions = withQuery.getExpressions();
for (int i = 0; i < withExpressions.size(); ++i) {
String columnName = cols != null ? cols[i]
: withExpressions.get(i).getColumnName();
columnTemplateList.add(new Column(columnName,
withExpressions.get(i).getType()));
}
} finally { } finally {
session.removeLocalTempTable(recursiveTable); session.removeLocalTempTable(recursiveTable);
} }
TableView view = createTemporarySessionView(tempViewName, querySQLOutput[0], columnTemplateList,true/*allowRecursiveQueryDetection*/);
return view;
}
private List<Column> createQueryColumnTemplateList(String[] cols, Query withQuery, String[] querySQLOutput) {
List<Column> columnTemplateList = new ArrayList<Column>();
withQuery.prepare();
querySQLOutput[0] = StringUtils.cache(withQuery.getPlanSQL());
ArrayList<Expression> withExpressions = withQuery.getExpressions();
for (int i = 0; i < withExpressions.size(); ++i) {
String columnName = cols != null ? cols[i]
: withExpressions.get(i).getColumnName();
columnTemplateList.add(new Column(columnName,
withExpressions.get(i).getType()));
}
return columnTemplateList;
}
private TableView createTemporarySessionView(String tempViewName, String querySQL,
List<Column> columnTemplateList, boolean allowRecursiveQueryDetection) {
Schema schema = getSchema();
int id = database.allocateObjectId(); int id = database.allocateObjectId();
// No easy way to determine if this is a recursive query up front, so we just compile // No easy way to determine if this is a recursive query up front, so we just compile
// it twice - once without the flag set, and if we didn't see a recursive term, // it twice - once without the flag set, and if we didn't see a recursive term,
// then we just compile it again. // then we just compile it again.
System.out.println("createTemporarySessionView="+tempViewName);
TableView view = new TableView(schema, id, tempViewName, querySQL, TableView view = new TableView(schema, id, tempViewName, querySQL,
parameters, columnTemplateList.toArray(new Column[0]), session, parameters, columnTemplateList.toArray(new Column[0]), session,
true/* recursive */, false); allowRecursiveQueryDetection/* recursive */, false);
if (!view.isRecursiveQueryDetected()) { if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) {
view = new TableView(schema, id, tempViewName, querySQL, parameters, view = new TableView(schema, id, tempViewName, querySQL, parameters,
columnTemplateList.toArray(new Column[0]), session, columnTemplateList.toArray(new Column[0]), session,
false/* recursive */, false); false/* recursive */, false);
......
...@@ -13,14 +13,16 @@ import org.h2.engine.Right; ...@@ -13,14 +13,16 @@ import org.h2.engine.Right;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord; import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter; import org.h2.expression.Parameter;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.ResultInterface; 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.PlanItem;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -32,10 +34,11 @@ public class MergeUsing extends Merge { ...@@ -32,10 +34,11 @@ public class MergeUsing extends Merge {
private TableFilter sourceTableFilter; private TableFilter sourceTableFilter;
private ArrayList<Expression> conditions = new ArrayList<Expression>(); private ArrayList<Expression> conditions = new ArrayList<Expression>();
private Prepared updateCommand; private Update updateCommand;
private Prepared deleteCommand; private Delete deleteCommand;
private Insert insertCommand; private Insert insertCommand;
private String queryAlias; private String queryAlias;
private TableView temporarySourceTableView;
public MergeUsing(Merge merge) { public MergeUsing(Merge merge) {
super(merge.getSession()); super(merge.getSession());
...@@ -49,6 +52,18 @@ public class MergeUsing extends Merge { ...@@ -49,6 +52,18 @@ public class MergeUsing extends Merge {
@Override @Override
public int update() { public int update() {
System.out.println("update");
if(targetTableFilter!=null){
targetTableFilter.startQuery(session);
targetTableFilter.reset();
}
if(sourceTableFilter!=null){
sourceTableFilter.startQuery(session);
sourceTableFilter.reset();
}
int count; int count;
checkRights(); checkRights();
setCurrentRowNumber(0); setCurrentRowNumber(0);
...@@ -56,7 +71,7 @@ public class MergeUsing extends Merge { ...@@ -56,7 +71,7 @@ public class MergeUsing extends Merge {
// process select query data for row creation // process select query data for row creation
ResultInterface rows = query.query(0); ResultInterface rows = query.query(0);
count = 0; count = 0;
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, true); targetTable.fire(session, evaluateTriggerMasks(), true);
targetTable.lock(session, true, false); targetTable.lock(session, true, false);
while (rows.next()) { while (rows.next()) {
count++; count++;
...@@ -76,10 +91,25 @@ public class MergeUsing extends Merge { ...@@ -76,10 +91,25 @@ public class MergeUsing extends Merge {
merge(newRow); merge(newRow);
} }
rows.close(); rows.close();
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, false); targetTable.fire(session, evaluateTriggerMasks(), false);
return count; return count;
} }
private int evaluateTriggerMasks() {
int masks = 0;
if(insertCommand!=null){
masks |= Trigger.INSERT;
}
if(updateCommand!=null){
masks |= Trigger.UPDATE;
}
if(deleteCommand!=null){
masks |= Trigger.DELETE;
}
return masks;
}
private void checkRights() { private void checkRights() {
if(insertCommand!=null){ if(insertCommand!=null){
session.getUser().checkRight(targetTable, Right.INSERT); session.getUser().checkRight(targetTable, Right.INSERT);
...@@ -116,11 +146,11 @@ public class MergeUsing extends Merge { ...@@ -116,11 +146,11 @@ public class MergeUsing extends Merge {
if(updateCommand!=null){ if(updateCommand!=null){
count+=updateCommand.update(); count+=updateCommand.update();
} }
if(deleteCommand!=null){ if(deleteCommand!=null && count==0){
count+=deleteCommand.update(); count+=deleteCommand.update();
} }
// if update does nothing, try an insert // if either updates do nothing, try an insert
if (count == 0) { if (count == 0) {
try { try {
targetTable.validateConvertUpdateSequence(session, row); targetTable.validateConvertUpdateSequence(session, row);
...@@ -161,13 +191,15 @@ public class MergeUsing extends Merge { ...@@ -161,13 +191,15 @@ public class MergeUsing extends Merge {
} }
private void addRowByInsert(Session session, Row row) { private void addRowByInsert(Session session, Row row) {
// TODO Auto-generated method stub System.out.println("addRowByInsert=(hashcode)"+row.hashCode());
targetTable.addRow(session, row); targetTable.addRow(session, row);
} }
@Override @Override
public String getPlanSQL() { public String getPlanSQL() {
System.out.println("getPlanSQL");
StatementBuilder buff = new StatementBuilder("MERGE INTO "); StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(targetTable.getSQL()).append('('); buff.append(targetTable.getSQL()).append('(');
for (Column c : columns) { for (Column c : columns) {
...@@ -210,8 +242,62 @@ public class MergeUsing extends Merge { ...@@ -210,8 +242,62 @@ public class MergeUsing extends Merge {
return buff.toString(); return buff.toString();
} }
/*
@Override
public void prepare() {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(session, tableFilter);
}
for (int i = 0, size = columns.size(); i < size; i++) {
Column c = columns.get(i);
Expression e = expressionMap.get(c);
e.mapColumns(tableFilter, 0);
expressionMap.put(c, e.optimize(session));
}
TableFilter[] filters = new TableFilter[] { tableFilter };
PlanItem item = tableFilter.getBestPlanItem(session, filters, 0,
ExpressionVisitor.allColumnsForTableFilters(filters));
tableFilter.setPlanItem(item);
tableFilter.prepare();
}
*/
/*
MERGE INTO targetTableName [[AS] t_alias] USING table_reference [[AS} s_alias] ON ( condition ,...)
WHEN MATCHED THEN
[UPDATE SET column1 = value1 [, column2 = value2 ... WHERE ...]
[DELETE WHERE ...]
WHEN NOT MATCHED THEN
INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);
table_reference ::= table | view | ( sub-query )
The current implementation (for comparison) uses this syntax:
MERGE INTO tablename [(columnName1,...)]
[KEY (keyColumnName1,...)]
[ VALUES (expression1,...) | SELECT ...]
*/
@Override @Override
public void prepare() { public void prepare() {
System.out.println("prepare:targetTableFilterAlias="+targetTableFilter.getTableAlias());
System.out.println("prepare:sourceTableFilterAlias="+sourceTableFilter.getTableAlias());
System.out.println("prepare:conditions="+conditions);
TableFilter[] filters = new TableFilter[] { sourceTableFilter, targetTableFilter };
for(Expression condition: conditions) {
System.out.println("condition="+condition+":op="+condition.getClass().getSimpleName());
filters[0].addJoin(filters[1], false, true, condition);
condition.mapColumns(sourceTableFilter, 0);
//condition.mapColumns(targetTableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(session, sourceTableFilter);
//condition.createIndexConditions(session, targetTableFilter);
}
if (columns == null) { if (columns == null) {
if (valuesExpressionList.size() > 0 && valuesExpressionList.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
...@@ -245,8 +331,30 @@ public class MergeUsing extends Merge { ...@@ -245,8 +331,30 @@ public class MergeUsing extends Merge {
} }
keys = idx.getColumns(); keys = idx.getColumns();
} }
String sql = buildPreparedSQL();
update = session.prepare(sql);
// Not sure how these sub-prepares will work...
if(updateCommand!=null){
updateCommand.prepare();
}
if(deleteCommand!=null){
deleteCommand.prepare();
}
if(insertCommand!=null){
insertCommand.prepare();
}
}
private String buildPreparedSQL() {
StatementBuilder buff = new StatementBuilder("UPDATE "); StatementBuilder buff = new StatementBuilder("UPDATE ");
buff.append(targetTable.getSQL()).append(" SET "); buff.append(targetTable.getSQL());
if(targetTableFilter.getTableAlias()!=null){
buff.append(" AS "+targetTableFilter.getTableAlias()+" ");
}
buff.append(" SET ");
for (Column c : columns) { for (Column c : columns) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(c.getSQL()).append("=?"); buff.append(c.getSQL()).append("=?");
...@@ -258,7 +366,7 @@ public class MergeUsing extends Merge { ...@@ -258,7 +366,7 @@ public class MergeUsing extends Merge {
buff.append(c.getSQL()).append("=?"); buff.append(c.getSQL()).append("=?");
} }
String sql = buff.toString(); String sql = buff.toString();
update = session.prepare(sql); return sql;
} }
public void setSourceTableFilter(TableFilter sourceTableFilter) { public void setSourceTableFilter(TableFilter sourceTableFilter) {
...@@ -273,7 +381,7 @@ public class MergeUsing extends Merge { ...@@ -273,7 +381,7 @@ public class MergeUsing extends Merge {
return updateCommand; return updateCommand;
} }
public void setUpdateCommand(Prepared updateCommand) { public void setUpdateCommand(Update updateCommand) {
this.updateCommand = updateCommand; this.updateCommand = updateCommand;
} }
...@@ -281,7 +389,7 @@ public class MergeUsing extends Merge { ...@@ -281,7 +389,7 @@ public class MergeUsing extends Merge {
return deleteCommand; return deleteCommand;
} }
public void setDeleteCommand(Prepared deleteCommand) { public void setDeleteCommand(Delete deleteCommand) {
this.deleteCommand = deleteCommand; this.deleteCommand = deleteCommand;
} }
...@@ -296,5 +404,16 @@ public class MergeUsing extends Merge { ...@@ -296,5 +404,16 @@ public class MergeUsing extends Merge {
public void setQueryAlias(String alias) { public void setQueryAlias(String alias) {
this.queryAlias = alias; this.queryAlias = alias;
} }
public String getQueryAlias() {
return this.queryAlias;
}
public Query getQuery() {
return query;
}
public void setTemporaryTableView(TableView temporarySourceTableView) {
this.temporarySourceTableView = temporarySourceTableView;
}
} }
...@@ -85,6 +85,9 @@ public abstract class Table extends SchemaObjectBase { ...@@ -85,6 +85,9 @@ public abstract class Table extends SchemaObjectBase {
public Table(Schema schema, int id, String name, boolean persistIndexes, public Table(Schema schema, int id, String name, boolean persistIndexes,
boolean persistData) { boolean persistData) {
System.out.println("newTable:schema="+schema);
System.out.println("newTable:name="+name);
System.out.println("newTable:schema="+schema+",database="+schema.getDatabase());
columnMap = schema.getDatabase().newStringMap(); columnMap = schema.getDatabase().newStringMap();
initSchemaObjectBase(schema, id, name, Trace.TABLE); initSchemaObjectBase(schema, id, name, Trace.TABLE);
this.persistIndexes = persistIndexes; this.persistIndexes = persistIndexes;
......
package org.h2.test.db;
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.h2.test.TestBase;
/**
* Test merge using syntax.
*/
public class TestMergeUsing extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
testMergeUsing();
}
private void testMergeUsing() throws Exception {
deleteDb("mergeUsingQueries");
Connection conn = getConnection("mergeUsingQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
int rowCount;
stat = conn.createStatement();
stat.execute("CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );");
prep = conn.prepareStatement("MERGE INTO PARENT AS P USING (SELECT 1 AS ID, 'Marcy' AS NAME) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)");
rowCount = prep.executeUpdate();
assertEquals(1,rowCount);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
for (int n : new int[] { 1 }) {
assertTrue(rs.next());
assertTrue(rs.getInt(1)!=0);
assertEquals(n, rs.getInt(2));
assertEquals("X1", rs.getString(3));
}
conn.close();
deleteDb("mergeUsingQueries");
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论