db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1545343 [1/2] - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/impl/sql/execute/ engine/org/apache/derby/impl/store/access/heap/ testing/org/apache/derbyTesting/functionTests/tests/lang/
Date Mon, 25 Nov 2013 17:02:41 GMT
Author: rhillegas
Date: Mon Nov 25 17:02:40 2013
New Revision: 1545343

URL: http://svn.apache.org/r1545343
Log:
DERBY-3155: Add support for UPDATE action of MERGE statement: derby-3155-08-ah-updateAction.diff.

Modified:
    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/FromBaseTable.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromList.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/UpdateNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.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/InsertResultSet.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.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/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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -1662,12 +1662,26 @@ abstract class DMLModStatementNode exten
         // 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
+        // don't need to fully optimize the dummy SELECT, which is never actually run
         if ( !inMatchingClause() )
         {
             /* First optimize the query */
             super.optimizeStatement();
         }
+        else if ( this instanceof UpdateNode )
+        {
+            //
+            // However, for UPDATE actions of MERGE statements, we do preprocess the driving SELECT.
+            // This is where the virtual column ids in CHECK constraints are re-mapped to
+            // refer to column positions in the SELECT list rather than in the base table.
+            //
+            resultSet = resultSet.preprocess
+                (
+                 getCompilerContext().getNumTables(),
+                 null,
+                 (FromList) null
+                 );
+        }
 
 		/* 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/FromBaseTable.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromBaseTable.java?rev=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -2796,7 +2796,7 @@ class FromBaseTable extends FromTable
             }
             
 			resultColumn = resultColumns.getResultColumn(columnReference.getColumnName());
-			/* Did we find a match? */
+            /* Did we find a match? */
 			if (resultColumn != null)
 			{
 				columnReference.setTableNumber(tableNumber);
@@ -3805,7 +3805,7 @@ class FromBaseTable extends FromTable
 	 *
 	 * @exception StandardException  Thrown on error
 	 */
-	private TableName getExposedTableName() throws StandardException  
+	TableName getExposedTableName() throws StandardException  
 	{
 		if (correlationName != null)
 			return makeTableName(null, correlationName);

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromList.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromList.java?rev=1545343&r1=1545342&r2=1545343&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromList.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromList.java Mon Nov 25 17:02:40 2013
@@ -572,24 +572,40 @@ class FromList extends    QueryTreeNodeV
 	}
 
 	/**
+     * <p>
 	 * Bind a column reference to one of the tables in this FromList.  The column name
 	 * must be unique within the tables in the FromList.  An exception is thrown
-	 * if a column name is not unique.
+	 * if a column name is not unique. This method fills in various fields
+     * in the column reference.
+     * </p>
 	 *
+     * <p>
 	 * NOTE: Callers are responsible for ordering the FromList by nesting level,
 	 * with tables at the deepest (current) nesting level first.  We will try to 
 	 * match against all FromTables at a given nesting level.  If no match is
 	 * found at a nesting level, then we proceed to the next level.  We stop
 	 * walking the list when the nesting level changes and we have found a match.
+     * </p>
 	 *
+     * <p>
 	 * NOTE: If the ColumnReference is qualified, then we will stop the search
 	 * at the first nesting level where there is a match on the exposed table name.
-	 * For example, s (a, b, c), t (d, e, f)
+	 * For example,
+     * <p>
+     *
+     * <pre>
+     *s (a, b, c), t (d, e, f)
 	 *		select * from s where exists (select * from t s where s.c = a)
+     * </pre>
+     *
+     * <p>
 	 * will not find a match for s.c, which is the expected ANSI behavior.
+     * </p>
 	 *
+     * <p>
 	 * bindTables() must have already been called on this FromList before
 	 * calling this method.
+     * </p>
 	 *
 	 * @param columnReference	The ColumnReference describing the column to bind
 	 *
@@ -603,7 +619,8 @@ class FromList extends    QueryTreeNodeV
 	{
 		boolean			columnNameMatch = false;
 		boolean			tableNameMatch = false;
-		FromTable		fromTable;
+		FromTable		fromTable = null;
+		FromTable		matchingTable = null;
         int             currentLevel;
 		int				previousLevel = -1;
 		ResultColumn	matchingRC = null;
@@ -640,7 +657,6 @@ class FromList extends    QueryTreeNodeV
 			previousLevel = currentLevel;
 
 			resultColumn = fromTable.getMatchingColumn(columnReference);
-
 			if (resultColumn != null)
 			{
 				if (! columnNameMatch)
@@ -661,7 +677,11 @@ class FromList extends    QueryTreeNodeV
 					columnNameMatch = true;
 
 					if (fromTable.isPrivilegeCollectionRequired())
-						getCompilerContext().addRequiredColumnPriv( resultColumn.getTableColumnDescriptor());						
+                    {
+						getCompilerContext().addRequiredColumnPriv( resultColumn.getTableColumnDescriptor());
+                    }
+
+                    matchingTable = fromTable;
 				}
 				else
 				{
@@ -678,6 +698,21 @@ class FromList extends    QueryTreeNodeV
 						 crTableName.equals(fromTable.getExposedName()) );
 		}
 
+        // fill in the table name
+        if ( (matchingTable != null) && (matchingRC != null) && (columnReference.getTableName() == null) )
+        {
+            TableName   crtn = matchingTable.getTableName();
+            if ( matchingTable instanceof FromBaseTable )
+            {
+                FromBaseTable   fbt = (FromBaseTable) matchingTable;
+                if ( fbt.getExposedTableName() !=  null )
+                {
+                    crtn = fbt.getExposedTableName();
+                }
+            }
+            columnReference.setTableNameNode( crtn );
+        }
+        
 		return matchingRC;
 	}
 
@@ -1619,8 +1654,8 @@ class FromList extends    QueryTreeNodeV
 		{
 			if (size() != 1)
 			{
-				SanityManager.THROWASSERT(
-					"size() expected to be 1");
+				SanityManager.THROWASSERT
+                    ( "size() is " + size() + " but should be 1");
 			}
 		}
         return elementAt(0).updateTargetLockMode();

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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -24,6 +24,7 @@ package	org.apache.derby.impl.sql.compil
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import org.apache.derby.catalog.types.DefaultInfoImpl;
 import org.apache.derby.iapi.error.StandardException;
@@ -44,6 +45,8 @@ import org.apache.derby.iapi.sql.diction
 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 import org.apache.derby.iapi.sql.execute.ConstantAction;
 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
+import org.apache.derby.iapi.types.DataTypeDescriptor;
+import org.apache.derby.iapi.types.DataValueDescriptor;
 
 /**
  * Node representing a WHEN MATCHED or WHEN NOT MATCHED clause
@@ -230,10 +233,19 @@ public class MatchingClauseNode extends 
 
         if ( isUpdateClause() )
         {
-            // get all columns mentioned on the right side of SET operators in WHEN MATCHED ... THEN UPDATE clauses
+            TableName   targetTableName = mergeNode.getTargetTable().getTableName();
+
+            //
+            // Get all columns mentioned on both sides of SET operators in WHEN MATCHED ... THEN UPDATE clauses.
+            // We need the left side because UPDATE needs before and after images of columns.
+            // We need the right side because there may be columns in the expressions there.
+            //
             for ( ResultColumn rc : _updateColumns )
             {
                 mergeNode.getColumnsInExpression( drivingColumnMap, rc.getExpression() );
+
+                ColumnReference leftCR = new ColumnReference( rc.exposedName, targetTableName, getContextManager() );
+                mergeNode.addColumn( drivingColumnMap, leftCR );
             }
         }
         else if ( isInsertClause() )
@@ -273,6 +285,59 @@ public class MatchingClauseNode extends 
         _dml = new UpdateNode( targetTable.getTableName(), selectNode, this, getContextManager() );
 
         _dml.bindStatement();
+
+        //
+        // Split the update row into its before and after images.
+        //
+        ResultColumnList    beforeColumns = new ResultColumnList( getContextManager() );
+        ResultColumnList    afterColumns = new ResultColumnList( getContextManager() );
+        ResultColumnList    fullUpdateRow = getBoundSelectUnderUpdate().resultColumns;
+        
+        // the full row is the before image, the after image, and a row location
+        int     rowSize = fullUpdateRow.size() / 2;
+
+        for ( int i = 0; i < rowSize; i++ )
+        {
+            ResultColumn    origBeforeRC = fullUpdateRow.elementAt( i );
+            ResultColumn    origAfterRC = fullUpdateRow.elementAt( i + rowSize );
+            ResultColumn    beforeRC = origBeforeRC.cloneMe();
+            ResultColumn    afterRC = origAfterRC.cloneMe();
+
+            beforeColumns.addResultColumn( beforeRC );
+            afterColumns.addResultColumn( afterRC );
+        }
+
+        buildThenColumnsForUpdate( fullFromList, targetTable, fullUpdateRow, beforeColumns, afterColumns );
+    }
+
+    /**
+     * <p>
+     * Get the bound SELECT node under the dummy UPDATE node.
+     * This may not be the source result set of the UPDATE node. That is because a ProjectRestrictResultSet
+     * may have been inserted on top of it by DEFAULT handling. This method
+     * exists to make the UPDATE actions of MERGE statement behave like ordinary
+     * UPDATE statements in this situation. The behavior is actually wrong. See
+     * DERBY-6414. Depending on how that bug is addressed, we may be able
+     * to remove this method eventually.
+     * </p>
+     */
+    private ResultSetNode    getBoundSelectUnderUpdate()
+        throws StandardException
+    {
+        ResultSetNode   candidate = _dml.resultSet;
+
+        while ( candidate != null )
+        {
+            if ( candidate instanceof SelectNode ) { return candidate; }
+            else if ( candidate instanceof SingleChildResultSetNode )
+            {
+                candidate = ((SingleChildResultSetNode) candidate).getChildResult();
+            }
+            else    { break; }
+        }
+        
+        // don't understand what's going on
+        throw StandardException.newException( SQLState.NOT_IMPLEMENTED );
     }
     
     /** Bind the SET clauses of an UPDATE action */
@@ -289,6 +354,253 @@ public class MatchingClauseNode extends 
         bindExpressions( _updateColumns, fullFromList );
     }
 
