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;