提交 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;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
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.
......@@ -738,6 +739,7 @@ public class Parser {
return filter.getTable().getColumn(columnName);
}
// TODO:(SM) parseUpdate
private Update parseUpdate() {
Update command = new Update(session);
currentPrepared = command;
......@@ -1050,6 +1052,7 @@ public class Parser {
private Merge parseMerge() {
Merge command = new Merge(session);
currentPrepared = command;
int start = lastParseIndex;
read("INTO");
List<String> excludeIdentifiers = Arrays.asList("USING","KEY","VALUES");
TableFilter targetTableFilter = readSimpleTableFilterWithAliasExcludes(0,excludeIdentifiers);
......@@ -1057,7 +1060,7 @@ public class Parser {
Table table = command.getTargetTable();
if (readIf("USING")){
return parseMergeUsing(command);
return parseMergeUsing(command,start);
}
if (readIf("(")) {
if (isSelect()) {
......@@ -1094,17 +1097,32 @@ public class Parser {
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
[UPDATE SET column1 = value1 [, column2 = value2 ...] | DELETE]
[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
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*/
private MergeUsing parseMergeUsing(Merge oldCommand) {
/* TODO: Finish coding parseMergeUsing */
private MergeUsing parseMergeUsing(Merge oldCommand, int start) {
if(schemaName==null){
schemaName = session.getCurrentSchemaName();
}
Schema schema = getSchema();
MergeUsing command = new MergeUsing(oldCommand);
currentPrepared = command;
if (readIf("(")) {
if (isSelect()) {
......@@ -1112,6 +1130,10 @@ public class Parser {
read(")");
}
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{
List<String> excludeIdentifiers = Arrays.asList("ON");
......@@ -1150,9 +1172,7 @@ public class Parser {
deleteCommand.setTableFilter(filter);
parseDeleteGivenTable(deleteCommand,null,lastParseIndex);
command.setDeleteCommand(deleteCommand);
}
}
}
if(readIf("WHEN")&&readIf("NOT")&&readIf("MATCHED")&&readIf("THEN")){
if (readIf("INSERT")){
......@@ -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;
}
......@@ -5102,7 +5135,7 @@ public class Parser {
if (readIf("(")) {
cols = parseColumnList();
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
columns.add(new Column(c, Value.STRING));
}
......@@ -5135,33 +5168,47 @@ public class Parser {
data.session = session;
recursiveTable = schema.createTable(data);
session.addLocalTempTable(recursiveTable);
String querySQL;
List<Column> columnTemplateList = new ArrayList<Column>();
List<Column> columnTemplateList;
String[] querySQLOutput = new String[]{null};
try {
read("AS");
read("(");
Query withQuery = parseSelect();
read(")");
withQuery.prepare();
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()));
}
columnTemplateList = createQueryColumnTemplateList(cols, withQuery, querySQLOutput);
} finally {
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();
// 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,
// then we just compile it again.
System.out.println("createTemporarySessionView="+tempViewName);
TableView view = new TableView(schema, id, tempViewName, querySQL,
parameters, columnTemplateList.toArray(new Column[0]), session,
true/* recursive */, false);
if (!view.isRecursiveQueryDetected()) {
allowRecursiveQueryDetection/* recursive */, false);
if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) {
view = new TableView(schema, id, tempViewName, querySQL, parameters,
columnTemplateList.toArray(new Column[0]), session,
false/* recursive */, false);
......
......@@ -13,14 +13,16 @@ import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
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.PlanItem;
import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.util.StatementBuilder;
import org.h2.value.Value;
......@@ -32,10 +34,11 @@ public class MergeUsing extends Merge {
private TableFilter sourceTableFilter;
private ArrayList<Expression> conditions = new ArrayList<Expression>();
private Prepared updateCommand;
private Prepared deleteCommand;
private Update updateCommand;
private Delete deleteCommand;
private Insert insertCommand;
private String queryAlias;
private TableView temporarySourceTableView;
public MergeUsing(Merge merge) {
super(merge.getSession());
......@@ -49,6 +52,18 @@ public class MergeUsing extends Merge {
@Override
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;
checkRights();
setCurrentRowNumber(0);
......@@ -56,7 +71,7 @@ public class MergeUsing extends Merge {
// process select query data for row creation
ResultInterface rows = query.query(0);
count = 0;
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
targetTable.fire(session, evaluateTriggerMasks(), true);
targetTable.lock(session, true, false);
while (rows.next()) {
count++;
......@@ -76,10 +91,25 @@ public class MergeUsing extends Merge {
merge(newRow);
}
rows.close();
targetTable.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
targetTable.fire(session, evaluateTriggerMasks(), false);
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() {
if(insertCommand!=null){
session.getUser().checkRight(targetTable, Right.INSERT);
......@@ -116,11 +146,11 @@ public class MergeUsing extends Merge {
if(updateCommand!=null){
count+=updateCommand.update();
}
if(deleteCommand!=null){
if(deleteCommand!=null && count==0){
count+=deleteCommand.update();
}
// if update does nothing, try an insert
// if either updates do nothing, try an insert
if (count == 0) {
try {
targetTable.validateConvertUpdateSequence(session, row);
......@@ -161,13 +191,15 @@ public class MergeUsing extends Merge {
}
private void addRowByInsert(Session session, Row row) {
// TODO Auto-generated method stub
System.out.println("addRowByInsert=(hashcode)"+row.hashCode());
targetTable.addRow(session, row);
}
@Override
public String getPlanSQL() {
System.out.println("getPlanSQL");
StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(targetTable.getSQL()).append('(');
for (Column c : columns) {
......@@ -210,8 +242,62 @@ public class MergeUsing extends Merge {
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
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 (valuesExpressionList.size() > 0 && valuesExpressionList.get(0).length == 0) {
// special case where table is used as a sequence
......@@ -245,8 +331,30 @@ public class MergeUsing extends Merge {
}
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 ");
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) {
buff.appendExceptFirst(", ");
buff.append(c.getSQL()).append("=?");
......@@ -258,7 +366,7 @@ public class MergeUsing extends Merge {
buff.append(c.getSQL()).append("=?");
}
String sql = buff.toString();
update = session.prepare(sql);
return sql;
}
public void setSourceTableFilter(TableFilter sourceTableFilter) {
......@@ -273,7 +381,7 @@ public class MergeUsing extends Merge {
return updateCommand;
}
public void setUpdateCommand(Prepared updateCommand) {
public void setUpdateCommand(Update updateCommand) {
this.updateCommand = updateCommand;
}
......@@ -281,7 +389,7 @@ public class MergeUsing extends Merge {
return deleteCommand;
}
public void setDeleteCommand(Prepared deleteCommand) {
public void setDeleteCommand(Delete deleteCommand) {
this.deleteCommand = deleteCommand;
}
......@@ -296,5 +404,16 @@ public class MergeUsing extends Merge {
public void setQueryAlias(String 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 {
public Table(Schema schema, int id, String name, boolean persistIndexes,
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();
initSchemaObjectBase(schema, id, name, Trace.TABLE);
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论