+    /**
+     * <p>
+     * Construct the row in the temporary table which drives an UPDATE action.
+     * Unlike a DELETE, whose temporary row is just a list of copied columns, the
+     * temporary row for UPDATE may contain complex expressions which must
+     * be code-generated later on.
+     * </p>
+     */
+    private void    buildThenColumnsForUpdate
+        (
+         FromList fullFromList,
+         FromTable targetTable,
+         ResultColumnList   fullRow,
+         ResultColumnList beforeRow,
+         ResultColumnList afterValues
+         )
+        throws StandardException
+    {
+        TableDescriptor td = targetTable.getTableDescriptor();
+        HashSet<String> changedColumns = getChangedColumnNames();
+        HashSet<String> changedGeneratedColumns = getChangedGeneratedColumnNames( td, changedColumns );
+
+        _thenColumns = fullRow.copyListAndObjects();
+
+        //
+        // Here we set up for the evaluation of expressions in the temporary table
+        // which drives the INSERT action. If we were actually generating the dummy SELECT
+        // for the DML action, the work would normally be done there. But we don't generate
+        // that SELECT. So we do the following here:
+        //
+        // 1) If a column has a value specified in the WHEN [ NOT ] MATCHED clause, then we use it.
+        //     There is some special handling to make the DEFAULT value work for identity columns.
+        //
+        // 2) Otherwise, if the column has a default, then we plug it in.
+        //
+        for ( int i = 0; i < _thenColumns.size(); i++ )
+        {
+            ResultColumn    origRC = _thenColumns.elementAt( i );
+
+            boolean isAfterColumn = (i >= beforeRow.size());
+
+            // skip the final RowLocation column of an UPDATE
+            boolean isRowLocation = isRowLocation( origRC );
+            ValueNode   origExpr = origRC.getExpression();
+
+            if ( isRowLocation ) { continue; }
+
+            String              columnName = origRC.getName();
+            ColumnDescriptor    cd = td.getColumnDescriptor( columnName );
+            boolean         changed = false;
+
+            //
+            // This handles the case that a GENERATED BY DEFAULT identity column is being
+            // set to the keyword DEFAULT. This causes the UPDATE action of a MERGE statement
+            // to have the same wrong behavior as a regular UPDATE statement. See derby-6414.
+            //
+            if ( cd.isAutoincrement() && (origRC.getExpression() instanceof NumericConstantNode) )
+            {
+                DataValueDescriptor numericValue = ((NumericConstantNode) origRC.getExpression()).getValue();
+                
+                if ( numericValue == null )
+                {
+                    ResultColumn    newRC = makeAutoGenRC( targetTable, origRC, i+1 );
+                    newRC.setVirtualColumnId( origRC.getVirtualColumnId() );
+                    _thenColumns.setElementAt( newRC, i  );
+
+                    continue;
+                }
+            }
+
+            //
+            // VirtualColumnNodes are skipped at code-generation time. This can result in
+            // NPEs when evaluating generation expressions. Replace VirtualColumnNodes with
+            // UntypedNullConstantNodes, except for identity columns, which require special
+            // handling below.
+            //
+            if ( !origRC.isAutoincrement() && (origRC.getExpression() instanceof VirtualColumnNode) )
+            {
+                origRC.setExpression( new UntypedNullConstantNode( getContextManager() ) );
+            }
+
+            //
+            // Generated columns need special handling. The value needs to be recalculated
+            // under the following circumstances:
+            //
+            // 1) It's the after image of the column
+            //
+            // 2) AND the statement causes the value to change.
+            //
+            // Otherwise, the value should be set to whatever is in the row coming out
+            // of the driving left join.
+            //
+            if ( cd.hasGenerationClause() )
+            {
+                if ( isAfterColumn && changedGeneratedColumns.contains( columnName ) )
+                {
+                    // Set the expression to something that won't choke ResultColumnList.generateEvaluatedRow().
+                    // The value will be a Java null at execution time, which will cause the value
+                    // to be re-generated.
+                    origRC.setExpression( new UntypedNullConstantNode( getContextManager() ) );
+                }
+                else
+                {
+                    ColumnReference cr = new ColumnReference
+                        ( columnName, targetTable.getTableName(), getContextManager() );
+                    origRC.setExpression( cr );
+
+                    // remove the column descriptor in order to turn off hasGenerationClause()
+                    origRC.columnDescriptor = null;
+                }
+                
+                continue;
+            }
+
+            if ( isAfterColumn )
+            {
+                for ( int ic = 0; ic < beforeRow.size(); ic++ )
+                {
+                    ResultColumn    icRC = beforeRow.elementAt( ic );
+
+                    if ( columnName.equals( icRC.getName() ) )
+                    {
+                        ResultColumn    newRC = null;
+                    
+                        // replace DEFAULT for a generated or identity column
+                        ResultColumn    valueRC = afterValues.elementAt( ic );
+
+                        if ( valueRC.wasDefaultColumn() || (valueRC.getExpression() instanceof UntypedNullConstantNode ) )
+                        {
+                            if ( !cd.isAutoincrement() )
+                            {
+                                //
+                                // Eliminate column references under identity columns. They
+                                // will mess up the code generation.
+                                //
+                                ValueNode   expr = origRC.getExpression();
+                                if ( expr instanceof ColumnReference )
+                                {
+                                    origRC.setExpression( new UntypedNullConstantNode( getContextManager() ) );
+                                }
+                                continue;
+                            }
+
+                            newRC = makeAutoGenRC( targetTable, origRC, i+1 );
+                        }
+                        else
+                        {
+                            newRC = valueRC.cloneMe();
+                            newRC.setType( origRC.getTypeServices() );
+                        }
+
+                        newRC.setVirtualColumnId( origRC.getVirtualColumnId() );
+                        _thenColumns.setElementAt( newRC, i  );
+                        changed = true;
+                        break;
+                    }
+                }
+            }
+
+            // plug in defaults if we haven't done so already
+            if ( !changed )
+            {
+                DefaultInfoImpl     defaultInfo = (DefaultInfoImpl) cd.getDefaultInfo();
+
+				if ( (defaultInfo != null) && !defaultInfo.isGeneratedColumn() && !cd.isAutoincrement() )
+				{
+                    _thenColumns.setDefault( origRC, cd, defaultInfo );
+                    changed = true;
+				}
+            }
+
+            // set the result column name correctly for buildThenColumnSignature()
+            ResultColumn    finalRC = _thenColumns.elementAt( i );
+            finalRC.setName( cd.getColumnName() );
+            
+            //
+            // Turn off the autogenerated bit for identity columns so that
+            // ResultColumnList.generateEvaluatedRow() doesn't try to compile
+            // code to generate values for the before images in UPDATE rows.
+            // This logic will probably need to be revisited as part of fixing derby-6414.
+            //
+            finalRC.resetAutoincrementGenerated();
+        }   // end loop through _thenColumns
+    }
+
+    /** Get the names of the columns explicitly changed by SET clauses */
+    private HashSet<String> getChangedColumnNames()
+        throws StandardException
+    {
+        HashSet<String> result = new HashSet<String>();
+
+        for ( int i = 0; i < _updateColumns.size(); i++ )
+        {
+            String  columnName = _updateColumns.elementAt( i ).getName();
+            result.add( columnName );
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * Get the names of the generated columns which are changed
+     * by the UPDATE statement. These are the generated columns which
+     * match one of the following conditions:
+     * </p>
+     *
+     * <ul>
+     * <li>Are explicitly mentioned on the left side of a SET clause.</li>
+     * <li>Are built from other columns which are explicitly mentioned on the left side of a SET clause.</li>
+     * </ul>
+     */
+    private HashSet<String> getChangedGeneratedColumnNames
+        (
+         TableDescriptor    targetTableDescriptor,
+         HashSet<String>    changedColumnNames  // columns which are explicitly mentioned on the left side of a SET clause
+         )
+        throws StandardException
+    {
+        HashSet<String> result = new HashSet<String>();
+
+        for ( ColumnDescriptor cd : targetTableDescriptor.getColumnDescriptorList() )
+        {
+            if ( !cd.hasGenerationClause() ) { continue; }
+
+            if ( changedColumnNames.contains( cd.getColumnName() ) )
+            {
+                result.add( cd.getColumnName() );
+                continue;
+            }
+
+            String[]    referencedColumns = cd.getDefaultInfo().getReferencedColumnNames();
+
+            for ( String referencedColumnName : referencedColumns )
+            {
+                if ( changedColumnNames.contains( referencedColumnName ) )
+                {
+                    result.add( referencedColumnName );
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+
     ////////////////////// DELETE ///////////////////////////////
 
     /** Bind a WHEN MATCHED ... THEN DELETE clause */
@@ -428,7 +740,7 @@ public class MatchingClauseNode extends 
 
         _dml.bindStatement();
 
-        buildInsertThenColumns( targetTable );
+        buildThenColumnsForInsert( fullFromList, targetTable, _dml.resultSet.resultColumns, _insertColumns, _insertValues );
     }
 
     /**  Bind the values in the INSERT list */
@@ -476,13 +788,27 @@ public class MatchingClauseNode extends 
         bindExpressions( _insertValues, fullFromList );
     }
     
-    /** Construct the row in the temporary table which drives an INSERT action */
-    private void    buildInsertThenColumns( FromTable targetTable )
+    /**
+     * <p>
+     * Construct the row in the temporary table which drives an INSERT action.
+     * Unlike a DELETE, whose temporary row is just a list of copied columns, the
+     * temporary row for INSERT may contain complex expressions which must
+     * be code-generated later on.
+     * </p>
+     */
+    private void    buildThenColumnsForInsert
+        (
+         FromList fullFromList,
+         FromTable targetTable,
+         ResultColumnList   fullRow,
+         ResultColumnList insertColumns,
+         ResultColumnList insertValues
+         )
         throws StandardException
     {
         TableDescriptor td = targetTable.getTableDescriptor();
 
-        _thenColumns = _dml.resultSet.resultColumns.copyListAndObjects();
+        _thenColumns = fullRow.copyListAndObjects();
 
         //
         // Here we set up for the evaluation of expressions in the temporary table
@@ -497,10 +823,10 @@ public class MatchingClauseNode extends 
         //
         for ( int i = 0; i < _thenColumns.size(); i++ )
         {
-            ColumnDescriptor    cd = td.getColumnDescriptor( i + 1 );
             ResultColumn    origRC = _thenColumns.elementAt( i );
+
             String              columnName = origRC.getName();
-            
+            ColumnDescriptor    cd = td.getColumnDescriptor( columnName );
             boolean         changed = false;
 
             //
@@ -514,20 +840,26 @@ public class MatchingClauseNode extends 
                 origRC.setExpression( new UntypedNullConstantNode( getContextManager() ) );
             }
 
-            for ( int ic = 0; ic < _insertColumns.size(); ic++ )
+            if ( cd.hasGenerationClause() )
             {
-                ResultColumn    icRC = _insertColumns.elementAt( ic );
+                origRC.setExpression( new UntypedNullConstantNode( getContextManager() ) );
+                continue;
+            }
+
+            for ( int ic = 0; ic < insertColumns.size(); ic++ )
+            {
+                ResultColumn    icRC = insertColumns.elementAt( ic );
 
                 if ( columnName.equals( icRC.getName() ) )
                 {
                     ResultColumn    newRC = null;
                     
                     // replace DEFAULT for a generated or identity column
-                    ResultColumn    insertRC =_insertValues.elementAt( ic );
+                    ResultColumn    valueRC = insertValues.elementAt( ic );
 
-                    if ( insertRC.wasDefaultColumn() || (insertRC.getExpression() instanceof UntypedNullConstantNode ) )
+                    if ( valueRC.wasDefaultColumn() || (valueRC.getExpression() instanceof UntypedNullConstantNode ) )
                     {
-                       if ( !cd.isAutoincrement() )
+                        if ( !cd.isAutoincrement() )
                         {
                             //
                             // Eliminate column references under identity columns. They
@@ -541,18 +873,11 @@ public class MatchingClauseNode extends 
                             continue;
                         }
 
-                        ColumnReference autoGenCR = new ColumnReference( columnName, targetTable.getTableName(), getContextManager() );
-                        ResultColumn    autoGenRC = new ResultColumn( autoGenCR, null, getContextManager() );
-                        VirtualColumnNode autoGenVCN = new VirtualColumnNode( targetTable, autoGenRC, i + 1, getContextManager() );
-
-                        newRC = new ResultColumn( autoGenCR, autoGenVCN, getContextManager() );
-
-                        // set the type so that buildThenColumnSignature() will function correctly
-                        newRC.setType( origRC.getTypeServices() );
+                        newRC = makeAutoGenRC( targetTable, origRC, i+1 );
                     }
                     else
                     {
-                        newRC = insertRC.cloneMe();
+                        newRC = valueRC.cloneMe();
                         newRC.setType( origRC.getTypeServices() );
                     }
 
@@ -578,9 +903,39 @@ public class MatchingClauseNode extends 
             // set the result column name correctly for buildThenColumnSignature()
             ResultColumn    finalRC = _thenColumns.elementAt( i );
             finalRC.setName( cd.getColumnName() );
+
         }   // end loop through _thenColumns
+
     }
 
+    /**
+     * <p>
+     * Make a ResultColumn for an identity column which is being set to the DEFAULT
+     * value. This special ResultColumn will make it through code generation so that it
+     * will be calculated when the INSERT/UPDATE action is run.
+     * </p>
+     */
+    private ResultColumn    makeAutoGenRC
+        (
+         FromTable targetTable,
+         ResultColumn   origRC,
+         int    virtualColumnID
+         )
+        throws StandardException
+    {
+        String              columnName = origRC.getName();
+        ColumnReference autoGenCR = new ColumnReference( columnName, targetTable.getTableName(), getContextManager() );
+        ResultColumn    autoGenRC = new ResultColumn( autoGenCR, null, getContextManager() );
+        VirtualColumnNode autoGenVCN = new VirtualColumnNode( targetTable, autoGenRC, virtualColumnID, getContextManager() );
+        ResultColumn    newRC = new ResultColumn( autoGenCR, autoGenVCN, getContextManager() );
+
+        // set the type so that buildThenColumnSignature() will function correctly
+        newRC.setType( origRC.getTypeServices() );
+
+        return newRC;
+    }
+
+
     ////////////////////// bind() MINIONS ///////////////////////////////
 
     /** Boilerplate for binding a list of ResultColumns against a FromList */
@@ -616,10 +971,6 @@ public class MatchingClauseNode extends 
         throws StandardException
     {
         if ( isDeleteClause() ) { bindDeleteThenColumns( selectList ); }
-        else if ( isUpdateClause() )
-        {
-            throw StandardException.newException( SQLState.NOT_IMPLEMENTED, "MERGE" );
-        }
     }
 
     /**
@@ -755,6 +1106,7 @@ public class MatchingClauseNode extends 
         (
          ActivationClassBuilder acb,
          ResultColumnList selectList,
+         ResultSetNode  generatedScan,
          HalfOuterJoinNode  hojn,
          int    clauseNumber
          )
@@ -762,7 +1114,7 @@ public class MatchingClauseNode extends 
     {
         _clauseNumber = clauseNumber;
 
-        if ( isInsertClause() ) { generateInsertRow( acb, selectList, hojn ); }
+        if ( isInsertClause() || isUpdateClause() ) { generateInsertUpdateRow( acb, selectList, generatedScan, hojn ); }
         
         _actionMethodName = "mergeActionMethod_" + _clauseNumber;
         
@@ -793,13 +1145,15 @@ public class MatchingClauseNode extends 
     private void    remapConstraints()
         throws StandardException
     {
-        if( !isInsertClause() ) { return; }
+        if( isDeleteClause()) { return; }
         else
         {
             CollectNodesVisitor<ColumnReference> getCRs =
                 new CollectNodesVisitor<ColumnReference>(ColumnReference.class);
 
-            ValueNode   checkConstraints = ((InsertNode) _dml).checkConstraints;
+            ValueNode   checkConstraints = isInsertClause() ?
+                ((InsertNode) _dml).checkConstraints :
+                ((UpdateNode) _dml).checkConstraints;
 
             if ( checkConstraints != null )
             {
@@ -840,23 +1194,24 @@ public class MatchingClauseNode extends 
     
     /**
      * <p>
-     * Generate a method to build a row for the temporary table for INSERT actions.
+     * Generate a method to build a row for the temporary table for INSERT/UPDATE actions.
      * The method stuffs each column in the row with the result of the corresponding
      * expression built out of columns in the current row of the driving left join.
      * The method returns the stuffed row.
      * </p>
      */
-    void    generateInsertRow
+    void    generateInsertUpdateRow
         (
          ActivationClassBuilder acb,
          ResultColumnList selectList,
+         ResultSetNode  generatedScan,
          HalfOuterJoinNode  hojn
          )
         throws StandardException
     {
         // point expressions in the temporary row at the columns in the
         // result column list of the driving left join.
-        adjustThenColumns( selectList, hojn );
+        adjustThenColumns( selectList, generatedScan, hojn );
         
         _rowMakingMethodName = "mergeRowMakingMethod_" + _clauseNumber;
         
@@ -873,18 +1228,19 @@ public class MatchingClauseNode extends 
 
     /**
      * <p>
-     * Point the column references in the temporary row at corresponding
+     * Point the column references in the temporary row at the corresponding
      * columns returned by the driving left join.
      * </p>
      */
     void    adjustThenColumns
         (
          ResultColumnList selectList,
+         ResultSetNode  generatedScan,
          HalfOuterJoinNode  hojn
          )
         throws StandardException
     {
-        ResultColumnList    leftJoinResult = hojn.resultColumns;
+        ResultColumnList    leftJoinResult = generatedScan.resultColumns;
         CollectNodesVisitor<ColumnReference> getCRs =
             new CollectNodesVisitor<ColumnReference>( ColumnReference.class );
         _thenColumns.accept( getCRs );
@@ -894,6 +1250,40 @@ public class MatchingClauseNode extends 
             ResultColumn    leftJoinRC = leftJoinResult.elementAt( getSelectListOffset( selectList, cr ) - 1 );
             cr.setSource( leftJoinRC );
         }
+
+        //
+        // For an UPDATE action, the final column in the temporary row is the
+        // RowLocation. Point it at the last column in the row returned by the left join.
+        //
+        int                 lastRCSlot = _thenColumns.size() - 1;
+        ResultColumn    lastRC = _thenColumns.elementAt( lastRCSlot );
+
+        if ( isRowLocation( lastRC ) )
+        {
+            ResultColumn    lastLeftJoinRC = leftJoinResult.elementAt( leftJoinResult.size() - 1 );
+            ValueNode       value = lastLeftJoinRC.getExpression();
+            String              columnName = lastLeftJoinRC.exposedName;
+            ColumnReference rowLocationCR = new ColumnReference
+                ( columnName, hojn.getTableName(), getContextManager() );
+
+            rowLocationCR.setSource( lastLeftJoinRC );
+            
+            ResultColumn    rowLocationRC = new ResultColumn( columnName, rowLocationCR, getContextManager() );
+
+            _thenColumns.removeElementAt( lastRCSlot );
+            _thenColumns.addResultColumn( rowLocationRC );
+        }
+    }
+
+    /** Return true if the ResultColumn represents a RowLocation */
+    private boolean isRowLocation( ResultColumn rc ) throws StandardException
+    {
+        if ( rc.getExpression() instanceof CurrentRowLocationNode ) { return true; }
+
+        DataTypeDescriptor  dtd = rc.getTypeServices();
+        if ( (dtd != null) && (dtd.getTypeId().isRefTypeId()) ) { return true; }
+
+        return false;
     }
     
     ///////////////////////////////////////////////////////////////////////////////////

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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -234,14 +234,6 @@ public final class MergeNode extends DML
         {
             mcn.bindResultSetNumbers( this, _leftJoinFromList );
         }
-        
-        for ( MatchingClauseNode mcn : _matchingClauses )
-        {
-            if ( mcn.isUpdateClause() )
-            {
-                throw StandardException.newException( SQLState.NOT_IMPLEMENTED, "MERGE" );
-            }
-        }
 	}
 
     /** Get the exposed name of a FromTable */
@@ -294,9 +286,9 @@ public final class MergeNode extends DML
             {
                 mcn.bindRefinement( this, _leftJoinFromList );
             }
-        
-            ResultColumnList    selectList = buildSelectList();
 
+            ResultColumnList    selectList = buildSelectList();
+            
             // save a copy so that we can remap column references when generating the temporary rows
             _selectList = selectList.copyListAndObjects();
 
@@ -342,6 +334,9 @@ public final class MergeNode extends DML
         }
     }
 
+    /** Get the target table for the MERGE statement */
+    FromBaseTable   getTargetTable() { return _targetTable; }
+
     /** Throw a "not base table" exception */
     private void    notBaseTable()  throws StandardException
     {
@@ -516,14 +511,11 @@ public final class MergeNode extends DML
         ( HashMap<String,ColumnReference> map, ResultColumnList rcl )
         throws StandardException
     {
-        ArrayList<ColumnReference>  colRefs = new ArrayList<ColumnReference>();
+        CollectNodesVisitor<ColumnReference> getCRs =
+            new CollectNodesVisitor<ColumnReference>( ColumnReference.class );
 
-        for ( int i = 0; i < rcl.size(); i++ )
-        {
-            ResultColumn    rc = rcl.elementAt( i );
-            ColumnReference cr = rc.getReference();
-            if ( cr != null ) { colRefs.add( cr ); }
-        }
+        rcl.accept( getCRs );
+        List<ColumnReference> colRefs = getCRs.getList();
 
         getColumnsFromList( map, colRefs );
     }
@@ -535,18 +527,29 @@ public final class MergeNode extends DML
     {
         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() );
-            }
+            addColumn( map, cr );
+        }
+    }
 
