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 ma...@apache.org on 2014/10/01 06:28:01 UTC

svn commit: r1628596 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/ engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/impl/sql/execute/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/

Author: mamta
Date: Wed Oct  1 04:28:01 2014
New Revision: 1628596

URL: http://svn.apache.org/r1628596
Log:
DERBY-6742(For update statement, collect generated keys if Statement.RETURN_GENERATED_KEYS flag is supplied to the JDBC call.)

This commit now allows JDBC statement to receive a resultset for auto generated keys for an UPDATE statement updating the generated values with DEFAULT clause. Such a resultset will be generated only if the UPDATE statement updated a single row and there were generated columns involved in the update. This functionality already exists for INSERT statement. It is currently implemented for INSERT statement by calling VALUES IDENTITY_VAL_LOCAL() from EmbedStatement.getGeneratedKeys(). I have used this existing mechanism to collect generated values resultset for UPDATE too. This means that the scope of IDENTITY_VAL_LOCAL() function has grown from INSERT statement to also include UPDATE statement. This will require us to update the documentation for IDENTITY_VAL_LOCAL(). I have created DERBY-6753(Docs for IDENTITY_VAL_LOCAL needs to be updated to indicate that the return value will be impacted by single row UPDATE of identity column) for the documentation task.


Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientPreparedStatement.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteGeneratedColumnsResultSet.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/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientPreparedStatement.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientPreparedStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientPreparedStatement.java Wed Oct  1 04:28:01 2014
@@ -2044,7 +2044,9 @@ public class ClientPreparedStatement ext
 
                 chainAutoCommit = connection_.willAutoCommitGenerateFlow() && isAutoCommittableStatement_;
 
