You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2013/10/24 14:42:00 UTC
svn commit: r1535360 [1/2] - in /db/derby/code/trunk/java:
engine/org/apache/derby/iapi/services/io/ engine/org/apache/derby/iapi/sql/
engine/org/apache/derby/iapi/sql/execute/ engine/org/apache/derby/impl/sql/
engine/org/apache/derby/impl/sql/compile/...
Author: rhillegas
Date: Thu Oct 24 12:41:59 2013
New Revision: 1535360
URL: http://svn.apache.org/r1535360
Log:
DERBY-3155: Add support for DELETE action of MERGE statement; checking in derby-3155-04-af-deleteAction.diff.
Added:
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/MatchingClauseConstantAction.java (with props)
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/MergeConstantAction.java (with props)
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/MergeResultSet.java (with props)
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ConstantAction.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CurrentOfNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BulkTableScanResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/HashScanResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScanResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java
db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java Thu Oct 24 12:41:59 2013
@@ -534,6 +534,8 @@ private static final String[] TwoByte
/* 473 */ "org.apache.derby.impl.sql.catalog.CoreDDFinderClassInfo",
/* 474 */ "org.apache.derby.catalog.types.UDTAliasInfo",
/* 475 */ "org.apache.derby.catalog.types.AggregateAliasInfo",
+ /* 476 */ "org.apache.derby.impl.sql.execute.MatchingClauseConstantAction",
+ /* 477 */ "org.apache.derby.impl.sql.execute.MergeConstantAction",
};
/** Return the number of two-byte format ids */
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java Thu Oct 24 12:41:59 2013
@@ -952,6 +952,18 @@ public interface StoredFormatIds {
(MIN_ID_2 + 39);
/**
+ class org.apache.derby.impl.sql.execute.MatchingClauseConstantAction
+ */
+ static public final int MATCHING_CLAUSE_CONSTANT_ACTION_V01_ID =
+ (MIN_ID_2 + 476);
+
+ /**
+ class org.apache.derby.impl.sql.execute.MatchingClauseConstantAction
+ */
+ static public final int MERGE_CONSTANT_ACTION_V01_ID =
+ (MIN_ID_2 + 477);
+
+ /**
*/
static public final int UNUSED_2_204 =
(MIN_ID_2 + 204);
@@ -1679,7 +1691,7 @@ public interface StoredFormatIds {
* Make sure this is updated when a new module is added
*/
public static final int MAX_ID_2 =
- (MIN_ID_2 + 475);
+ (MIN_ID_2 + 477);
// DO NOT USE 4 BYTE IDS ANYMORE
static public final int MAX_ID_4 =
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java Thu Oct 24 12:41:59 2013
@@ -544,6 +544,19 @@ public interface Activation extends Depe
*/
public java.sql.ResultSet getTargetVTI();
+ /**
+ * Push a ConstantAction to be returned by getConstantAction().
+ * Returns the newConstantAction.
+ */
+ public ConstantAction pushConstantAction( ConstantAction newConstantAction );
+
+ /**
+ * Pop the ConstantAction stack, returning the element which was just popped
+ * off the stack.
+ */
+ public ConstantAction popConstantAction();
+
+ /** Get the top ConstantAction on the stack without changing the stack. */
public ConstantAction getConstantAction();
//store a reference to the parent table result sets
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ConstantAction.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ConstantAction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ConstantAction.java Thu Oct 24 12:41:59 2013
@@ -41,6 +41,13 @@ import org.apache.derby.catalog.UUID;
public interface ConstantAction
{
+ /** clauseType for WHEN NOT MATCHED ... THEN INSERT */
+ public static final int WHEN_NOT_MATCHED_THEN_INSERT = 0;
+ /** clauseType for WHEN MATCHED ... THEN UPDATE */
+ public static final int WHEN_MATCHED_THEN_UPDATE = 1;
+ /** clauseType for WHEN MATCHED ... THEN DELETE */
+ public static final int WHEN_MATCHED_THEN_DELETE = 2;
+
/**
* Run the ConstantAction.
*
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java Thu Oct 24 12:41:59 2013
@@ -169,6 +169,20 @@ public interface ResultSetFactory {
throws StandardException;
/**
+ A MERGE result set simply reports that it completed, and
+ the number of rows that it INSERTed/UPDATEd/DELETEdd. It does not return rows.
+ The delete has been completed once the
+ MERGE result set is available.
+
+ @param drivingLeftJoin the result set from which to take rows to
+ be drive the INSERT/UPDATE/DELETE operations.
+ @return the MERGE operation as a result set.
+ @exception StandardException thrown when unable to perform the work
+ */
+ ResultSet getMergeResultSet(NoPutResultSet drivingLeftJoin)
+ throws StandardException;
+
+ /**
A delete Cascade result set simply reports that it completed, and
the number of rows deleted. It does not return rows.
The delete has been completed once the
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java Thu Oct 24 12:41:59 2013
@@ -772,6 +772,16 @@ final public class GenericActivationHold
return ac.isCursorActivation();
}
+ public ConstantAction pushConstantAction( ConstantAction newConstantAction )
+ {
+ return ac.pushConstantAction( newConstantAction );
+ }
+
+ public ConstantAction popConstantAction()
+ {
+ return ac.popConstantAction();
+ }
+
public ConstantAction getConstantAction() {
return ac.getConstantAction();
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CurrentOfNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CurrentOfNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CurrentOfNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CurrentOfNode.java Thu Oct 24 12:41:59 2013
@@ -69,6 +69,9 @@ public final class CurrentOfNode extends
private TableName baseTableName;
private CostEstimate singleScanCostEstimate;
+ // dummy variables for compiling a CurrentOfNode in the DELETE action of a MERGE statement
+ private FromBaseTable dummyTargetTable;
+
//
// initializers
//
@@ -81,6 +84,25 @@ public final class CurrentOfNode extends
cursorName = cursor;
}
+ /**
+ * <p>
+ * Construct a dummy CurrentOfNode just for compiling the DELETE action of a MERGE
+ * statement.
+ * </p>
+ */
+ static CurrentOfNode makeForMerge
+ (
+ String cursorName,
+ FromBaseTable dummyTargetTable,
+ ContextManager cm
+ )
+ {
+ CurrentOfNode node = new CurrentOfNode( null, cursorName, null, cm );
+ node.dummyTargetTable = dummyTargetTable;
+
+ return node;
+ }
+
/*
* Optimizable interface
*/
@@ -264,6 +286,10 @@ public final class CurrentOfNode extends
ResultColumn getMatchingColumn(ColumnReference columnReference)
throws StandardException {
+ // if this is a dummy CurrentOfNode cooked up to compile a DELETE action
+ // of a MERGE statement, then short-circuit the matching column lookup
+ if ( dummyTargetTable != null ) { return dummyTargetTable.getMatchingColumn( columnReference ); }
+
ResultColumn resultColumn = null;
TableName columnsTableName;
@@ -511,6 +537,10 @@ public final class CurrentOfNode extends
@Override
String getExposedName()
{
+ // short-circuit for dummy CurrentOfNode cooked up to support
+ // the DELETE action of a MERGE statement
+ if ( dummyTargetTable != null ) { return dummyTargetTable.getExposedName(); }
+
return exposedTableName.getFullTableName();
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java Thu Oct 24 12:41:59 2013
@@ -103,12 +103,21 @@ abstract class DMLModStatementNode exten
protected boolean isDependentTable;
protected int[][] fkColArrays;
protected TableName synonymTableName;
+ protected MatchingClauseNode matchingClause;
+
/** Set of dependent tables for cascading deletes. */
Set<String> dependentTables;
- DMLModStatementNode(ResultSetNode resultSet, ContextManager cm) {
+ DMLModStatementNode
+ (
+ ResultSetNode resultSet,
+ MatchingClauseNode matchingClause,
+ ContextManager cm
+ )
+ {
super(resultSet, cm);
+ this.matchingClause = matchingClause;
statementType = getStatementType();
}
@@ -117,18 +126,27 @@ abstract class DMLModStatementNode exten
*
* @param resultSet A ResultSetNode for the result set of the
* DML statement
+ * @param matchingClause Non-null if this DML is part of a MATCHED clause of a MERGE statement.
* @param statementType used by nodes that allocate a DMLMod directly
* (rather than inheriting it).
* @param cm The context manager
*/
- DMLModStatementNode(ResultSetNode resultSet,
- int statementType,
- ContextManager cm)
+ DMLModStatementNode
+ (
+ ResultSetNode resultSet,
+ MatchingClauseNode matchingClause,
+ int statementType,
+ ContextManager cm
+ )
{
super(resultSet, cm);
+ this.matchingClause = matchingClause;
this.statementType = statementType;
}
+ /** Returns true if this DMLModStatement a [ NOT ] MATCHED action of a MERGE statement */
+ public boolean inMatchingClause() { return matchingClause != null; }
+
void setTarget(QueryTreeNode targetName)
{
if (targetName instanceof TableName)
@@ -1624,8 +1642,17 @@ abstract class DMLModStatementNode exten
@Override
public void optimizeStatement() throws StandardException
{
- /* First optimize the query */
- super.optimizeStatement();
+ //
+ // If this is the INSERT/UPDATE/DELETE action of a MERGE statement,
+ // then we don't need to optimize the dummy driving result set, which
+ // is never actually run.
+ //
+ // don't need to optimize the dummy SELECT, which is never actually run
+ if ( !inMatchingClause() )
+ {
+ /* First optimize the query */
+ super.optimizeStatement();
+ }
/* In language we always set it to row lock, it's up to store to
* upgrade it to table lock. This makes sense for the default read
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java Thu Oct 24 12:41:59 2013
@@ -84,13 +84,19 @@ class DeleteNode extends DMLModStatement
* @param targetTableName The name of the table to delete from
* @param queryExpression The query expression that will generate
* the rows to delete from the given table
+ * @param matchingClause Non-null if this DML is part of a MATCHED clause of a MERGE statement.
* @param cm The context manager
*/
- DeleteNode(TableName targetTableName,
- ResultSetNode queryExpression,
- ContextManager cm) {
- super(queryExpression, cm);
+ DeleteNode
+ (
+ TableName targetTableName,
+ ResultSetNode queryExpression,
+ MatchingClauseNode matchingClause,
+ ContextManager cm
+ )
+ {
+ super( queryExpression, matchingClause, cm );
this.targetTableName = targetTableName;
}
@@ -130,9 +136,10 @@ class DeleteNode extends DMLModStatement
CurrentRowLocationNode rowLocationNode;
TableName cursorTargetTableName = null;
CurrentOfNode currentOfNode = null;
-
+
DataDictionary dataDictionary = getDataDictionary();
- super.bindTables(dataDictionary);
+ // for DELETE clause of a MERGE statement, the tables have already been bound
+ if ( !inMatchingClause() ) { super.bindTables(dataDictionary); }
// wait to bind named target table until the underlying
// cursor is bound, so that we can get it from the
@@ -149,7 +156,8 @@ class DeleteNode extends DMLModStatement
{
currentOfNode = (CurrentOfNode) targetTable;
- cursorTargetTableName = currentOfNode.getBaseCursorTargetTableName();
+ cursorTargetTableName = inMatchingClause() ?
+ targetTableName : currentOfNode.getBaseCursorTargetTableName();
// instead of an assert, we might say the cursor is not updatable.
if (SanityManager.DEBUG)
SanityManager.ASSERT(cursorTargetTableName != null);
@@ -191,9 +199,11 @@ class DeleteNode extends DMLModStatement
verifyTargetTable();
/* Generate a select list for the ResultSetNode - CurrentRowLocation(). */
- if (SanityManager.DEBUG)
+ if ( SanityManager.DEBUG )
+ {
SanityManager.ASSERT((resultSet.resultColumns == null),
"resultColumns is expected to be null until bind time");
+ }
if (targetTable instanceof FromVTI)
@@ -207,6 +217,7 @@ class DeleteNode extends DMLModStatement
}
else
{
+
/*
** Start off assuming no columns from the base table
** are needed in the rcl.
@@ -256,7 +267,13 @@ class DeleteNode extends DMLModStatement
/* Force the added columns to take on the table's correlation name, if any */
correlateAddedColumns( resultColumnList, targetTable );
- /* Set the new result column list in the result set */
+ /* Add the new result columns to the driving result set */
+ ResultColumnList originalRCL = resultSet.resultColumns;
+ if ( originalRCL != null )
+ {
+ originalRCL.appendResultColumns( resultColumnList, false );
+ resultColumnList = originalRCL;
+ }
resultSet.setResultColumns(resultColumnList);
}
@@ -490,7 +507,6 @@ class DeleteNode extends DMLModStatement
void generate(ActivationClassBuilder acb, MethodBuilder mb)
throws StandardException
{
-
// If the DML is on the temporary table, generate the code to
// mark temporary table as modified in the current UOW. After
// DERBY-827 this must be done in execute() since
@@ -503,7 +519,16 @@ class DeleteNode extends DMLModStatement
acb.pushGetResultSetFactoryExpression(mb);
acb.newRowLocationScanResultSetName();
- resultSet.generate(acb, mb); // arg 1
+
+ // arg 1
+ if ( inMatchingClause() )
+ {
+ matchingClause.generateResultSetField( acb, mb );
+ }
+ else
+ {
+ resultSet.generate( acb, mb );
+ }
String resultSetGetter;
int argCount;
@@ -565,7 +590,6 @@ class DeleteNode extends DMLModStatement
mb.setField(arrayField);
for(int index=0 ; index < dependentNodes.length ; index++)
{
-
dependentNodes[index].setRefActionInfo(fkIndexConglomNumbers[index],
fkColArrays[index],
parentResultSetId,
@@ -728,7 +752,6 @@ class DeleteNode extends DMLModStatement
private DeleteNode getEmptyDeleteNode(String schemaName, String targetTableName)
throws StandardException
{
-
ValueNode whereClause = null;
TableName tableName =
@@ -759,8 +782,7 @@ class DeleteNode extends DMLModStatement
null, /* optimizer override plan */
getContextManager());
- return new DeleteNode(tableName, rs, getContextManager());
-
+ return new DeleteNode(tableName, rs, null, getContextManager());
}
@@ -803,8 +825,7 @@ class DeleteNode extends DMLModStatement
null, /* optimizer override plan */
getContextManager());
- return new UpdateNode(tableName, sn, false, getContextManager());
-
+ return new UpdateNode(tableName, sn, null, getContextManager());
}
@@ -845,7 +866,7 @@ class DeleteNode extends DMLModStatement
}
}
- super.optimizeStatement();
+ super.optimizeStatement();
}
/**
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java Thu Oct 24 12:41:59 2013
@@ -191,6 +191,9 @@ class FromBaseTable extends FromTable
// true if we are running with sql authorization and this is the SYSUSERS table
private boolean authorizeSYSUSERS;
+ // non-null if we need to return a row location column
+ private String rowLocationColumnName;
+
/**
* Constructor for a table in a FROM list. Parameters are as follows:
*
@@ -236,6 +239,12 @@ class FromBaseTable extends FromTable
templateColumns = resultColumns;
}
+ /** Set the name of the row location column */
+ void setRowLocationColumnName( String rowLocationColumnName )
+ {
+ this.rowLocationColumnName = rowLocationColumnName;
+ }
+
/**
* no LOJ reordering for base table.
*/
@@ -2794,7 +2803,11 @@ class FromBaseTable extends FromTable
columnReference.setColumnNumber(
resultColumn.getColumnPosition());
- if (tableDescriptor != null)
+ // set the column-referenced bit if this is not the row location column
+ if (
+ (tableDescriptor != null) &&
+ ( (rowLocationColumnName == null) || !(rowLocationColumnName.equals( columnReference.getColumnName() )) )
+ )
{
FormatableBitSet referencedColumnMap = tableDescriptor.getReferencedColumnMap();
if (referencedColumnMap == null)
@@ -3324,7 +3337,12 @@ class FromBaseTable extends FromTable
@Override
void generate(ActivationClassBuilder acb, MethodBuilder mb)
throws StandardException
- {
+ {
+ if ( rowLocationColumnName != null )
+ {
+ resultColumns.conglomerateId = tableDescriptor.getHeapConglomerateId();
+ }
+
generateResultSet( acb, mb );
/*
@@ -3874,6 +3892,18 @@ class FromBaseTable extends FromTable
rcList.addResultColumn(resultColumn);
}
+ // add a row location column as necessary
+ if ( rowLocationColumnName != null )
+ {
+ CurrentRowLocationNode rowLocationNode = new CurrentRowLocationNode( getContextManager() );
+ ResultColumn rowLocationColumn = new ResultColumn
+ ( rowLocationColumnName, rowLocationNode, getContextManager() );
+ rowLocationColumn.markGenerated();
+ rowLocationNode.bindExpression( null, null, null );
+ rowLocationColumn.bindResultColumnToExpression();
+ rcList.addResultColumn( rowLocationColumn );
+ }
+
return rcList;
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java Thu Oct 24 12:41:59 2013
@@ -98,6 +98,7 @@ public final class InsertNode extends DM
* it out.
* @param queryExpression The query expression that will generate
* the rows to insert into the given table
+ * @param matchingClause Non-null if this DML is part of a MATCHED clause of a MERGE statement.
* @param targetProperties The properties specified on the target table
* @param orderByList The order by list for the source result set,
* null if no order by list
@@ -112,6 +113,7 @@ public final class InsertNode extends DM
QueryTreeNode targetName,
ResultColumnList insertColumns,
ResultSetNode queryExpression,
+ MatchingClauseNode matchingClause,
Properties targetProperties,
OrderByList orderByList,
ValueNode offset,
@@ -123,7 +125,7 @@ public final class InsertNode extends DM
* any properties, so we've kludged the code to get the
* right statementType for a bulk insert replace.
*/
- super(queryExpression, getStatementType(targetProperties), cm);
+ super(queryExpression, matchingClause, getStatementType(targetProperties), cm);
setTarget(targetName);
targetColumnList = insertColumns;
this.targetProperties = targetProperties;
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java Thu Oct 24 12:41:59 2013
@@ -21,11 +21,21 @@
package org.apache.derby.impl.sql.compile;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.HashMap;
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.ClassName;
import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.compiler.LocalField;
+import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
+import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.shared.common.sanity.SanityManager;
+import org.apache.derby.iapi.sql.compile.CompilerContext;
+import org.apache.derby.iapi.sql.compile.Visitor;
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;
+import org.apache.derby.iapi.sql.execute.ConstantAction;
/**
* Node representing a WHEN MATCHED or WHEN NOT MATCHED clause
@@ -41,6 +51,8 @@ public class MatchingClauseNode extends
//
///////////////////////////////////////////////////////////////////////////////////
+ private static final String CURRENT_OF_NODE_NAME = "$MERGE_CURRENT";
+
///////////////////////////////////////////////////////////////////////////////////
//
// STATE
@@ -53,9 +65,22 @@ public class MatchingClauseNode extends
private ResultColumnList _insertColumns;
private ResultColumnList _insertValues;
+ //
// filled in at bind() time
+ //
+
+ /** the INSERT/UPDATE/DELETE statement of this WHEN [ NOT ] MATCHED clause */
private DMLModStatementNode _dml;
+ /** the columns in the temporary conglomerate which drives the INSERT/UPDATE/DELETE */
+ private ResultColumnList _thenColumns;
+ private int[] _thenColumnOffsets;
+
+ // Filled in at generate() time
+ private int _clauseNumber;
+ private String _actionMethodName;
+ private String _resultSetFieldName;
+
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTORS/FACTORY METHODS
@@ -83,7 +108,7 @@ public class MatchingClauseNode extends
}
/** Make a WHEN MATCHED ... THEN UPDATE clause */
- public static MatchingClauseNode makeUpdateClause
+ static MatchingClauseNode makeUpdateClause
(
ValueNode matchingRefinement,
ResultColumnList updateColumns,
@@ -94,7 +119,7 @@ public class MatchingClauseNode extends
}
/** Make a WHEN MATCHED ... THEN DELETE clause */
- public static MatchingClauseNode makeDeleteClause
+ static MatchingClauseNode makeDeleteClause
(
ValueNode matchingRefinement,
ContextManager cm
@@ -104,7 +129,7 @@ public class MatchingClauseNode extends
}
/** Make a WHEN NOT MATCHED ... THEN INSERT clause */
- public static MatchingClauseNode makeInsertClause
+ static MatchingClauseNode makeInsertClause
(
ValueNode matchingRefinement,
ResultColumnList insertColumns,
@@ -122,17 +147,23 @@ public class MatchingClauseNode extends
///////////////////////////////////////////////////////////////////////////////////
/** Return true if this is a WHEN MATCHED ... UPDATE clause */
- public boolean isUpdateClause() { return (_updateColumns != null); }
+ boolean isUpdateClause() { return (_updateColumns != null); }
/** Return true if this is a WHEN NOT MATCHED ... INSERT clause */
- public boolean isInsertClause() { return (_insertValues != null); }
+ boolean isInsertClause() { return (_insertValues != null); }
/** Return true if this is a WHEN MATCHED ... DELETE clause */
- public boolean isDeleteClause() { return !( isUpdateClause() || isInsertClause() ); }
+ boolean isDeleteClause() { return !( isUpdateClause() || isInsertClause() ); }
/** Return the bound DML statement--returns null if called before binding */
- public DMLModStatementNode getDML() { return _dml; }
-
+ DMLModStatementNode getDML() { return _dml; }
+
+ /**
+ * Return the list of columns which form the rows of the ResultSet which drive
+ * the INSERT/UPDATE/DELETE actions.
+ */
+ ResultColumnList getBufferedColumns() { return _thenColumns; }
+
///////////////////////////////////////////////////////////////////////////////////
//
// bind() BEHAVIOR
@@ -140,33 +171,103 @@ public class MatchingClauseNode extends
///////////////////////////////////////////////////////////////////////////////////
/** Bind this WHEN [ NOT ] MATCHED clause against the parent JoinNode */
- public void bind( JoinNode joinNode, FromTable targetTable )
+ void bind
+ (
+ DataDictionary dd,
+ MergeNode mergeNode,
+ FromList fullFromList,
+ FromBaseTable targetTable
+ )
throws StandardException
{
- String clauseType = isInsertClause() ? "WHEN NOT MATCHED" : "WHEN MATCHED";
+ bindExpressions( mergeNode, fullFromList, targetTable );
+
+ if ( isDeleteClause() ) { bindDelete( dd, fullFromList, targetTable ); }
+ if ( isUpdateClause() ) { bindUpdate( dd, fullFromList, targetTable ); }
+ if ( isInsertClause() ) { bindInsert( dd, mergeNode, fullFromList, targetTable ); }
- // For WHEN NOT MATCHED clauses, the target table is not in scope.
- boolean useTargetTable = !isInsertClause();
+ bindExpressions( _thenColumns, fullFromList );
+ }
+ /** Bind the optional refinement condition in the MATCHED clause */
+ void bindRefinement( MergeNode mergeNode, FromList fullFromList ) throws StandardException
+ {
if ( _matchingRefinement != null )
{
- _matchingRefinement = joinNode.bindExpression
- ( _matchingRefinement, true, useTargetTable, clauseType );
+ mergeNode.bindExpression( _matchingRefinement, fullFromList );
}
+ }
- if ( isDeleteClause() ) { bindDelete( joinNode, targetTable ); }
- if ( isUpdateClause() ) { bindUpdate( joinNode, targetTable ); }
- if ( isInsertClause() ) { bindInsert( joinNode, targetTable ); }
+ /** Bind the expressions in this MATCHED clause */
+ private void bindExpressions
+ (
+ MergeNode mergeNode,
+ FromList fullFromList,
+ FromTable targetTable
+ )
+ throws StandardException
+ {
+ _thenColumns = new ResultColumnList( getContextManager() );
+
+ if ( isUpdateClause() )
+ {
+ // needed to make the UpdateNode bind
+ _updateColumns.replaceOrForbidDefaults( targetTable.getTableDescriptor(), _updateColumns, true );
+
+ bindExpressions( _updateColumns, fullFromList );
+ }
+ else if ( isInsertClause() )
+ {
+ // needed to make the SelectNode bind
+ _insertValues.replaceOrForbidDefaults( targetTable.getTableDescriptor(), _insertColumns, true );
+ bindExpressions( _insertValues, fullFromList );
+ }
}
+
+ /** Collect the columns mentioned by expressions in this MATCHED clause */
+ void getColumnsInExpressions
+ (
+ MergeNode mergeNode,
+ HashMap<String,ColumnReference> drivingColumnMap
+ )
+ throws StandardException
+ {
+ if ( _matchingRefinement != null )
+ {
+ mergeNode.getColumnsInExpression( drivingColumnMap, _matchingRefinement );
+ }
+ if ( isUpdateClause() )
+ {
+ // get all columns mentioned on the right side of SET operators in WHEN MATCHED ... THEN UPDATE clauses
+ for ( ResultColumn rc : _updateColumns )
+ {
+ mergeNode.getColumnsInExpression( drivingColumnMap, rc.getExpression() );
+ }
+ }
+ else if ( isInsertClause() )
+ {
+ // get all columns mentioned in the VALUES subclauses of WHEN NOT MATCHED ... THEN INSERT clauses
+ for ( ResultColumn rc : _insertValues )
+ {
+ mergeNode.getColumnsInExpression( drivingColumnMap, rc.getExpression() );
+ }
+ }
+ }
+
/** Bind a WHEN MATCHED ... THEN UPDATE clause */
- private void bindUpdate( JoinNode joinNode, FromTable targetTable )
+ private void bindUpdate
+ (
+ DataDictionary dd,
+ FromList fullFromList,
+ FromTable targetTable
+ )
throws StandardException
{
SelectNode selectNode = new SelectNode
(
_updateColumns,
- joinNode.makeFromList( true, true ),
+ fullFromList,
null, // where clause
null, // group by list
null, // having clause
@@ -174,50 +275,82 @@ public class MatchingClauseNode extends
null, // optimizer plan override
getContextManager()
);
- _dml = new UpdateNode( targetTable.getTableName(), selectNode, true, getContextManager() );
+ _dml = new UpdateNode( targetTable.getTableName(), selectNode, this, getContextManager() );
_dml.bindStatement();
}
/** Bind a WHEN MATCHED ... THEN DELETE clause */
- private void bindDelete( JoinNode joinNode, FromTable targetTable )
+ private void bindDelete
+ (
+ DataDictionary dd,
+ FromList fullFromList,
+ FromBaseTable targetTable
+ )
throws StandardException
{
- SelectNode selectNode = new SelectNode
+ CurrentOfNode currentOfNode = CurrentOfNode.makeForMerge
+ ( CURRENT_OF_NODE_NAME, targetTable, getContextManager() );
+ FromList fromList = new FromList( getContextManager() );
+ fromList.addFromTable( currentOfNode );
+ SelectNode selectNode = new SelectNode
(
- null, // select list
- joinNode.makeFromList( true, true ),
- null, // where clause
- null, // group by list
- null, // having clause
- null, // window list
- null, // optimizer plan override
+ null,
+ fromList, /* FROM list */
+ null, /* WHERE clause */
+ null, /* GROUP BY list */
+ null, /* having clause */
+ null, /* window list */
+ null, /* optimizer plan override */
getContextManager()
);
- _dml = new DeleteNode( targetTable.getTableName(), selectNode, getContextManager() );
+ _dml = new DeleteNode( targetTable.getTableName(), selectNode, this, getContextManager() );
_dml.bindStatement();
+
+ ResultColumnList deleteSignature = _dml.resultSet.resultColumns;
+ for ( int i = 0; i < deleteSignature.size(); i++ )
+ {
+ ResultColumn origRC = deleteSignature.elementAt( i );
+ ResultColumn newRC;
+ ValueNode expression = origRC.getExpression();
+
+ if ( expression instanceof ColumnReference )
+ {
+ ColumnReference cr = (ColumnReference) ((ColumnReference) expression).getClone();
+ newRC = new ResultColumn( cr, cr, getContextManager() );
+ }
+ else
+ {
+ newRC = origRC.cloneMe();
+ }
+ _thenColumns.addResultColumn( newRC );
+ }
}
/** Bind a WHEN NOT MATCHED ... THEN INSERT clause */
- private void bindInsert( JoinNode joinNode, FromTable targetTable )
+ private void bindInsert
+ (
+ DataDictionary dd,
+ MergeNode mergeNode,
+ FromList fullFromList,
+ FromTable targetTable
+ )
throws StandardException
{
- // needed to make the SelectNode bind
- _insertValues.replaceOrForbidDefaults( targetTable.getTableDescriptor(), _insertColumns, true );
-
// the VALUES clause may not mention columns in the target table
- _insertValues.bindExpressions
- (
- joinNode.makeFromList( true, false ),
- new SubqueryList( getContextManager() ),
- new ArrayList<AggregateNode>()
- );
+ FromList targetTableFromList = new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() );
+ targetTableFromList.addElement( fullFromList.elementAt( 0 ) );
+ bindExpressions( _insertValues, targetTableFromList );
+ if ( _matchingRefinement != null )
+ {
+ mergeNode.bindExpression( _matchingRefinement, targetTableFromList );
+ }
SelectNode selectNode = new SelectNode
(
_insertValues, // select list
- joinNode.makeFromList( true, true ),
+ fullFromList,
null, // where clause
null, // group by list
null, // having clause
@@ -230,6 +363,7 @@ public class MatchingClauseNode extends
targetTable.getTableName(),
_insertColumns,
selectNode,
+ this, // in NOT MATCHED clause
null, // targetProperties
null, // order by cols
null, // offset
@@ -241,4 +375,243 @@ public class MatchingClauseNode extends
_dml.bindStatement();
}
+ /** Boilerplate for binding a list of ResultColumns against a FromList */
+ private void bindExpressions( ResultColumnList rcl, FromList fromList )
+ throws StandardException
+ {
+ CompilerContext cc = getCompilerContext();
+ final int previousReliability = cc.getReliability();
+
+ try {
+ cc.setReliability( previousReliability | CompilerContext.SQL_IN_ROUTINES_ILLEGAL );
+
+ rcl.bindExpressions
+ (
+ fromList,
+ new SubqueryList( getContextManager() ),
+ new ArrayList<AggregateNode>()
+ );
+ }
+ finally
+ {
+ // Restore previous compiler state
+ cc.setReliability( previousReliability );
+ }
+ }
+
+ /**
+ * <p>
+ * Calculate the 1-based offsets which define the rows which will be buffered up
+ * for this INSERT/UPDATE/DELETE action at run-time. The rows are constructed
+ * from the columns in the SELECT list of the driving left joins. This method
+ * calculates an array of offsets into the SELECT list. The columns at those
+ * offsets will form the row which is buffered up for the INSERT/UPDATE/DELETE
+ * action.
+ * </p>
+ */
+ void bindThenColumns( ResultColumnList selectList )
+ throws StandardException
+ {
+ int bufferedCount = _thenColumns.size();
+ int selectCount = selectList.size();
+
+ _thenColumnOffsets = new int[ bufferedCount ];
+
+ for ( int bidx = 0; bidx < bufferedCount; bidx++ )
+ {
+ ResultColumn bufferedRC = _thenColumns.elementAt( bidx );
+ ValueNode bufferedExpression = bufferedRC.getExpression();
+ int offset = -1; // start out undefined
+
+ if ( bufferedExpression instanceof ColumnReference )
+ {
+ ColumnReference bufferedCR = (ColumnReference) bufferedExpression;
+ String tableName = bufferedCR.getTableName();
+ String columnName = bufferedCR.getColumnName();
+
+ // loop through the SELECT list to find this column reference
+ for ( int sidx = 0; sidx < selectCount; sidx++ )
+ {
+ ResultColumn selectRC = selectList.elementAt( sidx );
+ ValueNode selectExpression = selectRC.getExpression();
+ ColumnReference selectCR = selectExpression instanceof ColumnReference ?
+ (ColumnReference) selectExpression : null;
+
+ if ( selectCR != null )
+ {
+ if (
+ tableName.equals( selectCR.getTableName() ) &&
+ columnName.equals( selectCR.getColumnName() )
+ )
+ {
+ offset = sidx + 1;
+ break;
+ }
+ }
+ }
+ }
+ else if ( bufferedExpression instanceof CurrentRowLocationNode )
+ {
+ //
+ // There is only one RowLocation in the SELECT list, the row location for the
+ // tuple from the target table. The RowLocation is always the last column in
+ // the SELECT list.
+ //
+ offset = selectCount;
+ }
+
+ _thenColumnOffsets[ bidx ] = offset;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // optimize() BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * <p>
+ * Optimize the INSERT/UPDATE/DELETE action.
+ * </p>
+ */
+ void optimize() throws StandardException
+ {
+ _dml.optimizeStatement();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // generate() BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ ConstantAction makeConstantAction( ActivationClassBuilder acb )
+ throws StandardException
+ {
+ // generate the clause-specific refinement
+ String refinementName = null;
+ if ( _matchingRefinement != null )
+ {
+ MethodBuilder userExprFun = acb.newUserExprFun();
+
+ _matchingRefinement.generateExpression( acb, userExprFun );
+ userExprFun.methodReturn();
+
+ // we are done modifying userExprFun, complete it.
+ userExprFun.complete();
+
+ refinementName = userExprFun.getName();
+ }
+
+ return getGenericConstantActionFactory().getMatchingClauseConstantAction
+ (
+ getClauseType(),
+ refinementName,
+ _thenColumnOffsets,
+ _resultSetFieldName,
+ _actionMethodName,
+ _dml.makeConstantAction()
+ );
+ }
+ private int getClauseType()
+ {
+ if ( isUpdateClause() ) { return ConstantAction.WHEN_MATCHED_THEN_UPDATE; }
+ else if ( isInsertClause() ) { return ConstantAction.WHEN_NOT_MATCHED_THEN_INSERT; }
+ else { return ConstantAction.WHEN_MATCHED_THEN_DELETE; }
+ }
+
+ /**
+ * <p>
+ * Generate a method to invoke the INSERT/UPDATE/DELETE action. This method
+ * will be called at runtime by MatchingClauseConstantAction.executeConstantAction().
+ * </p>
+ */
+ void generate( ActivationClassBuilder acb, int clauseNumber )
+ throws StandardException
+ {
+ _clauseNumber = clauseNumber;
+ _actionMethodName = "mergeActionMethod_" + _clauseNumber;
+
+ MethodBuilder mb = acb.getClassBuilder().newMethodBuilder
+ (
+ Modifier.PUBLIC,
+ ClassName.ResultSet,
+ _actionMethodName
+ );
+ mb.addThrownException(ClassName.StandardException);
+
+ // now generate the action into this method
+ _dml.generate( acb, mb );
+
+ mb.methodReturn();
+ mb.complete();
+ }
+
+ /**
+ * <p>
+ * Adds a field to the generated class to hold the ResultSet of buffered rows
+ * which drive the INSERT/UPDATE/DELETE action. Generates code to push
+ * the contents of that field onto the stack.
+ * </p>
+ */
+ void generateResultSetField( ActivationClassBuilder acb, MethodBuilder mb )
+ throws StandardException
+ {
+ _resultSetFieldName = "mergeResultSetField_" + _clauseNumber;
+
+ // make the field public so we can stuff it at execution time
+ LocalField resultSetField = acb.newFieldDeclaration( Modifier.PUBLIC, ClassName.NoPutResultSet, _resultSetFieldName );
+
+ //
+ // At runtime, MatchingClauseConstantAction.executeConstantAction()
+ // will stuff the resultSetField with the temporary table which collects
+ // the rows relevant to this action. We want to push the value of resultSetField
+ // onto the stack, where it will be the ResultSet argument to the constructor
+ // of the actual INSERT/UPDATE/DELETE action.
+ //
+ mb.getField( resultSetField );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // Visitable BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Accept the visitor for all visitable children of this node.
+ *
+ * @param v the visitor
+ *
+ * @exception StandardException on error
+ */
+ @Override
+ void acceptChildren(Visitor v)
+ throws StandardException
+ {
+ super.acceptChildren( v );
+
+ if ( _matchingRefinement != null ) { _matchingRefinement.accept( v ); }
+ if ( _updateColumns != null ) { _updateColumns.accept( v ); }
+ if ( _insertColumns != null ) { _insertColumns.accept( v ); }
+ if ( _insertValues != null ) { _insertValues.accept( v ); }
+
+ if ( _dml != null ) { _dml.accept( v ); }
+ }
+
+ /**
+ * Convert this object to a String. See comments in QueryTreeNode.java
+ * for how this should be done for tree printing.
+ *
+ * @return This object as a String
+ */
+ @Override
+ public String toString()
+ {
+ if ( isUpdateClause() ) { return "UPDATE"; }
+ else if ( isInsertClause() ) { return "INSERT"; }
+ else { return "DELETE"; }
+ }
+
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java Thu Oct 24 12:41:59 2013
@@ -21,24 +21,105 @@
package org.apache.derby.impl.sql.compile;
+import java.util.Arrays;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.ClassName;
import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.classfile.VMOpcode;
+import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
+import org.apache.derby.iapi.sql.compile.CompilerContext;
+import org.apache.derby.iapi.sql.compile.Visitor;
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
+import org.apache.derby.iapi.sql.execute.ConstantAction;
+import org.apache.derby.iapi.util.IdUtil;
/**
* <p>
- * A MergeNode represents a MERGE statement. It is the top node of the
- * query tree for that statement. The driving result set for a MERGE statement
- * is essentially the following:
+ * A MergeNode represents a MERGE statement. The statement looks like
+ * this...
* </p>
*
* <pre>
- * sourceTable LEFT OUTER JOIN targetTable ON searchCondition
+ * MERGE INTO targetTable
+ * USING sourceTable
+ * ON searchCondition
+ * matchingClause1 ... matchingClauseN
* </pre>
+ *
+ * <p>
+ * ...where each matching clause looks like this...
+ * </p>
+ *
+ * <pre>
+ * WHEN MATCHED [ AND matchingRefinement ] THEN DELETE
+ * </pre>
+ *
+ * <p>
+ * ...or
+ * </p>
+ *
+ * <pre>
+ * WHEN MATCHED [ AND matchingRefinement ] THEN UPDATE SET col1 = expr1, ... colM = exprM
+ * </pre>
+ *
+ * <p>
+ * ...or
+ * </p>
+ *
+ * <pre>
+ * WHEN NOT MATCHED [ AND matchingRefinement ] THEN INSERT columnList VALUES valueList
+ * </pre>
+ *
+ * <p>
+ * The Derby compiler essentially rewrites this statement into a driving left join
+ * followed by a series of DELETE/UPDATE/INSERT actions. The left join looks like
+ * this:
+ * </p>
+ *
+ * <pre>
+ * SELECT selectList FROM sourceTable LEFT OUTER JOIN targetTable ON searchCondition
+ * </pre>
+ *
+ * <p>
+ * The selectList of the driving left join consists of the following:
+ * </p>
+ *
+ * <ul>
+ * <li>All of the columns mentioned in the searchCondition.</li>
+ * <li>All of the columns mentioned in the matchingRefinement clauses.</li>
+ * <li>All of the columns mentioned in the SET clauses and the INSERT columnLists and valueLists.</li>
+ * <li>All additional columns needed for the triggers and foreign keys fired by the DeleteResultSets
+ * and UpdateResultSets constructed for the WHEN MATCHED clauses.</li>
+ * <li>All additional columns needed to build index rows and evaluate generated columns
+ * needed by the UpdateResultSets constructed for the WHEN MATCHED...THEN UPDATE clauses.</li>
+ * <li>A trailing targetTable.RowLocation column.</li>
+ * </ul>
+ *
+ * <p>
+ * The matchingRefinement expressions are bound and generated against the
+ * FromList of the driving left join. Dummy DeleteNode, UpdateNode, and InsertNode
+ * statements are independently constructed in order to bind and generate the DELETE/UPDATE/INSERT
+ * actions.
+ * </p>
+ *
+ * <p>
+ * At execution time, the targetTable.RowLocation column is used to determine
+ * whether a given driving row matches. The row matches iff targetTable.RowLocation is not null.
+ * The driving row is then assigned to the
+ * first DELETE/UPDATE/INSERT action to which it applies. The relevant columns from
+ * the driving row are extracted and buffered in a temporary table specific to that
+ * DELETE/UPDATE/INSERT action. After the driving left join has been processed,
+ * the DELETE/UPDATE/INSERT actions are run in order, each taking its corresponding
+ * temporary table as its source ResultSet.
+ * </p>
*/
public final class MergeNode extends DMLModStatementNode
@@ -49,17 +130,30 @@ public final class MergeNode extends DML
//
///////////////////////////////////////////////////////////////////////////////////
+ private static final int SOURCE_TABLE_INDEX = 0;
+ private static final int TARGET_TABLE_INDEX = 1;
+
+ private static final String TARGET_ROW_LOCATION_NAME = "###TargetRowLocation";
+
///////////////////////////////////////////////////////////////////////////////////
//
// STATE
//
///////////////////////////////////////////////////////////////////////////////////
- private FromTable _targetTable;
+ // constructor args
+ private FromBaseTable _targetTable;
private FromTable _sourceTable;
private ValueNode _searchCondition;
private ArrayList<MatchingClauseNode> _matchingClauses;
+ // filled in at bind() time
+ private FromList _leftJoinFromList;
+
+ // filled in at generate() time
+ private ConstantAction _constantAction;
+ private CursorNode _leftJoinCursor;
+
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTOR
@@ -81,33 +175,14 @@ public final class MergeNode extends DML
)
throws StandardException
{
- super( null, cm );
+ super( null, null, cm );
- _targetTable = targetTable;
+ if ( !( targetTable instanceof FromBaseTable) ) { notBaseTable(); }
+ else { _targetTable = (FromBaseTable) targetTable; }
+
_sourceTable = sourceTable;
_searchCondition = searchCondition;
_matchingClauses = matchingClauses;
-
- makeJoin();
- }
-
- /**
- * <p>
- * Construct the left outer join which will drive the execution.
- * </p>
- */
- private void makeJoin() throws StandardException
- {
- resultSet = new HalfOuterJoinNode
- (
- _sourceTable,
- _targetTable,
- _searchCondition,
- null,
- false,
- null,
- getContextManager()
- );
}
///////////////////////////////////////////////////////////////////////////////////
@@ -121,35 +196,50 @@ public final class MergeNode extends DML
{
DataDictionary dd = getDataDictionary();
- //
- // Bind the left join. This binds _targetTable and _sourceTable.
- //
- bind( dd );
-
- bindSearchCondition();
-
- if ( !targetIsBaseTable() )
+ FromList dummyFromList = new FromList( getContextManager() );
+ FromBaseTable dummyTargetTable = new FromBaseTable
+ (
+ _targetTable.tableName,
+ _targetTable.correlationName,
+ null,
+ null,
+ getContextManager()
+ );
+ FromTable dummySourceTable = cloneSourceTable();
+
+ // source and target may not have the same correlation names
+ if ( getExposedName( dummyTargetTable ).equals( getExposedName( dummySourceTable ) ) )
{
- throw StandardException.newException( SQLState.LANG_TARGET_NOT_BASE_TABLE );
+ throw StandardException.newException( SQLState.LANG_SAME_EXPOSED_NAME );
}
- if ( !sourceIsBase_View_or_VTI() )
+ dummyFromList.addFromTable( dummySourceTable );
+ dummyFromList.addFromTable( dummyTargetTable );
+ dummyFromList.bindTables( dd, new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() ) );
+
+ if ( !targetIsBaseTable( dummyTargetTable ) ) { notBaseTable(); }
+
+ for ( MatchingClauseNode mcn : _matchingClauses )
{
- throw StandardException.newException( SQLState.LANG_SOURCE_NOT_BASE_VIEW_OR_VTI );
+ mcn.bind( dd, this, dummyFromList, dummyTargetTable );
}
+
+ bindLeftJoin( dd );
- // source and target may not have the same correlation names
- if ( getExposedName( _targetTable ).equals( getExposedName( _sourceTable ) ) )
+ // re-bind the matchingRefinement clauses now that we have result set numbers
+ // from the driving left join.
+ for ( MatchingClauseNode mcn : _matchingClauses )
{
- throw StandardException.newException( SQLState.LANG_SAME_EXPOSED_NAME );
+ mcn.bindRefinement( this, _leftJoinFromList );
}
-
+
for ( MatchingClauseNode mcn : _matchingClauses )
{
- mcn.bind( (JoinNode) resultSet, _targetTable );
+ if ( mcn.isUpdateClause() || mcn.isInsertClause() )
+ {
+ throw StandardException.newException( SQLState.NOT_IMPLEMENTED, "MERGE" );
+ }
}
-
- throw StandardException.newException( SQLState.NOT_IMPLEMENTED, "MERGE" );
}
/** Get the exposed name of a FromTable */
@@ -158,21 +248,145 @@ public final class MergeNode extends DML
return ft.getTableName().getTableName();
}
- /** Bind the search condition, the ON clause of the left join */
- private void bindSearchCondition() throws StandardException
+ /**
+ * Bind the driving left join select.
+ * Stuffs the left join SelectNode into the resultSet variable.
+ */
+ private void bindLeftJoin( DataDictionary dd ) throws StandardException
{
- FromList fromList = new FromList
- ( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() );
+ CompilerContext cc = getCompilerContext();
+ final int previousReliability = cc.getReliability();
+
+ try {
+ cc.setReliability( previousReliability | CompilerContext.SQL_IN_ROUTINES_ILLEGAL );
+
+ HalfOuterJoinNode hojn = new HalfOuterJoinNode
+ (
+ _sourceTable,
+ _targetTable,
+ _searchCondition,
+ null,
+ false,
+ null,
+ getContextManager()
+ );
+
+ _leftJoinFromList = hojn.makeFromList( true, true );
+ _leftJoinFromList.bindTables( dd, new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() ) );
+
+ if ( !sourceIsBase_View_or_VTI() )
+ {
+ throw StandardException.newException( SQLState.LANG_SOURCE_NOT_BASE_VIEW_OR_VTI );
+ }
+
+ FromList topFromList = new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() );
+ topFromList.addFromTable( hojn );
+
+ // preliminary binding of the matching clauses to resolve column
+ // referneces. this ensures that we can add all of the columns from
+ // the matching refinements to the SELECT list of the left join.
+ // we re-bind the matching clauses when we're done binding the left join
+ // because, at that time, we have result set numbers needed for
+ // code generation.
+ for ( MatchingClauseNode mcn : _matchingClauses )
+ {
+ mcn.bindRefinement( this, _leftJoinFromList );
+ }
+
+ ResultColumnList selectList = buildSelectList();
+
+ // calculate the offsets into the SELECT list which define the rows for
+ // the WHEN [ NOT ] MATCHED actions
+ for ( MatchingClauseNode mcn : _matchingClauses )
+ {
+ mcn.bindThenColumns( selectList );
+ }
+
+ resultSet = new SelectNode
+ (
+ selectList,
+ topFromList,
+ null, // where clause
+ null, // group by list
+ null, // having clause
+ null, // window list
+ null, // optimizer plan override
+ getContextManager()
+ );
+
+ // Wrap the SELECT in a CursorNode in order to finish binding it.
+ _leftJoinCursor = new CursorNode
+ (
+ "SELECT",
+ resultSet,
+ null,
+ null,
+ null,
+ null,
+ false,
+ CursorNode.READ_ONLY,
+ null,
+ getContextManager()
+ );
+ _leftJoinCursor.bindStatement();
+ }
+ finally
+ {
+ // Restore previous compiler state
+ cc.setReliability( previousReliability );
+ }
+ }
- resultSet.bindResultColumns( fromList );
+ /** Throw a "not base table" exception */
+ private void notBaseTable() throws StandardException
+ {
+ throw StandardException.newException( SQLState.LANG_TARGET_NOT_BASE_TABLE );
}
- /** Return true if the target table is a base table */
- private boolean targetIsBaseTable() throws StandardException
+ /** Build the select list for the left join */
+ private ResultColumnList buildSelectList() throws StandardException
{
- if ( !( _targetTable instanceof FromBaseTable) ) { return false; }
+ HashMap<String,ColumnReference> drivingColumnMap = new HashMap<String,ColumnReference>();
+ getColumnsInExpression( drivingColumnMap, _searchCondition );
+
+ for ( MatchingClauseNode mcn : _matchingClauses )
+ {
+ mcn.getColumnsInExpressions( this, drivingColumnMap );
+ getColumnsFromList( drivingColumnMap, mcn.getBufferedColumns() );
+ }
+
+ ResultColumnList selectList = new ResultColumnList( getContextManager() );
+
+ // add all of the columns from the source table which are mentioned
+ addColumns( (FromTable) _leftJoinFromList.elementAt( SOURCE_TABLE_INDEX ), drivingColumnMap, selectList );
+ // add all of the columns from the target table which are mentioned
+ addColumns( (FromTable) _leftJoinFromList.elementAt( TARGET_TABLE_INDEX ), drivingColumnMap, selectList );
+
+ addTargetRowLocation( selectList );
+
+ return selectList;
+ }
- FromBaseTable fbt = (FromBaseTable) _targetTable;
+ /** Add the target table's row location to the left join's select list */
+ private void addTargetRowLocation( ResultColumnList selectList )
+ throws StandardException
+ {
+ // tell the target table to generate a row location column
+ _targetTable.setRowLocationColumnName( TARGET_ROW_LOCATION_NAME );
+
+ TableName fromTableName = _targetTable.getTableName();
+ ColumnReference cr = new ColumnReference
+ ( TARGET_ROW_LOCATION_NAME, fromTableName, getContextManager() );
+ ResultColumn rowLocationColumn = new ResultColumn( (String) null, cr, getContextManager() );
+ rowLocationColumn.markGenerated();
+
+ selectList.addResultColumn( rowLocationColumn );
+ }
+
+ /** Return true if the target table is a base table */
+ private boolean targetIsBaseTable( FromBaseTable targetTable ) throws StandardException
+ {
+ FromBaseTable fbt = targetTable;
TableDescriptor desc = fbt.getTableDescriptor();
if ( desc == null ) { return false; }
@@ -200,4 +414,271 @@ public final class MergeNode extends DML
}
}
+ /** Clone the source table for binding the MATCHED clauses */
+ private FromTable cloneSourceTable() throws StandardException
+ {
+ if ( _sourceTable instanceof FromVTI )
+ {
+ FromVTI source = (FromVTI) _sourceTable;
+
+ return new FromVTI
+ (
+ source.methodCall,
+ source.correlationName,
+ source.resultColumns,
+ null,
+ source.exposedName,
+ getContextManager()
+ );
+ }
+ else if ( _sourceTable instanceof FromBaseTable )
+ {
+ FromBaseTable source = (FromBaseTable) _sourceTable;
+ return new FromBaseTable
+ (
+ source.tableName,
+ source.correlationName,
+ null,
+ null,
+ getContextManager()
+ );
+ }
+ else
+ {
+ throw StandardException.newException( SQLState.LANG_SOURCE_NOT_BASE_VIEW_OR_VTI );
+ }
+ }
+
+ /**
+ * <p>
+ * Add to an evolving select list the columns from the indicated table.
+ * </p>
+ */
+ private void addColumns
+ (
+ FromTable fromTable,
+ HashMap<String,ColumnReference> drivingColumnMap,
+ ResultColumnList selectList
+ )
+ throws StandardException
+ {
+ String[] columnNames = getColumns( getExposedName( fromTable ), drivingColumnMap );
+
+ for ( int i = 0; i < columnNames.length; i++ )
+ {
+ ColumnReference cr = new ColumnReference
+ ( columnNames[ i ], fromTable.getTableName(), getContextManager() );
+ ResultColumn rc = new ResultColumn( (String) null, cr, getContextManager() );
+ selectList.addResultColumn( rc );
+ }
+ }
+
+ /** Get the column names from the table with the given table number, in sorted order */
+ private String[] getColumns( String exposedName, HashMap<String,ColumnReference> map )
+ {
+ ArrayList<String> list = new ArrayList<String>();
+
+ for ( ColumnReference cr : map.values() )
+ {
+ if ( exposedName.equals( cr.getTableName() ) ) { list.add( cr.getColumnName() ); }
+ }
+
+ String[] retval = new String[ list.size() ];
+ list.toArray( retval );
+ Arrays.sort( retval );
+
+ return retval;
+ }
+
+ /** Add the columns in the matchingRefinement clause to the evolving map */
+ void getColumnsInExpression
+ ( HashMap<String,ColumnReference> map, ValueNode expression )
+ throws StandardException
+ {
+ if ( expression == null ) { return; }
+
+ CollectNodesVisitor<ColumnReference> getCRs =
+ new CollectNodesVisitor<ColumnReference>(ColumnReference.class);
+
+ expression.accept(getCRs);
+ List<ColumnReference> colRefs = getCRs.getList();
+
+ getColumnsFromList( map, colRefs );
+ }
+
+ /** Add a list of columns to the the evolving map */
+ private void getColumnsFromList
+ ( HashMap<String,ColumnReference> map, ResultColumnList rcl )
+ throws StandardException
+ {
+ ArrayList<ColumnReference> colRefs = new ArrayList<ColumnReference>();
+
+ for ( int i = 0; i < rcl.size(); i++ )
+ {
+ ResultColumn rc = rcl.elementAt( i );
+ ColumnReference cr = rc.getReference();
+ if ( cr != null ) { colRefs.add( cr ); }
+ }
+
+ getColumnsFromList( map, colRefs );
+ }
+
+ /** Add a list of columns to the the evolving map */
+ private void getColumnsFromList
+ ( HashMap<String,ColumnReference> map, List<ColumnReference> colRefs )
+ throws StandardException
+ {
+ for ( ColumnReference cr : colRefs )
+ {
+ if ( cr.getTableName() == null )
+ {
+ ResultColumn rc = _leftJoinFromList.bindColumnReference( cr );
+ TableName tableName = new TableName( null, rc.getTableName(), getContextManager() );
+ cr = new ColumnReference( cr.getColumnName(), tableName, getContextManager() );
+ }
+
+ String key = makeDCMKey( cr.getTableName(), cr.getColumnName() );
+ if ( map.get( key ) == null )
+ {
+ map.put( key, cr );
+ }
+ }
+ }
+
+ /** Make a HashMap key for a column in the driving column map of the LEFT JOIN */
+ private String makeDCMKey( String tableName, String columnName )
+ {
+ return IdUtil.mkQualifiedName( tableName, columnName );
+ }
+
+ /** Boilerplate for binding an expression against a FromList */
+ void bindExpression( ValueNode value, FromList fromList )
+ throws StandardException
+ {
+ CompilerContext cc = getCompilerContext();
+ final int previousReliability = cc.getReliability();
+
+ try {
+ cc.setReliability( previousReliability | CompilerContext.SQL_IN_ROUTINES_ILLEGAL );
+
+ value.bindExpression
+ (
+ fromList,
+ new SubqueryList( getContextManager() ),
+ new ArrayList<AggregateNode>()
+ );
+ }
+ finally
+ {
+ // Restore previous compiler state
+ cc.setReliability( previousReliability );
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // optimize() BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void optimizeStatement() throws StandardException
+ {
+ /* First optimize the left join */
+ _leftJoinCursor.optimizeStatement();
+
+ /* In language we always set it to row lock, it's up to store to
+ * upgrade it to table lock. This makes sense for the default read
+ * committed isolation level and update lock. For more detail, see
+ * Beetle 4133.
+ */
+ //lockMode = TransactionController.MODE_RECORD;
+
+ // now optimize the INSERT/UPDATE/DELETE actions
+ for ( MatchingClauseNode mcn : _matchingClauses )
+ {
+ mcn.optimize();
+ }
+ }
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // generate() BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ void generate( ActivationClassBuilder acb, MethodBuilder mb )
+ throws StandardException
+ {
+ int clauseCount = _matchingClauses.size();
+
+ /* generate the parameters */
+ generateParameterValueSet(acb);
+
+ acb.pushGetResultSetFactoryExpression( mb );
+
+ // arg 1: the driving left join
+ _leftJoinCursor.generate( acb, mb );
+
+ ConstantAction[] clauseActions = new ConstantAction[ clauseCount ];
+ for ( int i = 0; i < clauseCount; i++ )
+ {
+ MatchingClauseNode mcn = _matchingClauses.get( i );
+
+ mcn.generate( acb, i );
+ clauseActions[ i ] = mcn.makeConstantAction( acb );
+ }
+ _constantAction = getGenericConstantActionFactory().getMergeConstantAction( clauseActions );
+
+ mb.callMethod
+ ( VMOpcode.INVOKEINTERFACE, (String) null, "getMergeResultSet", ClassName.ResultSet, 1 );
+ }
+
+ @Override
+ public ConstantAction makeConstantAction() throws StandardException
+ {
+ return _constantAction;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // Visitable BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Accept the visitor for all visitable children of this node.
+ *
+ * @param v the visitor
+ *
+ * @exception StandardException on error
+ */
+ @Override
+ void acceptChildren(Visitor v)
+ throws StandardException
+ {
+ if ( _leftJoinCursor != null )
+ {
+ _leftJoinCursor.acceptChildren( v );
+ }
+ else
+ {
+ super.acceptChildren( v );
+
+ _targetTable.accept( v );
+ _sourceTable.accept( v );
+ _searchCondition.accept( v );
+ }
+
+ for ( MatchingClauseNode mcn : _matchingClauses )
+ {
+ mcn.accept( v );
+ }
+ }
+
+ @Override
+ String statementToString()
+ {
+ return "MERGE";
+ }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java Thu Oct 24 12:41:59 2013
@@ -1392,6 +1392,13 @@ public abstract class QueryTreeNode impl
break;
}
}
+ else if (
+ (getCompilerContext().getReliability() & fragmentBitMask & CompilerContext.SQL_IN_ROUTINES_ILLEGAL)
+ != 0
+ )
+ {
+ sqlState = SQLState.LANG_ROUTINE_CANT_PERMIT_SQL;
+ }
else
{
sqlState = SQLState.LANG_UNRELIABLE_QUERY_FRAGMENT;
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java Thu Oct 24 12:41:59 2013
@@ -78,8 +78,6 @@ public final class UpdateNode extends DM
protected FormatableBitSet readColsBitSet;
protected boolean positionedUpdate;
- private boolean inMatchedClause;
-
/* Column name for the RowLocation in the ResultSet */
static final String COLUMNNAME = "###RowLocationToUpdate";
@@ -88,17 +86,19 @@ public final class UpdateNode extends DM
*
* @param targetTableName The name of the table to update
* @param resultSet The ResultSet that we will generate
- * @param inMatchedClause True if this UPDATE is part of a MATCHED ... THEN UPDATE clause of a MERGE statement.
+ * @param matchingClause Non-null if this DML is part of a MATCHED clause of a MERGE statement.
* @param cm The context manager
*/
- UpdateNode(TableName targetTableName,
- ResultSetNode resultSet,
- boolean inMatchedClause,
- ContextManager cm)
+ UpdateNode
+ (
+ TableName targetTableName,
+ ResultSetNode resultSet,
+ MatchingClauseNode matchingClause,
+ ContextManager cm
+ )
{
- super(resultSet, cm);
+ super( resultSet, matchingClause, cm );
this.targetTableName = targetTableName;
- this.inMatchedClause = inMatchedClause;
}
/**
@@ -1341,7 +1341,7 @@ public final class UpdateNode extends DM
* statement. If we clear the table name, then we will not be able to
* resolve which table (target or source) holds the column.
*/
- if ( !inMatchedClause ) { column.clearTableName(); }
+ if ( !inMatchingClause() ) { column.clearTableName(); }
}
}
@@ -1402,6 +1402,9 @@ public final class UpdateNode extends DM
{
ResultColumn rc = targetRCL.elementAt( i );
+ // defaults may already have been substituted for MERGE statements
+ if ( rc.wasDefaultColumn() ) { continue; }
+
if ( rc.hasGenerationClause() )
{
ValueNode resultExpression =
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj Thu Oct 24 12:41:59 2013
@@ -725,7 +725,7 @@ public class SQLParser
getContextManager());
StatementNode retval =
- new DeleteNode(tableName, resultSet, getContextManager());
+ new DeleteNode(tableName, resultSet, null, getContextManager());
setUpAndLinkParameters();
@@ -758,7 +758,7 @@ public class SQLParser
getContextManager());
StatementNode retval =
- new UpdateNode(tableName, resultSet, false, getContextManager());
+ new UpdateNode(tableName, resultSet, null, getContextManager());
setUpAndLinkParameters();
@@ -8510,6 +8510,7 @@ insertColumnsAndSource(QueryTreeNode tar
targetTable,
columnList,
queryExpression,
+ null,
targetProperties,
orderCols,
offsetClauses[ OFFSET_CLAUSE ],
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java Thu Oct 24 12:41:59 2013
@@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
+import java.util.Stack;
import java.util.Vector;
import org.apache.derby.catalog.Dependable;
@@ -202,6 +203,11 @@ public abstract class BaseActivation imp
*/
private SQLSessionContext sqlSessionContextForChildren;
+ /**
+ * Stack of ConstantActions.
+ */
+ private Stack<ConstantAction> constantActionStack = new Stack<ConstantAction>();
+
//Following is the position of the session table names list in savedObjects in compiler context
//This is updated to be the correct value at cursor generate time if the cursor references any session table names.
//If the cursor does not reference any session table names, this will stay negative
@@ -312,8 +318,23 @@ public abstract class BaseActivation imp
return preStmt;
}
- public ConstantAction getConstantAction() {
- return preStmt.getConstantAction();
+ public ConstantAction pushConstantAction( ConstantAction newConstantAction )
+ {
+ return constantActionStack.push( newConstantAction );
+ }
+
+ public ConstantAction popConstantAction()
+ {
+ return constantActionStack.pop();
+ }
+
+ public ConstantAction getConstantAction()
+ {
+ if ( constantActionStack.size() > 0 )
+ {
+ return constantActionStack.peek();
+ }
+ else { return preStmt.getConstantAction(); }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BulkTableScanResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BulkTableScanResultSet.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BulkTableScanResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BulkTableScanResultSet.java Thu Oct 24 12:41:59 2013
@@ -70,8 +70,11 @@ class BulkTableScanResultSet extends Tab
implements CursorResultSet
{
private DataValueDescriptor[][] rowArray;
+ private RowLocation[] rowLocations;
private int curRowPosition;
private int numRowsInArray;
+ private int baseColumnCount;
+ private int resultColumnCount;
private static int OUT_OF_ROWS = 0;
@@ -154,6 +157,41 @@ class BulkTableScanResultSet extends Tab
"rowsPerRead = " + rowsPerRead);
}
}
+
+ // determine whether we should fetch row locations
+ setRowLocationsState();
+
+ //
+ // The following code block was introduced to support the driving left join
+ // of the MERGE statement. If we are executing a MERGE statement, we need
+ // to fetch the row location of every row in the target table. If we are in this
+ // situation, then the last column in the candidate row will be a RowLocation template
+ // and the highest bit in accessedCols will be set. We want to smudge out this
+ // information before we ask the Store for rows. The Store will be confused if we ask
+ // for the RowLocation in the same row array as the actual base columns.
+ //
+ if ( fetchRowLocations )
+ {
+ resultColumnCount = accessedCols == null ? candidate.nColumns() : accessedCols.getNumBitsSet();
+ baseColumnCount = candidate.nColumns() - 1;
+ candidate.setRowArray( lopOffRowLocation() );
+
+ // remove the RowLocation from the accessed column map
+ if ( accessedCols == null )
+ {
+ accessedCols = new FormatableBitSet( baseColumnCount );
+ for ( int i = 0; i < baseColumnCount; i++ ) { accessedCols.set( i ); }
+ }
+ else
+ {
+ FormatableBitSet newCols = new FormatableBitSet( baseColumnCount );
+ for ( int i = 0; i < baseColumnCount; i++ )
+ {
+ if ( accessedCols.isSet( i ) ) { newCols.set( i ); }
+ }
+ accessedCols = newCols;
+ }
+ }
}
/**
@@ -253,6 +291,7 @@ class BulkTableScanResultSet extends Tab
*/
beginTime = getCurrentTimeMillis();
rowArray = new DataValueDescriptor[rowsPerRead][];
+ if ( fetchRowLocations ) { rowLocations = new RowLocation[ rowsPerRead ]; }
// we only allocate the first row -- the
// store clones as needed for the rest
@@ -264,6 +303,23 @@ class BulkTableScanResultSet extends Tab
openTime += getElapsedMillis(beginTime);
}
+ /**
+ * Get a blank row by cloning the candidate row and lopping off
+ * the trailing RowLocation column for scans done on
+ * behalf of MERGE statements.
+ */
+ private DataValueDescriptor[] lopOffRowLocation()
+ throws StandardException
+ {
+ DataValueDescriptor[] temp = candidate.getRowArrayClone();
+
+ int count = temp.length - 1;
+ DataValueDescriptor[] result = new DataValueDescriptor[ count ] ;
+ for ( int i = 0; i < count; i++ ) { result[ i ] = temp[ i ]; }
+
+ return result;
+ }
+
/**
* Reopen the result set. Delegate
* most work to TableScanResultSet.reopenCore().
@@ -336,6 +392,17 @@ outer: for (;;)
}
result = currentRow;
+ if ( fetchRowLocations )
+ {
+ result = new ValueRow( resultColumnCount );
+ int idx = 1;
+
+ for ( ; idx < resultColumnCount; idx++ )
+ {
+ result.setColumn( idx, currentRow.getColumn( idx ) );
+ }
+ result.setColumn( idx, rowLocations[ curRowPosition ] );
+ }
break outer;
}
}
@@ -355,11 +422,11 @@ outer: for (;;)
curRowPosition = -1;
numRowsInArray =
((GroupFetchScanController) scanController).fetchNextGroup(
- rowArray, (RowLocation[]) null);
+ rowArray, rowLocations);
return numRowsInArray;
-
}
+
/**
* If the result set has been opened,
* close the open scan. Delegate most
@@ -378,6 +445,7 @@ outer: for (;;)
numRowsInArray = -1;
curRowPosition = -1;
rowArray = null;
+ rowLocations = null;
}
/**
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java Thu Oct 24 12:41:59 2013
@@ -103,7 +103,7 @@ abstract class DMLWriteResultSet extends
* which case we do the objectifying in UpdateResultSet. Beetle 4896. Related bug entries:
* 2432, 3383.
*/
- needToObjectifyStream = (this.constantAction.getTriggerInfo() != null);
+ needToObjectifyStream = (this.constantAction.getTriggerInfo() != null);
}
@Override
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java?rev=1535360&r1=1535359&r2=1535360&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java Thu Oct 24 12:41:59 2013
@@ -346,8 +346,8 @@ class DeleteResultSet extends DMLWriteRe
while ( row != null )
{
- /* By convention, the last column for a delete contains a SQLRef
- * containing the RowLocation of the row to be deleted. If we're
+ /* By convention, the last column for a delete contains a data value
+ * wrapping the RowLocation of the row to be deleted. If we're
* doing a deferred delete, store the RowLocations in the
* temporary conglomerate. If we're not doing a deferred delete,
* just delete the rows immediately.
@@ -406,7 +406,7 @@ class DeleteResultSet extends DMLWriteRe
baseRowLocation =
(RowLocation) (rlColumn).getObject();
- if (SanityManager.DEBUG)
+ if (SanityManager.DEBUG)
{
SanityManager.ASSERT(baseRowLocation != null,
"baseRowLocation is null");