-            String  key = makeDCMKey( cr.getTableName(), cr.getColumnName() );
-            if ( map.get( key ) == null )
-            {
-                map.put( key, cr );
-            }
+    /** Add a column to the evolving map of referenced columns */
+    void    addColumn
+        (
+         HashMap<String,ColumnReference> map,
+         ColumnReference    cr
+         )
+        throws StandardException
+    {
+        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 );
         }
     }
 
@@ -615,14 +618,6 @@ public final class MergeNode extends DML
     void generate( ActivationClassBuilder acb, MethodBuilder mb )
 							throws StandardException
 	{
-        for ( MatchingClauseNode mcn : _matchingClauses )
-        {
-            if ( mcn.isUpdateClause() )
-            {
-                throw StandardException.newException( SQLState.NOT_IMPLEMENTED, "MERGE" );
-            }
-        }
-
         int     clauseCount = _matchingClauses.size();
 
 		/* generate the parameters */
@@ -633,12 +628,16 @@ public final class MergeNode extends DML
         // arg 1: the driving left join 
         _leftJoinCursor.generate( acb, mb );
 
+        // dig up the actual result set which was generated and which will drive the MergeResultSet
+        ScrollInsensitiveResultSetNode  sirs = (ScrollInsensitiveResultSetNode) _leftJoinCursor.resultSet;
+        ResultSetNode   generatedScan = sirs.getChildResult();
+
         ConstantAction[]    clauseActions = new ConstantAction[ clauseCount ];
         for ( int i = 0; i < clauseCount; i++ )
         {
             MatchingClauseNode  mcn = _matchingClauses.get( i );
 
-            mcn.generate( acb, _selectList, _hojn, i );
+            mcn.generate( acb, _selectList, generatedScan, _hojn, i );
             clauseActions[ i ] = mcn.makeConstantAction( acb );
         }
         _constantAction = getGenericConstantActionFactory().getMergeConstantAction( clauseActions );

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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -424,11 +424,18 @@ public final class UpdateNode extends DM
 
 		changedColumnIds = getChangedColumnIds(resultSet.getResultColumns());
 
+        //
+        // Trigger transition tables are implemented as VTIs. This short-circuits some
+        // necessary steps if the source table of a MERGE statement is a trigger
+        // transition table. The following boolean is meant to prevent that short-circuiting.
+        //
+        boolean needBaseColumns = (targetVTI == null) || inMatchingClause();
+        
 		/*
 		** We need to add in all the columns that are needed
 		** by the constraints on this table.  
 		*/
-		if (!allColumns && targetVTI == null)
+		if (!allColumns && needBaseColumns)
 		{
 			getCompilerContext().pushCurrentPrivType( Authorizer.NULL_PRIV);
 			try
@@ -472,7 +479,7 @@ public final class UpdateNode extends DM
 
         ValueNode rowLocationNode;
 
-		if (targetVTI == null)
+		if (needBaseColumns)
 		{
 			/* Append the list of "after" columns to the list of "before" columns,
 			 * preserving the afterColumns list.  (Necessary for binding
@@ -552,7 +559,7 @@ public final class UpdateNode extends DM
 			}
 		}
 
-        if( null != targetVTI)
+        if( null != targetVTI && !inMatchingClause() )
 		{
             deferred = VTIDeferModPolicy.deferIt( DeferModification.UPDATE_STATEMENT,
                                                   targetVTI,
@@ -598,7 +605,7 @@ public final class UpdateNode extends DM
 
 		getCompilerContext().popCurrentPrivType();		
 
-	} // end of bind()
+    } // end of bind()
 
     @Override
 	int getPrivType()
@@ -634,7 +641,7 @@ public final class UpdateNode extends DM
 		** Updates are also deferred if they update a column in the index
 		** used to scan the table being updated.
 		*/
-		if (! deferred )
+		if ( !deferred && !inMatchingClause() )
 		{
 			ConglomerateDescriptor updateCD =
 										targetTable.
@@ -666,7 +673,8 @@ public final class UpdateNode extends DM
 						deferred, changedColumnIds);
 		}
 
-        int lckMode = resultSet.updateTargetLockMode();
+        int lckMode = inMatchingClause() ?
+            TransactionController.MODE_RECORD : resultSet.updateTargetLockMode();
 		long heapConglomId = targetTableDescriptor.getHeapConglomerateId();
 		TransactionController tc = 
 			getLanguageConnectionContext().getTransactionCompile();
@@ -794,9 +802,18 @@ public final class UpdateNode extends DM
 		*/
 
 		acb.pushGetResultSetFactoryExpression(mb);
-		resultSet.generate(acb, mb); // arg 1
 
-        if( null != targetVTI)
+        // arg 1
+        if ( inMatchingClause() )
+        {
+            matchingClause.generateResultSetField( acb, mb );
+        }
+        else
+        {
+            resultSet.generate( acb, mb );
+        }
+
+        if( null != targetVTI && !inMatchingClause() )
         {
 			targetVTI.assignCostEstimate(resultSet.getNewCostEstimate());
             mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getUpdateVTIResultSet", ClassName.ResultSet, 1);

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.java?rev=1545343&r1=1545342&r2=1545343&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.java Mon Nov 25 17:02:40 2013
@@ -51,19 +51,9 @@ abstract class DMLVTIResultSet extends D
 	UpdatableVTIConstantAction	constants;
 	TransactionController 	tc;
 
-    ResultDescription 		resultDescription;
 	private int						numOpens;
 	boolean				firstExecute;
 
-	/**
-     * Returns the description of the inserted rows.
-     * REVISIT: Do we want this to return NULL instead?
-	 */
-	public ResultDescription getResultDescription()
-	{
-	    return resultDescription;
-	}
-
     /**
 	 *
 	 * @exception StandardException		Thrown on error

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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -27,6 +27,8 @@ import org.apache.derby.iapi.services.io
 import org.apache.derby.iapi.services.io.StreamStorable;
 import org.apache.derby.shared.common.sanity.SanityManager;
 import org.apache.derby.iapi.sql.Activation;
+import org.apache.derby.iapi.sql.ResultColumnDescriptor;
+import org.apache.derby.iapi.sql.ResultDescription;
 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
 import org.apache.derby.iapi.sql.execute.ConstantAction;
 import org.apache.derby.iapi.sql.execute.ExecRow;
@@ -34,6 +36,7 @@ import org.apache.derby.iapi.sql.execute
 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
 import org.apache.derby.iapi.store.access.TransactionController;
 import org.apache.derby.iapi.transaction.TransactionControl;
+import org.apache.derby.iapi.types.DataTypeDescriptor;
 import org.apache.derby.iapi.types.DataValueDescriptor;
 
 /**
@@ -51,6 +54,15 @@ abstract class DMLWriteResultSet extends
 
 	public long rowCount;
 
+	// divined at run time
+    protected   ResultDescription 		resultDescription;
+
+    /**
+     * This array contains data value descriptors that can be used (and reused)
+     * to hold the normalized column values.
+     */
+    protected DataValueDescriptor[] cachedDestinations;
+
 
 	/**
 	 * Constructor
@@ -109,6 +121,14 @@ abstract class DMLWriteResultSet extends
     @Override
 	public final long	modifiedRowCount() { return rowCount + RowUtil.getRowCountBase(); }
 
+	/**
+     * Returns the description of the inserted rows.
+     * REVISIT: Do we want this to return NULL instead?
+	 */
+	public ResultDescription getResultDescription()
+	{
+	    return resultDescription;
+	}
 
 	/**
 	 * Get next row from the source result set.
@@ -278,4 +298,60 @@ abstract class DMLWriteResultSet extends
 	{
 		return this.constantAction.getIndexNameFromCID(indexCID);
 	}
+
+    /**
+     * <p>
+     * Normalize a row as part of the INSERT/UPDATE action of a MERGE statement.
+     * This applies logic usually found in a NormalizeResultSet, which is missing for
+     * the MERGE statement.
+     * </p>
+     */
+    protected   ExecRow normalizeRow( NoPutResultSet sourceResultSet, ExecRow row )
+        throws StandardException
+    {
+        //
+        // Make sure that the evaluated expressions fit in the base table row.
+        //
+        int count = resultDescription.getColumnCount();
+        if ( cachedDestinations == null )
+        {
+            cachedDestinations = new DataValueDescriptor[ count ];
+            for ( int i = 0; i < count; i++)
+            {
+                int         position = i + 1;
+                ResultColumnDescriptor  colDesc = resultDescription.getColumnDescriptor( position );
+                cachedDestinations[ i ] = colDesc.getType().getNull();
+            }
+        }
+
+        for ( int i = 0; i < count; i++ )
+        {
+            int         position = i + 1;
+            DataTypeDescriptor  dtd = resultDescription.getColumnDescriptor( position ).getType();
+
+            if ( row.getColumn( position ) == null )
+            {
+                row.setColumn( position, dtd.getNull() );
+            }
+
+            row.setColumn
+                (
+                 position,
+                 NormalizeResultSet.normalizeColumn
+                 (
+                  dtd,
+                  row,
+                  position,
+                  cachedDestinations[ i ],
+                  resultDescription
+                  )
+                 );
+        }
+
+        // put the row where expressions in constraints can access it
+        activation.setCurrentRow( row, sourceResultSet.resultSetNumber() );
+
+        return row;
+    }
+    
 }

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=1545343&r1=1545342&r2=1545343&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 Mon Nov 25 17:02:40 2013
@@ -51,7 +51,6 @@ class DeleteResultSet extends DMLWriteRe
 {
 	private TransactionController   	tc;
 	DeleteConstantAction		constants;
-    protected ResultDescription 				resultDescription;
 	protected  NoPutResultSet			source;
 	NoPutResultSet			savedSource;
 	int 							numIndexes;
@@ -81,16 +80,6 @@ class DeleteResultSet extends DMLWriteRe
 	ExecRow		deferredRLRow = null;
 	int	numberOfBaseColumns = 0;
 
-	/**
-     * Returns the description of the deleted rows.
-     * REVISIT: Do we want this to return NULL instead?
-	 */
-    @Override
-	public ResultDescription getResultDescription()
-	{
-	    return resultDescription;
-	}
-
     /*
      * class interface
      *

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java?rev=1545343&r1=1545342&r2=1545343&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java Mon Nov 25 17:02:40 2013
@@ -102,7 +102,6 @@ class InsertResultSet extends DMLWriteRe
 
 	// divined at run time
 
-    private	ResultDescription 		resultDescription;
 	private RowChanger 				rowChanger;
 
 	private	TransactionController 	tc;
@@ -171,22 +170,6 @@ class InsertResultSet extends DMLWriteRe
 	private long					identityVal;  //support of IDENTITY_LOCAL_VAL function
 	private boolean					setIdentity;
 	
-    /**
-     * This array contains data value descriptors that can be used (and reused)
-     * to hold the normalized column values.
-     */
-    private DataValueDescriptor[] cachedDestinations;
-
-
-	/**
-     * Returns the description of the inserted rows.
-     * REVISIT: Do we want this to return NULL instead?
-	 */
-	public ResultDescription getResultDescription()
-	{
-	    return resultDescription;
-	}
-
 	// TargetResultSet interface
 
 	/**
@@ -1248,7 +1231,7 @@ class InsertResultSet extends DMLWriteRe
 	{
 		ExecRow row = super.getNextRowCore( source );
 
-        if ( (row != null) && constants.underMerge() ) { row = processMergeRow( row ); }
+        if ( (row != null) && constants.underMerge() ) { row = processMergeRow( source, row ); }
 
         return row;
 	}
@@ -1258,7 +1241,7 @@ class InsertResultSet extends DMLWriteRe
      * Special handling if this is an INSERT action of a MERGE statement.
      * </p>
      */
-	private ExecRow processMergeRow( ExecRow row )
+	private ExecRow processMergeRow( NoPutResultSet sourceRS, ExecRow row )
 		throws StandardException
 	{
         //
@@ -1293,49 +1276,7 @@ class InsertResultSet extends DMLWriteRe
             }
         }
 
-        //
-        // Make sure that the evaluated expressions fit in the base table row.
-        //
-        int count = resultDescription.getColumnCount();
-        if ( cachedDestinations == null )
-        {
-            cachedDestinations = new DataValueDescriptor[ count ];
-            for ( int i = 0; i < count; i++)
-            {
-                int         position = i + 1;
-                ResultColumnDescriptor  colDesc = resultDescription.getColumnDescriptor( position );
-                cachedDestinations[ i ] = colDesc.getType().getNull();
-            }
-        }
-
-        for ( int i = 0; i < count; i++ )
-        {
-            int         position = i + 1;
-            DataTypeDescriptor  dtd = resultDescription.getColumnDescriptor( position ).getType();
-
-            if ( row.getColumn( position ) == null )
-            {
-                row.setColumn( position, dtd.getNull() );
-            }
-
-            row.setColumn
-                (
-                 position,
-                 NormalizeResultSet.normalizeColumn
-                 (
-                  dtd,
-                  row,
-                  position,
-                  cachedDestinations[ i ],
-                  resultDescription
-                  )
-                 );
-        }
-
-        // put the row where expressions in constraints can access it
-        activation.setCurrentRow( row, sourceResultSet.resultSetNumber() );
-
-        return row;
+        return normalizeRow( sourceRS, row );
 	}
 
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java?rev=1545343&r1=1545342&r2=1545343&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java Mon Nov 25 17:02:40 2013
@@ -61,7 +61,6 @@ class UpdateResultSet extends DMLWriteRe
 	private ExecRow 					deferredSparseRow;
 	UpdateConstantAction		constants;
 	
-    private ResultDescription 		resultDescription;
 	private NoPutResultSet			source;
 	NoPutResultSet			savedSource;
 	private RowChanger				rowChanger;
@@ -99,15 +98,6 @@ class UpdateResultSet extends DMLWriteRe
 	boolean deferred;
 	boolean beforeUpdateCopyRequired = false;
 
-	/**
-     * Returns the description of the updated rows.
-     * REVISIT: Do we want this to return NULL instead?
-	 */
-	public ResultDescription getResultDescription()
-	{
-	    return resultDescription;
-	}
-
     /*
      * class interface
      *
@@ -437,6 +427,7 @@ class UpdateResultSet extends DMLWriteRe
 
 		boolean rowsFound = false;
 		row = getNextRowCore(source);
+
 		if (row!=null)
 			rowsFound = true;
 		else
@@ -589,6 +580,33 @@ class UpdateResultSet extends DMLWriteRe
 		return rowsFound;
 	}
 
+    @Override
+	protected ExecRow getNextRowCore( NoPutResultSet source )
+		throws StandardException
+	{
+		ExecRow row = super.getNextRowCore( source );
+
+        if ( (row != null) && constants.underMerge() ) { row = processMergeRow( source, row ); }
+
+        return row;
+	}
+
+    /**
+     * <p>
+     * Special handling if this is an UPDATE action of a MERGE statement.
+     * </p>
+     */
+	private ExecRow processMergeRow( NoPutResultSet sourceRS, ExecRow row )
+		throws StandardException
+	{
+        //
+        // After we fix derby-6414, we will need to handle the DEFAULT keyword
+        // for identity columns, just as we do in InsertResultSet.processMergeRow().
+        // For the moment, we just allow the bad behavior described by derby-6414.
+        //
+        return normalizeRow( sourceRS, row );
+	}
+
 	/* beetle 3865, updateable cursor use index. If the row we are updating has new value that
 	 * falls into the direction of the index scan of the cursor, we save this rid into a hash table
 	 * (for fast search), so that when the cursor hits it again, it knows to skip it.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java?rev=1545343&r1=1545342&r2=1545343&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java Mon Nov 25 17:02:40 2013
@@ -36,6 +36,7 @@ import org.apache.derby.iapi.store.raw.C
 
 import org.apache.derby.iapi.types.DataType;
 import org.apache.derby.iapi.types.DataValueDescriptor;
+import org.apache.derby.iapi.types.RefDataValue;
 import org.apache.derby.iapi.types.RowLocation;
 
 import java.io.ObjectOutput;
@@ -63,7 +64,7 @@ import java.io.IOException;
  *     record id(CompressedNumber.writeInt())
  **/
 
-public class HeapRowLocation extends DataType implements RowLocation
+public class HeapRowLocation extends DataType implements RowLocation, RefDataValue
 {
 	/**
 	The HeapRowLocation simply maintains a raw store record handle.
@@ -196,6 +197,13 @@ public class HeapRowLocation extends Dat
 			return 1;
 	}
 
+    public  void    setValue( RowLocation rowLocation )
+    {
+        HeapRowLocation hrl = (HeapRowLocation) rowLocation;
+
+        setFrom( hrl.rh );
+    }
+
 	/*
 	** Methods of HeapRowLocation
 	*/



Mime
View raw message