db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r1537888 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/sql/execute/InsertResultSet.java testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java
Date Fri, 01 Nov 2013 10:31:02 GMT
Author: kahatlen
Date: Fri Nov  1 10:31:02 2013
New Revision: 1537888

URL: http://svn.apache.org/r1537888
Log:
DERBY-5823: Multi-row insert fails on table without generated keys with RETURN_GENERATED_KEYS

The fix contains two parts:

1. Don't collect generated keys if the statement does not actually
   generate key values. (This is the fix for the reported problem.)

2. Cache the array of generated key columns between executions. In the
   existing code, the array of key columns was created only on the
   first execution. Since it wasn't cached, it was null on all
   subsequent executions. When it is null, all columns are collected
   into the temporary row holder, which wastes space. Now, only the
   key columns are collected, also on re-execution.

The test case was contributed by Kristian Waagan.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/AutoGenJDBC30Test.java

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=1537888&r1=1537887&r2=1537888&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
Fri Nov  1 10:31:02 2013
@@ -21,6 +21,7 @@
 
 package org.apache.derby.impl.sql.execute;
 
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
@@ -96,6 +97,7 @@ class InsertResultSet extends DMLWriteRe
 	//following is for jdbc3.0 feature auto generated keys resultset
 	private  ResultSet			autoGeneratedKeysResultSet;
 	private	TemporaryRowHolderImpl	autoGeneratedKeysRowsHolder;
+    private int[]                   autoGeneratedKeysColumnIndexes;
 
 	// divined at run time
 
@@ -610,10 +612,8 @@ class InsertResultSet extends DMLWriteRe
 		int size = td.getMaxColumnID();
 
 		int[] generatedColumnPositionsArray = new int[size];
+        Arrays.fill(generatedColumnPositionsArray, -1);
 		int generatedColumnNumbers = 0;
-		for (int i=0; i<size; i++) {
-			generatedColumnPositionsArray[i] = -1;
-		}
 
 		for (int i=0; i<size; i++) {
 			cd = td.getColumnDescriptor(i+1);
@@ -991,23 +991,29 @@ class InsertResultSet extends DMLWriteRe
 			rowChanger.setRowHolder(rowHolder);
 		}
 
-		int[] columnIndexes = null;
 		if (firstExecute && activation.getAutoGeneratedKeysResultsetMode())
 		{
 			ResultDescription rd;
 			Properties properties = new Properties();
-			columnIndexes = activation.getAutoGeneratedKeysColumnIndexes();
+            autoGeneratedKeysColumnIndexes =
+                    activation.getAutoGeneratedKeysColumnIndexes();
 
 			// Get the properties on the old heap
 			rowChanger.getHeapConglomerateController().getInternalTablePropertySet(properties);
 
-			if ( columnIndexes != null) {//use user provided column positions array
-				columnIndexes = uniqueColumnPositionArray(columnIndexes);
-			} else { //prepare array of auto-generated keys for the table since user didn't provide
any
-				columnIndexes = generatedColumnPositionsArray();
-			}
+            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,columnIndexes);
+            rd = lcc.getLanguageFactory().getResultDescription(
+                    resultDescription, autoGeneratedKeysColumnIndexes);
 			autoGeneratedKeysRowsHolder =
 				new TemporaryRowHolderImpl(activation, properties, rd);
 		}
@@ -1015,8 +1021,14 @@ class InsertResultSet extends DMLWriteRe
 
 		while ( row != null )
 		{
-			if (activation.getAutoGeneratedKeysResultsetMode())
-				autoGeneratedKeysRowsHolder.insert(getCompactRow(row, columnIndexes));
+            // 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));
+            }
 
             // fill in columns that are computed from expressions on other columns
             evaluateGenerationClauses( generationClauses, activation, sourceResultSet, row,
false );

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=1537888&r1=1537887&r2=1537888&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
Fri Nov  1 10:31:02 2013
@@ -30,7 +30,6 @@ import java.sql.Statement;
 import java.sql.SQLException;
 
 import org.apache.derbyTesting.junit.BaseJDBCTestCase;
-import org.apache.derbyTesting.junit.BaseJDBCTestSetup;
 import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
 import org.apache.derbyTesting.junit.JDBC;
 import org.apache.derbyTesting.junit.TestConfiguration;
@@ -79,6 +78,9 @@ public class AutoGenJDBC30Test extends B
 
         { "t21_noAutoGen",
           "create table t21_noAutoGen (c21 int not null unique, c22 char(5))" },
+
+        { "t21_feed_table",
+          "create table t21_feed_table (c21 int not null unique, c22 char(5))"},
     };
 
     /**
@@ -300,6 +302,50 @@ public class AutoGenJDBC30Test extends B
     }
 
     /**
+     * <p>
+     * Regression test for DERBY-5823 where the temporary row holder code
+     * failed when switching from an in-memory to an on-disk representation.
+     * </p>
+     *
+     * <p>
+     * Note that ideally the transition should never have happened in the first
+     * place, so this test verifies that either the transition logic can deal
+     * with the degenerate case where the row template is zero-length, or the
+     * insert code is smart enough to understand that there are no
+     * auto-generated keys for the query.
+     * </p>
+     */
+    public void testDerby5823() throws SQLException {
+        setAutoCommit(false);
+        PreparedStatement ps = prepareStatement(
+                "insert into t21_feed_table values (?,?)");
+        ps.setString(2, "false");
+        // Just make sure we exceed the threshold for when the temporary row
+        // holder overflows to disk (implementation detail).
+        // When this test was written the threshold was five (5).
+        for (int i=0; i < 250; i++) {
+            ps.setInt(1, i);
+            ps.executeUpdate();
+        }
+        commit();
+        setAutoCommit(true);
+        final String insertSql =
+                "insert into t21_noAutoGen select * from t21_feed_table";
+        // No keys will be auto-generated by the insert query.
+        Statement s = createStatement();
+        s.execute(insertSql,
+                Statement.RETURN_GENERATED_KEYS
+                );
+        verifyNullKey("s.execute()", s.getGeneratedKeys());
+        // For good measure we also test with a prepared statement.
+        s.execute("delete from t21_noAutoGen");
+        // Again, no keys will be auto-generated by the insert query.
+        ps = prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS);
+        ps.executeUpdate();
+        verifyNullKey("ps.executeUpdate()", ps.getGeneratedKeys());
+    }
+
+    /**
      * Requests generated keys after doing a one-row insert into a table that 
      * has a generated column, but the insert is via a subquery with no where
      * clause.



Mime
View raw message