-                boolean chainOpenQueryForAutoGeneratedKeys = (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS);
+                boolean chainOpenQueryForAutoGeneratedKeys = 
+                		((sqlUpdateMode_ == isInsertSql__ || sqlUpdateMode_ == isUpdateSql__) 
+                				&& autoGeneratedKeys_ == RETURN_GENERATED_KEYS);
                 writeExecute(getSection(),
                         parameterMetaData_,
                         parameters_,
@@ -2128,7 +2130,8 @@ public class ClientPreparedStatement ext
                 else {
                     readExecute();
 
-                    if (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
+            		if ((sqlUpdateMode_ == isInsertSql__ || sqlUpdateMode_ == isUpdateSql__) 
+            				&& autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
                         if (prepareSentForAutoGeneratedKeys) {
                             preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_.readPrepareDescribeOutput_();
                         }

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientStatement.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/ClientStatement.java Wed Oct  1 04:28:01 2014
@@ -2325,7 +2325,8 @@ public class ClientStatement implements 
                     newSection = agent_.sectionManager_.getDynamicSection(resultSetHoldability_);
 
                     writeExecuteImmediate(sql, newSection);
-                    if (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
+            		if ((sqlUpdateMode_ == isInsertSql__ || sqlUpdateMode_ == isUpdateSql__) 
+            				&& autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
                         // chain a "select from identity_val_local()" to the insert statement
                         if (preparedStatementForAutoGeneratedKeys_ == null) {
                             preparedStatementForAutoGeneratedKeys_ =
@@ -2399,7 +2400,8 @@ public class ClientStatement implements 
                 }
                 readExecuteImmediate();
 
-                if (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
+        		if ((sqlUpdateMode_ == isInsertSql__ || sqlUpdateMode_ == isUpdateSql__) 
+        				&& autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
                     if (prepareSentForAutoGeneratedKeys) {
                         preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_.readPrepareDescribeOutput_();
                     }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java Wed Oct  1 04:28:01 2014
@@ -143,7 +143,7 @@ public class EmbedPreparedStatement exte
 				checkRequiresCallableStatement(activation);
 
 			//bug 4838 - save the auto-generated key information in activation. keeping this
-			//information in lcc will not work work as it can be tampered by a nested trasaction
+			//information in lcc will not work as it can be tampered by a nested transaction
   				if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS)
   					activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames);
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java Wed Oct  1 04:28:01 2014
@@ -244,7 +244,7 @@ public class EmbedStatement extends Conn
      * Executes the given SQL statement and signals the driver that the
      * auto-generated keys indicated in the given array should be made
      * available for retrieval. The driver will ignore the array if the SQL
-     * statement is not an INSERT statement. For use with
+     * statement is not an INSERT/UPDATE statement. For use with
      * statements which may touch more than Integer.MAX_VALUE rows.
      */
 	public long executeLargeUpdate( String sql, int[] columnIndexes ) throws SQLException
@@ -285,7 +285,7 @@ public class EmbedStatement extends Conn
      * Executes the given SQL statement and signals the driver that the
      * auto-generated keys indicated in the given array should be made
      * available for retrieval. The driver will ignore the array if the SQL
-     * statement is not an INSERT statement. For use with
+     * statement is not an INSERT/UPDATE statement. For use with
      * statements which may touch more than Integer.MAX_VALUE rows.
      */
 	public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException
@@ -714,7 +714,7 @@ public class EmbedStatement extends Conn
      * Executes the given SQL statement, which may return multiple
      * results, and signals the driver that any auto-generated keys
      * should be made available for retrieval. The driver will ignore
-     * this signal if the SQL statement is not an INSERT statement.
+     * this signal if the SQL statement is not an INSERT/UPDATE statement.
      *
      * @param sql any SQL statement
      * @param autoGeneratedKeys - a constant indicating whether
@@ -739,12 +739,12 @@ public class EmbedStatement extends Conn
      * This array contains the indexes of the columns in the target table
      * that contain the auto-generated keys that should be made available.
      * The driver will ignore the array if the given SQL statement is not an
-     * INSERT statement.
+     * INSERT/UPDATE statement.
      *
      * @param sql any SQL statement
      * @param columnIndexes - an array of the indexes of the columns in the
-     * inserted row that should be made available for retrieval by a call to
-     * the method getGeneratedKeys
+     * inserted/updated row that should be made available for retrieval by a
+     * call to the method getGeneratedKeys
      * @return rue if the first result is a ResultSet object; false if
      * it is an update count or there are no results
      * @exception SQLException if a database access error occurs
@@ -768,12 +768,12 @@ public class EmbedStatement extends Conn
      * This array contains the names of the columns in the target table
      * that contain the auto-generated keys that should be made available.
      * The driver will ignore the array if the given SQL statement is not an
-     * INSERT statement.
+     * INSERT/UPDATE statement.
      *
      * @param sql any SQL statement
      * @param columnNames - an array of the names of the columns in the
-     * inserted row that should be made available for retrieval by a call to
-     * the method getGeneratedKeys
+     * inserted/updated row that should be made available for retrieval by a
+     * call to the method getGeneratedKeys
      * @return rue if the first result is a ResultSet object; false if
      * it is an update count or there are no results
      * @exception SQLException if a database access error occurs
@@ -1365,8 +1365,8 @@ public class EmbedStatement extends Conn
 				}
 				else {
 
-					// Only applipable for an insert statement, which does not return rows.
-					//the auto-generated keys resultset will be null if used for non-insert statement
+					// Only applicable for an insert/update statement, which does not return rows.
+					//the auto-generated keys resultset will be null if used for other statement
 					if (a.getAutoGeneratedKeysResultsetMode() && (resultsToWrap.getAutoGeneratedKeysResultset() != null))
 					{
 						resultsToWrap.getAutoGeneratedKeysResultset().open();

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteGeneratedColumnsResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteGeneratedColumnsResultSet.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteGeneratedColumnsResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DMLWriteGeneratedColumnsResultSet.java Wed Oct  1 04:28:01 2014
@@ -21,19 +21,24 @@
 
 package org.apache.derby.impl.sql.execute;
 
+import java.util.Arrays;
 import java.util.HashMap;
-
-
-import org.apache.derby.catalog.UUID;
+import java.util.Properties;
 import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.SQLState;
 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.ResultSet;
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 import org.apache.derby.iapi.sql.execute.ConstantAction;
+import org.apache.derby.iapi.sql.execute.ExecRow;
 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
 import org.apache.derby.iapi.types.DataValueDescriptor;
 import org.apache.derby.iapi.types.NumberDataValue;
-import org.apache.derby.iapi.types.RowLocation;
 import org.apache.derby.shared.common.sanity.SanityManager;
+import org.apache.derby.catalog.UUID;
+import org.apache.derby.iapi.sql.execute.RowChanger;
 
 /*
  * This class includes code for auto generated columns that can be shared
@@ -51,6 +56,20 @@ abstract public class DMLWriteGeneratedC
 
 	protected	NoPutResultSet			sourceResultSet;
 
+	//following is for jdbc3.0 feature auto generated keys resultset
+	protected  ResultSet			autoGeneratedKeysResultSet;
+	protected  TemporaryRowHolderImpl	autoGeneratedKeysRowsHolder;
+	protected  int[]                   autoGeneratedKeysColumnIndexes;
+	/**
+	 * If set to true, implies that Derby has generated autoincrement 
+	 * values for this (rep)resultset. During refresh for example, the 
+	 * autoincrement values are not generated but sent from the source 
+	 * to target or vice-versa.
+	 */
+	protected boolean autoincrementGenerated;
+	protected long	  identityVal;  //support of IDENTITY_LOCAL_VAL function
+	protected boolean setIdentity;
+
 	/**
 	 * Constructor
 	 *
@@ -117,5 +136,262 @@ abstract public class DMLWriteGeneratedC
             else
                 itec.copyHashtableToAIHT(aiHashtable);
         }	
+
+        /* autoGeneratedResultset for JDBC3. Nulled after statement execution 
+        is over (ie after it is saved off in LocalSatement object) */
+        if (activation.getAutoGeneratedKeysResultsetMode())
+            autoGeneratedKeysResultSet = autoGeneratedKeysRowsHolder.getResultSet();
+        else
+            autoGeneratedKeysResultSet = null;
+    }
+    
+    /*
+    ** verify the auto-generated key columns list(ie there are no invalid
+    ** column names or positions). This is done at execution time because
+    ** for a precompiled insert statement, user can specify different column
+    ** selections for auto-generated keys.
+    */
+    protected void verifyAutoGeneratedRScolumnsList(UUID targetUUID)
+            throws StandardException{
+        if(activation.getAutoGeneratedKeysResultsetMode())
+        {
+            int[]   agi = activation.getAutoGeneratedKeysColumnIndexes();
+            if ( agi != null ) { 
+                verifyAutoGeneratedColumnsIndexes( agi, targetUUID ); 
+            } else
+            {
+                String[]    agc = activation.getAutoGeneratedKeysColumnNames();
+                if ( agc != null ) { 
+                    verifyAutoGeneratedColumnsNames( agc, targetUUID ); 
+                }
+            }
+        }
+    }
+
+    /**
+     * Verify that the auto-generated columns list (by position) has valid
+     * column positions for the table.
+     */
+    private void verifyAutoGeneratedColumnsIndexes(
+            int[] columnIndexes, UUID targetUUID)
+        throws StandardException
+    {
+        int size = columnIndexes.length;
+        TableDescriptor tabDesc = 
+                lcc.getDataDictionary().getTableDescriptor(targetUUID);
+
+        // all 1-based column ids.
+        for (int i = 0; i < size; i++)
+        {
+            ColumnDescriptor cd = tabDesc.getColumnDescriptor(columnIndexes[i]);
+            if (!verifyAutoGenColumn(cd))
+            {
+                throw StandardException.newException(
+                    SQLState.LANG_INVALID_AUTOGEN_COLUMN_POSITION,
+                    new Integer(columnIndexes[i]), tabDesc.getName());
+            }
+       }
+    }
+
+    /**
+     * Verify that the auto-generated columns list (by name) has valid
+     * column names for the table. If all the column names are valid,
+     * convert column names array to corresponding column positions array
+     * Save that column positions array in activation. We do this to 
+     * simplify the rest of the logic(it only has to deal with column 
+     * positions here after).
+     *
+     * @exception StandardException		Thrown on error if invalid column
+     * name in the list.
+     */
+     private void verifyAutoGeneratedColumnsNames(String[] columnNames, UUID targetUUID)
+		throws StandardException
+     {
+        int size = columnNames.length;
+        int columnPositions[] = new int[size];
+        
+        TableDescriptor tabDesc = 
+            lcc.getDataDictionary().getTableDescriptor(targetUUID);
+        ColumnDescriptor cd;
+
+        for (int i = 0; i < size; i++)
+        {
+            if (columnNames[i] == null)
+            {
+                throw StandardException.newException(
+                    SQLState.LANG_INVALID_AUTOGEN_COLUMN_NAME,
+                    columnNames[i], tabDesc.getName());
+            }
+
+            cd = tabDesc.getColumnDescriptor(columnNames[i]);
+            if (!verifyAutoGenColumn(cd))
+            {
+                throw StandardException.newException(
+                    SQLState.LANG_INVALID_AUTOGEN_COLUMN_NAME,
+                    columnNames[i], tabDesc.getName());
+            }
+
+            columnPositions[i] = cd.getPosition();
+        }
+        activation.setAutoGeneratedKeysResultsetInfo(columnPositions, null);
+	}
+
+	/**
+	 * Check that the received ColumnDescriptor corresponds to a column
+	 * for which it is possible to fetch auto-generated keys.
+	 */
+	private boolean verifyAutoGenColumn(ColumnDescriptor cd)
+	{
+		/* Derby currently gets generated keys by calling the
+		 * IDENTITY_VAL_LOCAL() function (see "getGeneratedKeys()"
+		 * as defined on EmbedStatement).  That function only
+		 * considers autoincrement columns.  So if the column
+		 * specified by the user is not autoincrement, we return
+		 * false.
+		 */
+		return ((cd != null) && cd.isAutoincrement());
+	}
+	
+    protected void firstExecuteSpecialHandlingAutoGen(boolean firstExecute,
+        RowChanger rowChanger, UUID targetUUID) 
+            throws StandardException {
+    if (firstExecute && activation.getAutoGeneratedKeysResultsetMode()) {
+        ResultDescription rd;
+        Properties properties = new Properties();
+        autoGeneratedKeysColumnIndexes =
+          	activation.getAutoGeneratedKeysColumnIndexes();
+
+        // Get the properties on the old heap
+        rowChanger.getHeapConglomerateController().getInternalTablePropertySet(properties);
+
+        if (autoGeneratedKeysColumnIndexes != null) {
+            // Use user-provided column positions array.
+            autoGeneratedKeysColumnIndexes =
+              	uniqueColumnPositionArray(autoGeneratedKeysColumnIndexes, targetUUID);
+        } else {
+            // Prepare array of auto-generated keys for the table since
+            // user didn't provide any.
+            autoGeneratedKeysColumnIndexes =
+                generatedColumnPositionsArray(targetUUID);
+        }
+
+        rd = lcc.getLanguageFactory().getResultDescription(
+            resultDescription, autoGeneratedKeysColumnIndexes);
+        autoGeneratedKeysRowsHolder =
+            new TemporaryRowHolderImpl(activation, properties, rd);
+        }
+    }
+
+    /**
+     * If user didn't provide columns list for auto-generated columns, then only include
+     * columns with auto-generated values in the resultset. Those columns would be ones
+     * with default value defined.
+     */
+    private int[] generatedColumnPositionsArray(UUID targetUUID)
+        throws StandardException
+    {
+        TableDescriptor tabDesb = lcc.getDataDictionary().getTableDescriptor(targetUUID);
+	    ColumnDescriptor cd;
+        int size = tabDesb.getMaxColumnID();
+
+        int[] generatedColumnPositionsArray = new int[size];
+        Arrays.fill(generatedColumnPositionsArray, -1);
+        int generatedColumnNumbers = 0;
+
+        for (int i=0; i<size; i++) {
+            cd = tabDesb.getColumnDescriptor(i+1);
+            if (cd.isAutoincrement()) { //if the column has auto-increment value
+                generatedColumnNumbers++;
+                generatedColumnPositionsArray[i] = i+1;
+            } else if (cd.getDefaultValue() != null || cd.getDefaultInfo() != null) {//default value
+                generatedColumnNumbers++;
+                generatedColumnPositionsArray[i] = i+1;
+            }
+        }
+        int[] returnGeneratedColumnPositionsArray = new int[generatedColumnNumbers];
+
+        for (int i=0, j=0; i<size; i++) {
+            if (generatedColumnPositionsArray[i] != -1)
+                returnGeneratedColumnPositionsArray[j++] = generatedColumnPositionsArray[i];
+        }
+
+        return returnGeneratedColumnPositionsArray;
+        }
+
+    /**
+     * Remove duplicate columns from the array. Then use this array to generate a sub-set
+     * of insert resultset to be returned for JDBC3.0 getGeneratedKeys() call.
+     */
+    private int[] uniqueColumnPositionArray(int[] columnIndexes, UUID targetUUID)
+        throws StandardException
+    {
+        int size = columnIndexes.length;
+        TableDescriptor tabDesc = lcc.getDataDictionary().getTableDescriptor(targetUUID);
+
+        //create an array of integer (the array size = number of columns in table)
+        // valid column positions are 1...getMaxColumnID()
+        int[] uniqueColumnIndexes = new int[tabDesc.getMaxColumnID()];
+
+        int uniqueColumnNumbers = 0;
+
+        //At the end of following loop, the uniqueColumnIndexes elements will not be 0 for user
+        //selected auto-generated columns.
+        for (int i=0; i<size; i++) {
+            if (uniqueColumnIndexes[columnIndexes[i] - 1] == 0) {
+                uniqueColumnNumbers++;
+                uniqueColumnIndexes[columnIndexes[i] - 1] = columnIndexes[i];
+            }
+        }
+        int[] returnUniqueColumnIndexes = new int[uniqueColumnNumbers];
+
+        //return just the column positions which are not marked 0 in the uniqueColumnIndexes array
+        for (int i=0, j=0; i<uniqueColumnIndexes.length; i++) {
+            if (uniqueColumnIndexes[i] != 0)
+                returnUniqueColumnIndexes[j++] = uniqueColumnIndexes[i];
+        }
+
+        return returnUniqueColumnIndexes;
+    }
+
+    /**
+     * Take the input row and return a new compact ExecRow
+     * using the column positions provided in columnIndexes.
+     * Copies references, no cloning.
+     */
+    protected ExecRow getCompactRow
+    (
+    	ExecRow 					inputRow, 
+        int[] 						columnIndexes
+    )
+    throws StandardException
+    {
+        ExecRow outRow;
+        int numInputCols = inputRow.nColumns();
+
+        if (columnIndexes == null)
+        {
+            outRow = new ValueRow(numInputCols);
+            Object[] src = inputRow.getRowArray();
+            Object[] dst = outRow.getRowArray();
+            System.arraycopy(src, 0, dst, 0, src.length);
+            return outRow;
+        }
+
+        int numOutputCols = columnIndexes.length;
+
+        outRow = new ValueRow(numOutputCols);
+        for (int i = 0; i < numOutputCols; i++)
+        {
+            outRow.setColumn(i+1,
+            	inputRow.getColumn(columnIndexes[i]));
+        }
+
+        return outRow;
+    }
+
+    @Override
+    public ResultSet getAutoGeneratedKeysResultset()
+    {
+        return autoGeneratedKeysResultSet;
     }
 }
\ No newline at end of file

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=1628596&r1=1628595&r2=1628596&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 Wed Oct  1 04:28:01 2014
@@ -24,7 +24,6 @@ package org.apache.derby.impl.sql.execut
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Properties;
@@ -41,7 +40,6 @@ import org.apache.derby.iapi.services.lo
 import org.apache.derby.iapi.sql.Activation;
 import org.apache.derby.iapi.sql.LanguageProperties;
 import org.apache.derby.iapi.sql.ResultColumnDescriptor;
-import org.apache.derby.iapi.sql.ResultDescription;
 import org.apache.derby.iapi.sql.ResultSet;
 import org.apache.derby.iapi.sql.StatementUtil;
 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
@@ -50,10 +48,8 @@ import org.apache.derby.iapi.sql.diction
 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
-import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
-import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;
 import org.apache.derby.iapi.sql.dictionary.StatisticsDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
@@ -91,9 +87,9 @@ import org.apache.derby.shared.common.sa
  */
 class InsertResultSet extends DMLWriteGeneratedColumnsResultSet implements TargetResultSet
 {
-	// RESOLVE. Embarassingly large public state. If we could move the Replication
-	// code into the same package, then these variables could be protected.
-
+	// RESOLVE. Embarrassingly large public state. If we could move the 
+	// Replication code into the same package, then these variables 
+	// could be protected. 
 	// passed in at construction time
                                                  
 	NoPutResultSet			savedSource;
@@ -102,11 +98,6 @@ class InsertResultSet extends DMLWriteGe
 	private GeneratedMethod			checkGM;
 	private long					heapConglom;
 
-	//following is for jdbc3.0 feature auto generated keys resultset
-	private  ResultSet			autoGeneratedKeysResultSet;
-	private	TemporaryRowHolderImpl	autoGeneratedKeysRowsHolder;
-    private int[]                   autoGeneratedKeysColumnIndexes;
-
 	// divined at run time
 
 	private RowChanger 				rowChanger;
@@ -165,15 +156,6 @@ class InsertResultSet extends DMLWriteGe
     private BulkInsertCounter[]                 bulkInsertCounters;
     private BackingStoreHashtable   deferredChecks; // cached ref.
     private List<UUID>              violatingCheckConstraints;
-	/**
-	 * If set to true, implies that this (rep)insertresultset has generated
-	 * autoincrement values. During refresh for example, the autoincrement
-	 * values are not generated but sent from the source to target or 
-	 * vice-versa.
-	 */
-	protected boolean 				autoincrementGenerated;
-	private long					identityVal;  //support of IDENTITY_LOCAL_VAL function
-	private boolean					setIdentity;
 	
 	// TargetResultSet interface
 
@@ -410,22 +392,8 @@ class InsertResultSet extends DMLWriteGe
 
 		dd = lcc.getDataDictionary();
 
-		/*
-		** verify the auto-generated key columns list(ie there are no invalid column
-		** names or positions). This is done at at execution time because for a precompiled
-		** insert statement, user can specify different column selections for
-		** auto-generated keys.
-		*/
-		if(activation.getAutoGeneratedKeysResultsetMode())
-		{
-            int[]   agi = activation.getAutoGeneratedKeysColumnIndexes();
-            if ( agi != null ) { verifyAutoGeneratedColumnsIndexes( agi ); }
-            else
-            {
-                String[]    agc = activation.getAutoGeneratedKeysColumnNames();
-                if ( agc != null ) { verifyAutoGeneratedColumnsNames( agc ); }
-            }
-		}
+		verifyAutoGeneratedRScolumnsList(constants.targetUUID);
+
 		rowCount = 0L;
 
 		if (numOpens++ == 0)
@@ -491,13 +459,6 @@ class InsertResultSet extends DMLWriteGe
 			savedSource = sourceResultSet;
 		}
 
-		/* autoGeneratedResultset for JDBC3. Nulled after statement execution is over
-		(ie after it is saved off in LocalSatement object) */
-		if (activation.getAutoGeneratedKeysResultsetMode())
-			autoGeneratedKeysResultSet = autoGeneratedKeysRowsHolder.getResultSet();
-		else
-			autoGeneratedKeysResultSet = null;
-
 		cleanUp();
 
 		saveAIcacheInformation(constants.getSchemaName(), 
@@ -517,29 +478,6 @@ class InsertResultSet extends DMLWriteGe
 	}
 
 	/**
-	 * Verify that the auto-generated columns list (by position) has valid
-	 * column positions for the table.
-	 */
-	private void verifyAutoGeneratedColumnsIndexes(int[] columnIndexes)
-		throws StandardException
-	{
-		int size = columnIndexes.length;
-        TableDescriptor tabDesc = dd.getTableDescriptor(constants.targetUUID);
-
-		// all 1-based column ids.
-		for (int i = 0; i < size; i++)
-		{
-            ColumnDescriptor cd = tabDesc.getColumnDescriptor(columnIndexes[i]);
-			if (!verifyAutoGenColumn(cd))
-			{
-				throw StandardException.newException(
-					SQLState.LANG_INVALID_AUTOGEN_COLUMN_POSITION,
-                    new Integer(columnIndexes[i]), tabDesc.getName());
-			}
-		}
-	}
-
-	/**
 	 * If user didn't provide columns list for auto-generated columns, then only include
 	 * columns with auto-generated values in the resultset. Those columns would be ones
 	 * with default value defined.
@@ -576,106 +514,6 @@ class InsertResultSet extends DMLWriteGe
 	}
 
 	/**
-	 * Remove duplicate columns from the array. Then use this array to generate a sub-set
-	 * of insert resultset to be returned for JDBC3.0 getGeneratedKeys() call.
-	 */
-	private int[] uniqueColumnPositionArray(int[] columnIndexes)
-		throws StandardException
-	{
-		int size = columnIndexes.length;
-        TableDescriptor tabDesc = dd.getTableDescriptor(constants.targetUUID);
-
-		//create an array of integer (the array size = number of columns in table)
-		// valid column positions are 1...getMaxColumnID()
-        int[] uniqueColumnIndexes = new int[tabDesc.getMaxColumnID()];
-
-		int uniqueColumnNumbers = 0;
-
-
-		//At the end of following loop, the uniqueColumnIndexes elements will not be 0 for user
-		//selected auto-generated columns.
-		for (int i=0; i<size; i++) {
-			if (uniqueColumnIndexes[columnIndexes[i] - 1] == 0) {
-				uniqueColumnNumbers++;
-				uniqueColumnIndexes[columnIndexes[i] - 1] = columnIndexes[i];
-			}
-		}
-		int[] returnUniqueColumnIndexes = new int[uniqueColumnNumbers];
-
-		//return just the column positions which are not marked 0 in the uniqueColumnIndexes array
-		for (int i=0, j=0; i<uniqueColumnIndexes.length; i++) {
-			if (uniqueColumnIndexes[i] != 0)
-				returnUniqueColumnIndexes[j++] = uniqueColumnIndexes[i];
-		}
-
-		return returnUniqueColumnIndexes;
-	}
-
-	/**
-	 * Verify that the auto-generated columns list (by name) has valid
-	 * column names for the table. If all the column names are valid,
-	 * convert column names array to corresponding column positions array
-	 * Save that column positions array in activation. We do this to simplify the
-	 * rest of the logic(it only has to deal with column positions here after).
-	 *
-	 * @exception StandardException		Thrown on error if invalid column
-   * name in the list.
-	 */
-	private void verifyAutoGeneratedColumnsNames(String[] columnNames)
-		throws StandardException
-	{
-		int size = columnNames.length;
-		int columnPositions[] = new int[size];
-
-        TableDescriptor tabDesc = dd.getTableDescriptor(constants.targetUUID);
-		ColumnDescriptor cd;
-
-		for (int i = 0; i < size; i++)
-		{
-			if (columnNames[i] == null)
-			{
-				throw StandardException.newException(
-					SQLState.LANG_INVALID_AUTOGEN_COLUMN_NAME,
-                    columnNames[i], tabDesc.getName());
-			}
-
-            cd = tabDesc.getColumnDescriptor(columnNames[i]);
-			if (!verifyAutoGenColumn(cd))
-			{
-				throw StandardException.newException(
-					SQLState.LANG_INVALID_AUTOGEN_COLUMN_NAME,
-                    columnNames[i], tabDesc.getName());
-			}
-
-			columnPositions[i] = cd.getPosition();
-		}
-		activation.setAutoGeneratedKeysResultsetInfo(columnPositions, null);
-	}
-
-	/**
-	 * Check that the received ColumnDescriptor corresponds to a column
-	 * for which it is possible to fetch auto-generated keys.
-	 */
-	private boolean verifyAutoGenColumn(ColumnDescriptor cd)
-	{
-		/* Derby currently gets generated keys by calling the
-		 * IDENTITY_VAL_LOCAL() function (see "getGeneratedKeys()"
-		 * as defined on EmbedStatement).  That function only
-		 * considers autoincrement columns.  So if the column
-		 * specified by the user is not autoincrement, we return
-		 * false.
-		 */
-		return ((cd != null) && cd.isAutoincrement());
-	}
-
-    @Override
-	public ResultSet getAutoGeneratedKeysResultset()
-	{
-		return autoGeneratedKeysResultSet;
-	}
-
-
-	/**
 	 * getSetAutoincrementValue will get the autoincrement value of the 
 	 * columnPosition specified for the target table. If increment is 
 	 * non-zero we will also update the autoincrement value. 
@@ -984,33 +822,7 @@ class InsertResultSet extends DMLWriteGe
 			rowChanger.setRowHolder(rowHolder);
 		}
 
-		if (firstExecute && activation.getAutoGeneratedKeysResultsetMode())
-		{
-			ResultDescription rd;
-			Properties properties = new Properties();
-            autoGeneratedKeysColumnIndexes =
-                    activation.getAutoGeneratedKeysColumnIndexes();
-
-			// Get the properties on the old heap
-			rowChanger.getHeapConglomerateController().getInternalTablePropertySet(properties);
-
-            if (autoGeneratedKeysColumnIndexes != null) {
-                // Use user-provided column positions array.
-                autoGeneratedKeysColumnIndexes =
-                    uniqueColumnPositionArray(autoGeneratedKeysColumnIndexes);
-            } else {
-                // Prepare array of auto-generated keys for the table since
-                // user didn't provide any.
-                autoGeneratedKeysColumnIndexes =
-                        generatedColumnPositionsArray();
-            }
-
-            rd = lcc.getLanguageFactory().getResultDescription(
-                    resultDescription, autoGeneratedKeysColumnIndexes);
-			autoGeneratedKeysRowsHolder =
-				new TemporaryRowHolderImpl(activation, properties, rd);
-		}
-
+		firstExecuteSpecialHandlingAutoGen(firstExecute, rowChanger, constants.targetUUID);
 
 		while ( row != null )
 		{
@@ -1319,43 +1131,6 @@ class InsertResultSet extends DMLWriteGe
         return normalizeRow( sourceRS, row );
 	}
 
-
-	/**
-	 * Take the input row and return a new compact ExecRow
-	 * using the column positions provided in columnIndexes.
-	 * Copies references, no cloning.
-	 */
-	private ExecRow getCompactRow
-	(
-		ExecRow 					inputRow, 
-		int[] 						columnIndexes
-	)
-		throws StandardException
-	{
-		ExecRow outRow;
-		int numInputCols = inputRow.nColumns();
-
-		if (columnIndexes == null)
-		{
-			outRow = new ValueRow(numInputCols);
-			Object[] src = inputRow.getRowArray();
-			Object[] dst = outRow.getRowArray();
-			System.arraycopy(src, 0, dst, 0, src.length);
-			return outRow;
-		}
-
-		int numOutputCols = columnIndexes.length;
-
-		outRow = new ValueRow(numOutputCols);
-		for (int i = 0; i < numOutputCols; i++)
-		{
-			outRow.setColumn(i+1,
-				inputRow.getColumn(columnIndexes[i]));
-		}
-
-		return outRow;
-	}
-
 	// Do the work for a bulk insert
     private void bulkInsertCore(LanguageConnectionContext lcc,
                                 ExecRow fullTemplate,

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=1628596&r1=1628595&r2=1628596&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 Wed Oct  1 04:28:01 2014
@@ -22,6 +22,7 @@
 package org.apache.derby.impl.sql.execute;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Properties;
 import java.util.Vector;
@@ -53,7 +54,9 @@ import org.apache.derby.iapi.types.SQLBo
 import org.apache.derby.iapi.types.SQLRef;
 import org.apache.derby.impl.sql.execute.DeferredConstraintsMemory.CheckInfo;
 import org.apache.derby.shared.common.sanity.SanityManager;
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 /**
  * Update the rows from the specified
  * base table. This will cause constraints to be checked
@@ -265,6 +268,7 @@ class UpdateResultSet extends DMLWriteGe
 	{
 
 		setup();
+		autoincrementGenerated = false;
 		collectAffectedRows();
 
 		/*
@@ -340,6 +344,7 @@ class UpdateResultSet extends DMLWriteGe
 			rowChanger.setIndexNames(constants.indexNames);
 		}
 
+		verifyAutoGeneratedRScolumnsList(constants.targetUUID);
 
 		/* Open the RowChanger before the source ResultSet so that
 		 * the store will see the RowChanger's lock as a covering lock
@@ -409,6 +414,7 @@ class UpdateResultSet extends DMLWriteGe
 			rowChanger.setRowHolder(insertedRowHolder);
 		}
 
+		firstExecuteSpecialHandlingAutoGen(firstOpen, rowChanger, constants.targetUUID);
 	}	
 
 	/* Following 2 methods are for checking and make sure we don't have one un-objectified stream
@@ -490,6 +496,44 @@ class UpdateResultSet extends DMLWriteGe
 
         while ( row != null )
         {
+            // Collect auto-generated keys if requested.
+            // DERBY-5823: No need to collect them if there are no
+            // auto-generated key columns.
+            if (activation.getAutoGeneratedKeysResultsetMode() &&
+                    autoGeneratedKeysColumnIndexes.length > 0) {
+                autoGeneratedKeysRowsHolder.insert(
+                        getCompactRow(row, autoGeneratedKeysColumnIndexes));
+                /*
+                 * find the value of the identity column. This could either
+                 * have been generated by Derby or supplied by user(user's
+                 * can supply value for "generated by default as identity").
+                 * In both cases, the value can be found in the row. Save
+                 * that value in identityVal. This locally saved value
+                 * is made available to JDBC Statement.RETURN_GENERATED_KEYS 
+                 * or IDENTITY_LOCAL_VAL function only if the UPDATE statement
+                 * has updated only one row. This implementation is as per the
+                 * JDBC spec
+                 */
+                TableDescriptor td = lcc.getDataDictionary().getTableDescriptor(constants.targetUUID);
+            		
+                int maxColumns = td.getMaxColumnID();
+                int col;
+                    
+                for(col=1;col<=maxColumns;col++)
+                {
+                    ColumnDescriptor cd = td.getColumnDescriptor(col);
+                    if(cd.isAutoincrement())
+                    {
+                        break;
+                    }
+                }
+                    
+                if(col <= maxColumns)
+                {
+                    DataValueDescriptor dvd = row.cloneColumn(col);
+                    identityVal = dvd.getLong();
+                }
+            }
             evaluateGenerationClauses( generationClauses, activation, sourceResultSet, row, true );
 
 			/* By convention, the last column in the result set for an
@@ -650,6 +694,9 @@ class UpdateResultSet extends DMLWriteGe
 				row = getNextRowCore(sourceResultSet);
 			}
 		}
+        
+        if(rowCount==1 && constants.hasAutoincrement()) 
+			lcc.setIdentityValue(identityVal);
 
 		return rowsFound;
 	}
@@ -1220,11 +1267,15 @@ class UpdateResultSet extends DMLWriteGe
     	getSetAutoincrementValue(int columnPosition, long increment)
     	throws StandardException
     {
+        autoincrementGenerated = true;
         int index = columnPosition - 1;	// all our indices are 0 based.
         NumberDataValue newValue;
         newValue = activation.getCurrentValueAndAdvance
                 ( identitySequenceUUIDString, aiCache[ index ].getTypeFormatId() );
         aiCache[index] = newValue;
+        //Save the generated auto increment value for use by JDBC api and
+        // IDENTITY_LOCAL_VAL function
+		identityVal = newValue.getLong();
         return (NumberDataValue) aiCache[index];
     }
 	

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java?rev=1628596&r1=1628595&r2=1628596&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java Wed Oct  1 04:28:01 2014
@@ -215,6 +215,45 @@ public class AutoGenJDBC30Test extends B
     }
 
     /**
+     * Requests generated keys for a multi-row update statement after a
+     * one-row update into a table with an auto-generated key.
+     * Expected result: ResultSet has one row with a non-NULL key for the
+     * one-row update.
+     * @throws SQLException 
+     */
+    public void testUpdateManyRowsAfterOneRowKey() throws SQLException
+    {
+        // Do a one-row insert into a table with an auto-generated key.
+        Statement s = createStatement();
+        s.execute("insert into t11_AutoGen(c11) values (99)", Statement.RETURN_GENERATED_KEYS);
+        int expected=1;
+        int keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", expected, keyval);
+        
+        // Do a one-row update of a table with an auto-generated key.
+        s.execute("update t11_AutoGen set c12=default where c11=99", Statement.RETURN_GENERATED_KEYS);
+        expected=2;
+        keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", expected, keyval);
+    	
+        String sql="insert into t11_AutoGen(c11) values (99), (98), (97)";
+        s.execute(sql, Statement.RETURN_GENERATED_KEYS);
+        keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", expected, keyval);
+
+        // Do a one-row update of a table with an auto-generated key.
+        s.execute("update t11_AutoGen set c12=default where c11=97", Statement.RETURN_GENERATED_KEYS);
+        expected=6;
+        keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", expected, keyval);
+        
+        // Do a multi-row update of a table with an auto-generated key.
+        s.execute("update t11_AutoGen set c12=default where c11=99", Statement.RETURN_GENERATED_KEYS);
+        keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", expected, keyval);
+    }
+
+    /**
      * Requests generated keys for a multi-row insert statement after a
      * one-row insert into a table with an auto-generated key.
      * Old harness Test 7.
@@ -511,31 +550,32 @@ public class AutoGenJDBC30Test extends B
      * Expected result: a NULL ResultSet.
      * @throws SQLException 
      */
-    public void testUpdate() throws SQLException
+    public void testUpdateOneRowKey() throws SQLException
     {
         Statement s = createStatement();
         s.execute("insert into t11_AutoGen(c11) values(999)");
 
-        String sqlStmt="update t11_AutoGen set c11=1";
+        String sqlStmt="update t11_AutoGen set c12=default where c11=999";
         s.execute(sqlStmt, Statement.RETURN_GENERATED_KEYS);
-        assertNull("Expected NULL ResultSet after s.execute()", 
-            s.getGeneratedKeys());
+        int keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.execute()", 2, keyval);
 
         s.executeUpdate(sqlStmt, Statement.RETURN_GENERATED_KEYS);
-        assertNull("Expected NULL ResultSet after s.executeUpdate()", 
-            s.getGeneratedKeys());
+        keyval = getKeyValue (s.getGeneratedKeys());
+        assertEquals("Key value after s.executeUpdate()", 3, keyval);
 
         s.close();
 
         PreparedStatement ps = prepareStatement(
             sqlStmt, Statement.RETURN_GENERATED_KEYS);
         ps.execute();
-        assertNull("Expected NULL ResultSet after ps.execute()", 
-            ps.getGeneratedKeys());
+        keyval = getKeyValue (ps.getGeneratedKeys());
+        assertEquals("Key value after ps.execute()", 4, keyval);
 
+        ps = prepareStatement(sqlStmt, Statement.RETURN_GENERATED_KEYS);
         ps.executeUpdate();
-        assertNull("Expected NULL ResultSet after ps.executeUpdate()", 
-            ps.getGeneratedKeys());
+        keyval = getKeyValue (ps.getGeneratedKeys());
+        assertEquals("Key value after ps.executeUpdate()", 5, keyval);
 
         ps.close();
     }
@@ -1480,7 +1520,7 @@ public class AutoGenJDBC30Test extends B
      * @exception SQLException if a database error occurs
      */
     public int getKeyValue (ResultSet r) throws SQLException
-    {
+    {if(r==null) System.out.println("it is null");
         JDBC.assertGeneratedKeyResultSet("AutoGenJDBC30Test.getKeyValue", r);
         
         int i = 0;