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 dj...@apache.org on 2008/01/11 20:45:19 UTC
svn commit: r611274 - in
/db/derby/code/branches/10.3/java/engine/org/apache/derby: iapi/jdbc/
impl/jdbc/ impl/sql/execute/
Author: djd
Date: Fri Jan 11 11:45:13 2008
New Revision: 611274
URL: http://svn.apache.org/viewvc?rev=611274&view=rev
Log:
DERBY-1585 For a CALL statement that generates dynamic result sets add code so t hat the result sets are closed if they are inaccessible following the SQL Standard.
Merge of 602991 and 603030 from trunk.
Slight hand-merge for EmbedStatement with 602991 due to a moved call to ResultSet.finish() being changed to a ResultSet.close().
Modified:
db/derby/code/branches/10.3/java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java
db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java
db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java
db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java?rev=611274&r1=611273&r2=611274&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java Fri Jan 11 11:45:13 2008
@@ -21,7 +21,6 @@
package org.apache.derby.iapi.jdbc;
-import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.sql.ResultSet;
import java.sql.Connection;
@@ -57,4 +56,27 @@
(
ResultSet executionResultSet
) throws java.sql.SQLException;
+
+ /**
+ * Process the resultSet as a dynamic result for closure.
+ * The result set will have been created in a Java procedure.
+ * If the ResultSet is a valid dynamic ResultSet for
+ * this connection, then it is set up as a dynamic result
+ * which includes:
+ * <UL>
+ * <LI> breaking its link with the JDBC connection
+ * that created it, since there is a good chance that connection
+ * was closed explicitly by the Java procedure.
+ * <LI> marking its activation as single use to ensure the
+ * close of the ResultSet will close the activation.
+ * </UL>
+ * <P>
+ * If the result set a valid dynamic result then false will
+ * be returned and no action made against it.
+ *
+ * @param resultSet ResultSet to process.
+ * @return True if this ResultSet was created by this connection
+ * and the result set is open. False otherwise.
+ */
+ public boolean processInaccessibleDynamicResult(java.sql.ResultSet resultSet);
}
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java?rev=611274&r1=611273&r2=611274&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java Fri Jan 11 11:45:13 2008
@@ -133,4 +133,25 @@
false, (EmbedStatement) null, true);
return rs;
}
+
+ /**
+ * Process a ResultSet from a procedure to be a dynamic result,
+ * but one that will be closed due to it being inaccessible. We cannot simply
+ * close the ResultSet as it the nested connection that created
+ * it might be closed, leading to its close method being a no-op.
+ * This performs all the conversion (linking the ResultSet
+ * to a valid connection) required but does not close
+ * the ResultSet.
+ *
+ * @see EmbedStatement#processDynamicResult(EmbedConnection, java.sql.ResultSet, EmbedStatement)
+ */
+ public boolean processInaccessibleDynamicResult(java.sql.ResultSet resultSet) {
+ EmbedConnection conn = (EmbedConnection) connRef.get();
+ if (conn == null)
+ return false;
+
+ // Pass in null as the Statement to own the ResultSet since
+ // we don't have one since the dynamic result will be inaccessible.
+ return EmbedStatement.processDynamicResult(conn, resultSet, null) != null;
+ }
}
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=611274&r1=611273&r2=611274&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Fri Jan 11 11:45:13 2008
@@ -590,7 +590,7 @@
// just give up and return
return;
}
-
+
try {
try {
theResults.close();
@@ -4411,15 +4411,25 @@
}
/**
- A dynamic result set was created in a procedure by a nested connection.
+ A dynamic result was created in a procedure by a nested connection.
Once the procedure returns, there is a good chance that connection is closed,
so we re-attach the result set to the connection of the statement the called
the procedure, which will be still open.
+ <BR>
+ In the case where the dynamic result will not be accessible
+ then owningStmt will be null, the ResultSet will be linked to
+ the root connection to allow its close method to work. It
+ will remain attached to its original statement.
*/
void setDynamicResultSet(EmbedStatement owningStmt) {
- this.owningStmt = owningStmt;
- this.localConn = owningStmt.getEmbedConnection();
+
+ if (owningStmt != null) {
+ this.owningStmt = owningStmt;
+ this.localConn = owningStmt.getEmbedConnection();
+ }
+ else
+ this.localConn = this.localConn.rootConnection;
// The activation that created these results now becomes
// a single use activation so it will be closed when this
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java?rev=611274&r1=611273&r2=611274&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java Fri Jan 11 11:45:13 2008
@@ -1242,7 +1242,6 @@
updateCount = resultsToWrap.modifiedRowCount();
- resultsToWrap.close(); // Don't need the result set any more
results = null; // note that we have none.
int dynamicResultCount = 0;
@@ -1251,6 +1250,8 @@
processDynamicResults(a.getDynamicResults(),
a.getMaxDynamicResults());
}
+
+ resultsToWrap.close(); // Don't need the result set any more
// executeQuery() is not allowed if the statement
// doesn't return exactly one ResultSet.
@@ -1522,26 +1523,25 @@
java.sql.ResultSet[] param = holder[i];
- if (param[0] == null)
- continue;
-
java.sql.ResultSet rs = param[0];
- param[0] = null;
-
- // ignore non-Derby result sets or results sets from another connection
- if (!(rs instanceof EmbedResultSet))
- continue;
-
- EmbedResultSet lrs = (EmbedResultSet) rs;
-
- if (lrs.getEmbedConnection().rootConnection != getEmbedConnection().rootConnection)
- continue;
- // ignore closed result sets.
- if (lrs.isClosed)
- continue;
+ // Clear the JDBC dynamic ResultSet from the language
+ // ResultSet for the CALL statement. This stops the
+ // CALL statement closing the ResultSet when its language
+ // ResultSet is closed, which will happen just after the
+ // call to the processDynamicResults() method.
+ param[0] = null;
+
+ // ignore non-Derby result sets or results sets from another connection
+ // and closed result sets.
+ EmbedResultSet lrs = EmbedStatement.processDynamicResult(
+ getEmbedConnection(), rs, this);
+
+ if (lrs == null)
+ {
+ continue;
+ }
- lrs.setDynamicResultSet(this);
sorted[actualCount++] = lrs;
}
@@ -1577,6 +1577,49 @@
return actualCount;
}
+
+ /**
+ * Process a ResultSet created in a Java procedure as a dynamic result.
+ * To be a valid dynamic result the ResultSet must be:
+ * <UL>
+ * <LI> From a Derby system
+ * <LI> From a nested connection of connection passed in
+ * or from the connection itself.
+ * <LI> Open
+ * </UL>
+ * Any invalid ResultSet is ignored.
+ *
+ *
+ * @param conn Connection ResultSet needs to belong to
+ * @param resultSet ResultSet to be tested
+ * @param callStatement Statement that executed the CALL, null if
+ * @return The result set cast down to EmbedResultSet, null if not a valid
+ * dynamic result.
+ */
+ static EmbedResultSet processDynamicResult(EmbedConnection conn,
+ java.sql.ResultSet resultSet,
+ EmbedStatement callStatement)
+ {
+ if (resultSet == null)
+ return null;
+
+ // ignore non-Derby result sets or results sets from another connection
+ if (!(resultSet instanceof EmbedResultSet))
+ return null;
+
+ EmbedResultSet lrs = (EmbedResultSet) resultSet;
+
+ if (lrs.getEmbedConnection().rootConnection != conn.rootConnection)
+ return null;
+
+ // ignore closed result sets.
+ if (lrs.isClosed)
+ return null;
+
+ lrs.setDynamicResultSet(callStatement);
+
+ return lrs;
+ }
/**
Callback on the statement when one of its result sets is closed.
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java?rev=611274&r1=611273&r2=611274&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java Fri Jan 11 11:45:13 2008
@@ -21,14 +21,30 @@
package org.apache.derby.impl.sql.execute;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.jdbc.ConnectionContext;
import org.apache.derby.iapi.services.loader.GeneratedMethod;
import org.apache.derby.iapi.sql.Activation;
-import org.apache.derby.iapi.sql.ResultSet;
/**
- * Call the specified expression, ignoring the return, if any.
- *
+ * Call a Java procedure. This calls a generated method in the
+ * activation which sets up the parameters and then calls the
+ * Java method that the procedure resolved to.
+ * <P>
+ * Valid dynamic results returned by the procedure will be closed
+ * as inaccessible when this is closed (e.g. a CALL within a trigger).
+ *
+ * <BR>
+ * Any code that requires the dynamic results to be accessible
+ * (such as the JDBC Statement object executing the CALL) must
+ * obtain the dynamic results from Activation.getDynamicResults()
+ * and remove each ResultSet it will be handling by clearing the
+ * reference in the object returned.
+ *
+ * @see Activation#getDynamicResults()
*/
class CallStatementResultSet extends NoRowsResultSetImpl
{
@@ -49,13 +65,105 @@
}
/**
+ * Just invoke the method.
@exception StandardException Standard Derby error policy
*/
public void open() throws StandardException
{
setup();
methodCall.invoke(activation);
- close();
+ }
+
+ /**
+ * Need to explicitly close any dynamic result sets.
+ * <BR>
+ * If the dynamic results are not accessible then they
+ * need to be destroyed (ie. closed) according the the
+ * SQL Standard.
+ * <BR>
+ * An execution of a CALL statement through JDBC makes the
+ * dynamic results accessible, in this case the closing
+ * of the dynamic result sets is handled by the JDBC
+ * statement object (EmbedStatement) that executed the CALL.
+ * We cannot unify the closing of dynamic result sets to
+ * this close, as in accessible case it is called during
+ * the Statement.execute call, thus it would close the
+ * dynamic results before the application has a change
+ * to use them.
+ *
+ * <BR>
+ * With an execution of a CALL
+ * statement as a trigger's action statement the dynamic
+ * result sets are not accessible. In this case this close
+ * method is called after the execution of the trigger's
+ * action statement.
+ * <BR>
+ * <BR>
+ * Section 4.27.5 of the TECHNICAL CORRIGENDUM 1 to the SQL 2003
+ * Standard details what happens to dynamic result sets in detail,
+ * the SQL 2003 foundation document is missing these details.
+ */
+ public void close() throws StandardException
+ {
+ super.close();
+
+
+
+ ResultSet[][] dynamicResults = getActivation().getDynamicResults();
+ if (dynamicResults != null)
+ {
+ // Need to ensure all the result sets opened by this
+ // CALL statement for this connection are closed.
+ // If any close() results in an exception we need to keep going,
+ // save any exceptions and then throw them once we are complete.
+ StandardException errorOnClose = null;
+
+ ConnectionContext jdbcContext = null;
+
+ for (int i = 0; i < dynamicResults.length; i++)
+ {
+ ResultSet[] param = dynamicResults[i];
+ ResultSet drs = param[0];
+
+ // Can be null if the procedure never set this parameter
+ // or if the dynamic results were processed by JDBC (EmbedStatement).
+ if (drs == null)
+ continue;
+
+ if (jdbcContext == null)
+ jdbcContext = (ConnectionContext)
+ lcc.getContextManager().getContext(ConnectionContext.CONTEXT_ID);
+
+ try {
+
+ // Is this a valid, open dynamic result set for this connection?
+ if (!jdbcContext.processInaccessibleDynamicResult(drs))
+ {
+ // If not just ignore it, not Derby's problem.
+ continue;
+ }
+
+ drs.close();
+
+ } catch (SQLException e) {
+
+ // Just report the first error
+ if (errorOnClose == null)
+ {
+ StandardException se = StandardException.plainWrapException(e);
+ errorOnClose = se;
+ }
+ }
+ finally {
+ // Remove any reference to the ResultSet to allow
+ // it and any associated resources to be garbage collected.
+ param[0] = null;
+ }
+ }
+
+ if (errorOnClose != null)
+ throw errorOnClose;
+ }
}
/**