Index: java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java
===================================================================
--- java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java (revision 601462)
+++ java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java (working copy)
@@ -21,10 +21,13 @@
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.
@@ -49,14 +52,104 @@
}
/**
+ * 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.
+ *
+ * If the dynamic results are not accessible then they
+ * need to be destroyed (ie. closed) according the the
+ * SQL Standard.
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.isMyOpenResultSet(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;
+ }
+ }
/**
* @see ResultSet#cleanUp
Index: java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java
===================================================================
--- java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java (revision 601462)
+++ java/engine/org/apache/derby/impl/jdbc/EmbedConnectionContext.java (working copy)
@@ -133,4 +133,12 @@
false, (EmbedStatement) null, true);
return rs;
}
+
+ public boolean isMyOpenResultSet(java.sql.ResultSet resultSet) {
+ EmbedConnection conn = (EmbedConnection) connRef.get();
+ if (conn == null)
+ return false;
+
+ return EmbedStatement.isOpenResultSetForConnection(conn, resultSet) != null;
+ }
}
Index: java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java
===================================================================
--- java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java (revision 601462)
+++ java/engine/org/apache/derby/impl/jdbc/EmbedStatement.java (working copy)
@@ -1272,8 +1272,6 @@
}
updateCount = resultsToWrap.modifiedRowCount();
-
- resultsToWrap.finish(); // Don't need the result set any more
results = null; // note that we have none.
int dynamicResultCount = 0;
@@ -1282,6 +1280,8 @@
processDynamicResults(a.getDynamicResults(),
a.getMaxDynamicResults());
}
+
+ resultsToWrap.finish(); // Don't need the result set any more
// executeQuery() is not allowed if the statement
// doesn't return exactly one ResultSet.
@@ -1553,25 +1553,25 @@
java.sql.ResultSet[] param = holder[i];
- if (param[0] == null)
- continue;
+ java.sql.ResultSet rs = param[0];
- java.sql.ResultSet rs = param[0];
+ // 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.isOpenResultSetForConnection(
+ getEmbedConnection(), rs);
+
+ if (lrs == null)
+ {
+ continue;
+ }
- // 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;
-
lrs.setDynamicResultSet(this);
sorted[actualCount++] = lrs;
}
@@ -1608,7 +1608,37 @@
return actualCount;
}
+
+ /**
+ * Does the ResultSet belong to the passed in connection
+ * and is open.
+ * @param conn Connection ResultSet needs to belong to
+ * @param resultSet ResultSet to be tested
+ * @return The result set cast down to EmbedResultSet, null if not valid
+ * for this connection.
+ */
+ static EmbedResultSet isOpenResultSetForConnection(EmbedConnection conn,
+ java.sql.ResultSet resultSet)
+ {
+ 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;
+
+ return lrs;
+ }
+
/**
Callback on the statement when one of its result sets is closed.
This allows the statement to control when it completes and hence
Index: java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java
===================================================================
--- java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java (revision 601462)
+++ java/engine/org/apache/derby/iapi/jdbc/ConnectionContext.java (working copy)
@@ -57,4 +57,13 @@
(
ResultSet executionResultSet
) throws java.sql.SQLException;
+
+ /**
+ * Return true if this ResultSet is one created by
+ * this connection and is open.
+ * @param resultSet ResultSet to test.
+ * @return True if this ResultSet was created by this connection
+ * and the result set is open. False otherwise.
+ */
+ public boolean isMyOpenResultSet(java.sql.ResultSet resultSet);
}