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 da...@apache.org on 2008/10/10 01:53:04 UTC

svn commit: r703295 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/ engine/org/apache/derby/iapi/sql/conn/ engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/impl/sql/ engine/org/apache/derby/impl/sql/conn/ engine/org/apache/d...

Author: dag
Date: Thu Oct  9 16:53:03 2008
New Revision: 703295

URL: http://svn.apache.org/viewvc?rev=703295&view=rev
Log:
DERBY-3897 SQLSessionContext not correctly initialized in some non-method call nested contexts

Patch derby-3897-3, which sets up the SQL session context correctly
also for substatements. See javadoc for
LanguageConnectionContext#setupSubStatementSessionContext for an
enumeration of these cases. Also adds test cases for this.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/LanguageConnectionContext.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.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/GenericActivationHolder.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericPreparedStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/AlterTableConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ConstraintConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericTriggerExecutor.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SQLSessionContextTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java Thu Oct  9 16:53:03 2008
@@ -590,26 +590,33 @@
 
 
 	/**
-	 * Return the current SQL session context for all immediately
-	 * nested connections stemming from the call or function
-	 * invocation of the statement corresponding to this activation.
+	 * Get the current SQL session context if in a nested connection of a
+	 * stored routine or in a substatement.
 	 */
-	public SQLSessionContext getNestedSQLSessionContext();
+	public SQLSessionContext getSQLSessionContextForChildren();
 
 	/**
-	 * This activation is created in a dynamic call context, remember
-	 * its caller's activation.
-	 *
-	 * @param a The caller's activation
+	 * Set up and return the current SQL session context for all immediately
+	 * nested connections stemming from the call or function invocation of the
+	 * statement corresponding to this activation (push=true) or for a
+	 * substatement, which shares the parents statement's session context
+	 * (push=false).
+	 * @param push true if used to push a new connection context
+	 */
+	public SQLSessionContext setupSQLSessionContextForChildren(boolean push);
+
+	/**
+	 * This activation is created in a dynamic call context or a substatement
+	 * execution context, chain its parent statements activation..
 	 */
-	public void setCallActivation(Activation a);
+	public void setParentActivation(Activation a);
 
 	/**
-	 * This activation is created in a dynamic call context, get its
-	 * caller's activation.
+	 * This activation is created in a dynamic call context, or substatement
+	 * execution context; get its caller's or superstatement's activation.
 	 *
 	 * @return The caller's activation
 	 */
-	public Activation getCallActivation();
+	public Activation getParentActivation();
 
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.java Thu Oct  9 16:53:03 2008
@@ -93,7 +93,8 @@
 	Activation	getActivation(LanguageConnectionContext lcc, boolean scrollable) throws StandardException;
 
 	/**
-	 * Execute the PreparedStatement and return results.
+	 * Execute the PreparedStatement and return results, used for top level
+	 * statements (not substatements) in a connection.
 	 *<p>
 	 * There is no executeQuery() or
 	 * executeUpdate(); a method is provided in
@@ -101,9 +102,6 @@
 	 *
 	 * @param activation The activation containing all the local state
 	 *		to execute the plan.
- 	 * @param rollbackParentContext True if 1) the statement context is
-	 *  NOT a top-level context, AND 2) in the event of a statement-level
-	 *	 exception, the parent context needs to be rolled back, too.
      * @param timeoutMillis timeout value in milliseconds.
 	 *
 	 * @return	A ResultSet for a statement. A ResultSet represents
@@ -114,17 +112,53 @@
 	 * @exception StandardException		Thrown on failure
 	 */
     ResultSet execute(Activation activation,
-                      boolean rollbackParentContext,
                       long timeoutMillis)
         throws StandardException;
 
 	/**
-		Simple form of execute(). Creates a new single use activation and executes it,
-		but also passes rollbackParentContext parameter (see above).
-	*/
-    ResultSet execute(LanguageConnectionContext lcc,
-                      boolean rollbackParentContext,
-                      long timeoutMillis)
+	 * Execute a statement as part of another statement (ithout a nested
+	 * connection) and return results.
+	 * <p>
+	 * There is no executeQuery() or
+	 * executeUpdate(); a method is provided in
+	 * ResultSet to tell whether to expect rows to be returned.
+	 *
+	 * @param parent The activation of the superstatement
+	 * @param activation The activation containing all the local state
+	 *		to execute the plan for substatement
+ 	 * @param rollbackParentContext True if in the event of a statement-level
+	 *	 exception, the parent context needs to be rolled back, too.
+     * @param timeoutMillis timeout value in milliseconds.
+	 *
+	 * @return	A ResultSet for a statement. A ResultSet represents
+	 *		the results returned from the statement, if any.
+	 *		Will return NULL if the plan for the PreparedStatement
+	 *		has aged out of cache, or the plan is out of date.
+	 *
+	 * @exception StandardException		Thrown on failure
+	 */
+    ResultSet executeSubStatement(Activation parent,
+								  Activation activation,
+								  boolean rollbackParentContext,
+								  long timeoutMillis)
+        throws StandardException;
+
+
+	/**
+	 * Execute a statement as part of another statement (without a nested
+	 * connection) and return results.
+	 * <p>
+	 * Creates a new single use activation and executes it, but also passes
+	 * rollbackParentContext parameter.
+	 * @param lcc language connection context
+	 * @param rollbackParentContext  True if in the event of a statement-level
+	 *	 exception, the parent context needs to be rolled back, too.
+	 * @param timeoutMillis timeout value in milliseconds.
+	 * @see #executeSubStatement(Activation, Activation, boolean, long)
+	 */
+    ResultSet executeSubStatement(LanguageConnectionContext lcc,
+								  boolean rollbackParentContext,
+								  long timeoutMillis)
         throws StandardException;
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/LanguageConnectionContext.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/LanguageConnectionContext.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/LanguageConnectionContext.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/LanguageConnectionContext.java Thu Oct  9 16:53:03 2008
@@ -471,7 +471,7 @@
 
 	/**
 	 * Reset any occurence of schemaName as current default schema in
-	 * the SQLSessionContext stack to the initial default, presumably
+	 * the SQLSessionContext stack to the initial default,
 	 * because schemaName is no longer a valid schema.
 	 *
 	 * @param activation current activation
@@ -1137,25 +1137,55 @@
 	public boolean roleIsSettable(String role) throws StandardException;
 
 	/**
-	 * Create a new SQL session context for the current activation
-	 * on the basis of the existing SQL session context (logical
-	 * session context analogue to call stack push, i.e. this happens
-	 * when a stored procedure or function that can contain SQL is
-	 * invoked. Called from generated code, see
-	 * StaticMethodCallNode#generateSetupNestedSessionContext.
-	 *
+	 * Create a new SQL session context for the current activation on the basis
+	 * of the existing SQL session context. This happens when a stored
+	 * procedure or function that can contain SQL is invoked, cf. SQL 2003
+	 * section 4.27.3, since this gives rise to a nested connection.
+	 * <p>
+	 * Called from generated code, see
+	 * {@code StaticMethodCallNode#generateSetupNestedSessionContext}.
+	 * <p>
 	 * The new SQL session context is also set in the current statement
 	 * context (of the invocation).
 	 *
 	 * @see org.apache.derby.impl.sql.compile.StaticMethodCallNode#generateSetupNestedSessionContext
 	 * @see StatementContext#getSQLSessionContext
+	 * @see #setupSubStatementSessionContext
 	 *
 	 * @param a activation of the statement which performs the call.
 	 */
 	public void setupNestedSessionContext(Activation a);
 
 	/**
-	 * Create a fresh SQLSessionContext
+	 * Get the value of top level session context of the top level connection.
+	 */
+	public SQLSessionContext getTopLevelSQLSessionContext();
+
+	/**
+	 * Used when a statement as part of its operation executes an other
+	 * statement. In contrast to setupNestedSessionContext, the activation (for
+	 * the substatement) just inherits the current session context from the
+	 * parent statements activation, it does <b>not</b> push a new copy on the
+	 * stack of session contexts.
+	 *
+	 * Currently, this is used in the following situations:
+	 * <ul>
+	 *     <li>With {@code ALTER TABLE} adding a column which has a default
+	 *         values, the default value for all the existing rows is added
+	 *         using an {@code UPDATE} substatement.
+	 *     <li>With {@code ALTER TABLE} adding a a check constraint, we will use
+	 *         a substatement {@code SELECT} to check if all rows satisfy the
+	 *         constraint.
+	 *     <li>{@code ResultSet.insertRow}, {@code updateRow}
+	 *         and {@code deleteRow}.
+	 *     <li>During trigger body execution.
+	 * </ul>
+	 * @see #setupNestedSessionContext
+	 */
+	public void setupSubStatementSessionContext(Activation a);
+
+	/**
+	 * Create a fresh SQLSessionContext for this connection.
 	 * @return new SQLSessionContext
 	 */
 	public SQLSessionContext createSQLSessionContext();

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Thu Oct  9 16:53:03 2008
@@ -3627,7 +3627,7 @@
                 // Don't see any timeout when inserting rows (use 0)
                 //execute the insert
                 org.apache.derby.iapi.sql.ResultSet rs = 
-                        ps.execute(act, true, 0L); 
+					ps.executeSubStatement(activation, act, true, 0L);
                 act.close();
 
                 lcc.popStatementContext(statementContext, null);
@@ -3702,7 +3702,8 @@
             }
             // Don't set any timeout when updating rows (use 0)
             // Execute the update where current of sql.
-            org.apache.derby.iapi.sql.ResultSet rs = ps.execute(act, true, 0L);
+            org.apache.derby.iapi.sql.ResultSet rs =
+				ps.executeSubStatement(activation, act, true, 0L);
             SQLWarning w = act.getWarnings();
             if (w != null) {
                 addWarning(w);
@@ -3767,7 +3768,7 @@
                 // Don't set any timeout when deleting rows (use 0)
                 //execute delete where current of sql
                 org.apache.derby.iapi.sql.ResultSet rs = 
-                        ps.execute(act, true, 0L);
+					ps.executeSubStatement(activation, act, true, 0L);
                 SQLWarning w = act.getWarnings();
                 if (w != null) {
                     addWarning(w);

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=703295&r1=703294&r2=703295&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 Thu Oct  9 16:53:03 2008
@@ -1232,9 +1232,7 @@
 				//and clear existing result sets in case this has been cached
 				a.reset();
 				a.setMaxRows(maxRows);
-                ResultSet resultsToWrap = ps.execute(a,
-                                                     false,
-                                                     timeoutMillis);
+                ResultSet resultsToWrap = ps.execute(a, timeoutMillis);
 				addWarning(a.getWarnings());
 
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java Thu Oct  9 16:53:03 2008
@@ -97,9 +97,9 @@
  *
  */
 
-final class GenericActivationHolder implements Activation
+final public class GenericActivationHolder implements Activation
 {
-	BaseActivation			ac;
+	public BaseActivation			ac;
 	ExecPreparedStatement	ps;
 	GeneratedClass			gc;
 	DataTypeDescriptor[]	paramTypes;
@@ -580,16 +580,20 @@
 		return ac.getTargetVTI();
 	}
 
-	public SQLSessionContext getNestedSQLSessionContext() {
-		return ac.getNestedSQLSessionContext();
+	public SQLSessionContext getSQLSessionContextForChildren() {
+		return ac.getSQLSessionContextForChildren();
     }
 
-	public void setCallActivation(Activation a) {
-		ac.setCallActivation(a);
+	public SQLSessionContext setupSQLSessionContextForChildren(boolean push) {
+		return ac.setupSQLSessionContextForChildren(push);
+    }
+
+	public void setParentActivation(Activation a) {
+		ac.setParentActivation(a);
 	}
 
-	public Activation getCallActivation() {
-		return ac.getCallActivation();
+	public Activation getParentActivation() {
+		return ac.getParentActivation();
 	}
 
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericPreparedStatement.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericPreparedStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericPreparedStatement.java Thu Oct  9 16:53:03 2008
@@ -240,31 +240,64 @@
 		// deadlock.
 		lcc.closeUnusedActivations();
 
-		Activation callingAct = null;
+		Activation parentAct = null;
 		StatementContext stmctx = lcc.getStatementContext();
 
 		if (stmctx != null) {
-			// if not null, callingAct represents the activation of
-			// a calling statement and this activation corresponds to
-			// a statement inside a stored procedure or function
-			callingAct = stmctx.getActivation();
+			// If not null, parentAct represents one of 1) the activation of a
+			// calling statement and this activation corresponds to a statement
+			// inside a stored procedure or function, and 2) the activation of
+			// a statement that performs a substatement, e.g. trigger body
+			// execution.
+			parentAct = stmctx.getActivation();
 		}
 
-		ac.setCallActivation(callingAct);
+		ac.setParentActivation(parentAct);
 
 		return ac;
 	}
 
-    public ResultSet execute(LanguageConnectionContext lcc,
-                             boolean rollbackParentContext,
-                             long timeoutMillis)
+	/**
+	 * @see PreparedStatement#executeSubStatement(LanguageConnectionContext, boolean, long)
+	 */
+    public ResultSet executeSubStatement(LanguageConnectionContext lcc,
+										 boolean rollbackParentContext,
+										 long timeoutMillis)
 		throws StandardException
 	{
+		Activation parent = lcc.getLastActivation();
 		Activation a = getActivation(lcc, false);
 		a.setSingleExecution();
-		return execute(a, rollbackParentContext, timeoutMillis);
+		lcc.setupSubStatementSessionContext(parent);
+		return executeStmt(a, rollbackParentContext, timeoutMillis);
+	}
+
+	/**
+	 * @see PreparedStatement#executeSubStatement(Activation, Activation, boolean, long)
+	 */
+    public ResultSet executeSubStatement(Activation parent,
+										 Activation activation,
+										 boolean rollbackParentContext,
+										 long timeoutMillis)
+		throws StandardException
+	{
+		parent.getLanguageConnectionContext().
+			setupSubStatementSessionContext(parent);
+		return executeStmt(activation, rollbackParentContext, timeoutMillis);
 	}
 
+
+	/**
+	 * @see PreparedStatement#execute
+	 */
+	public ResultSet execute(Activation activation,
+							 long timeoutMillis)
+			throws StandardException
+	{
+		return executeStmt(activation, false, timeoutMillis);
+	}
+
+
 	/**
 	  *	The guts of execution.
 	  *
@@ -277,10 +310,9 @@
 	  *
 	  *	@exception	StandardException thrown on error
 	  */
-
-    public ResultSet execute(Activation activation,
-                             boolean rollbackParentContext,
-                             long timeoutMillis)
+    private ResultSet executeStmt(Activation activation,
+								  boolean rollbackParentContext,
+								  long timeoutMillis)
         throws
             StandardException 
 	{

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java Thu Oct  9 16:53:03 2008
@@ -1844,8 +1844,7 @@
 	 * @see LanguageConnectionContext#getDefaultSchema(Activation a)
 	 */
 	public SchemaDescriptor getDefaultSchema(Activation a) {
-		return getCurrentSQLSessionContext(a.getCallActivation()).
-			getDefaultSchema();
+		return getCurrentSQLSessionContext(a).getDefaultSchema();
 	}
 
 	/**
@@ -1908,13 +1907,11 @@
 	public void setDefaultSchema(Activation a, SchemaDescriptor sd)
 		throws StandardException
 	{
-		Activation caller = a.getCallActivation();
-
 		if (sd == null) {
 			sd = getInitialDefaultSchemaDescriptor();
 		}
 
-		getCurrentSQLSessionContext(caller).setDefaultSchema(sd);
+		getCurrentSQLSessionContext(a).setDefaultSchema(sd);
 	}
 
 
@@ -1925,12 +1922,12 @@
 	public void resetSchemaUsages(Activation activation, String schemaName)
 			throws StandardException {
 
-		Activation caller = activation.getCallActivation();
+		Activation parent = activation.getParentActivation();
 		SchemaDescriptor defaultSchema = getInitialDefaultSchemaDescriptor();
 
 		// walk SQL session context chain
-		while (caller != null) {
-			SQLSessionContext ssc = caller.getNestedSQLSessionContext();
+		while (parent != null) {
+			SQLSessionContext ssc = parent.getSQLSessionContextForChildren();
 			SchemaDescriptor s = ssc.getDefaultSchema();
 
 			if (SanityManager.DEBUG) {
@@ -1940,7 +1937,7 @@
 			if (schemaName.equals(s.getSchemaName())) {
 				ssc.setDefaultSchema(defaultSchema);
 			}
-			caller = caller.getCallActivation();
+			parent = parent.getParentActivation();
 		}
 
 		// finally top level
@@ -3265,8 +3262,7 @@
 	 * @see LanguageConnectionContext#setCurrentRole(Activation a, String role)
 	 */
 	public void setCurrentRole(Activation a, String role) {
-		getCurrentSQLSessionContext(a.getCallActivation()).
-			setRole(role);
+		getCurrentSQLSessionContext(a).setRole(role);
 	}
 
 
@@ -3274,8 +3270,7 @@
 	 * @see LanguageConnectionContext#getCurrentRoleId(Activation a)
 	 */
 	public String getCurrentRoleId(Activation a) {
-		return getCurrentSQLSessionContext(a.getCallActivation()).
-			getRole();
+		return getCurrentSQLSessionContext(a).getRole();
 	}
 
 
@@ -3285,8 +3280,7 @@
 	public String getCurrentRoleIdDelimited(Activation a)
 			throws StandardException {
 
-		String role = getCurrentSQLSessionContext(a.getCallActivation()).
-			getRole();
+		String role = getCurrentSQLSessionContext(a).getRole();
 
 		if (role != null) {
 			beginNestedTransaction(true);
@@ -3335,22 +3329,23 @@
 	}
 
 	/**
-	 * Return the current SQL session context based on caller
+	 * Return the current SQL session context of the activation
 	 *
-	 * @param caller the activation of the caller, if any, of the
-	 * current activation
+	 * @param activation the activation
 	 */
-	private SQLSessionContext getCurrentSQLSessionContext(Activation caller) {
+	private SQLSessionContext getCurrentSQLSessionContext(Activation activation) {
 		SQLSessionContext curr;
 
-		if (caller == null ) {
+		Activation parent = activation.getParentActivation();
+
+		if (parent == null ) {
 			// top level
 			curr = getTopLevelSQLSessionContext();
 		} else {
-			// inside a nested SQL session context (stored
-			// procedure/function), the SQL session context is
-			// maintained in the activation of the caller
-			curr = caller.getNestedSQLSessionContext();
+			// inside a nested connection (stored procedure/function), or when
+			// executing a substatement the SQL session context is maintained
+			// in the activation of the parent
+			curr = parent.getSQLSessionContextForChildren();
 		}
 
 		return curr;
@@ -3386,7 +3381,12 @@
 	 * @see LanguageConnectionContext#setupNestedSessionContext(Activation a)
 	 */
 	public void setupNestedSessionContext(Activation a) {
-		SQLSessionContext sc = a.getNestedSQLSessionContext();
+		setupSessionContextMinion(a, true);
+	}
+
+	private void setupSessionContextMinion(Activation a,
+												 boolean push) {
+		SQLSessionContext sc = a.setupSQLSessionContextForChildren(push);
 
 		// Semantics for roles dictate (SQL 4.34.1.1 and 4.27.3.) that the
 		// role is initially inherited from the current session
@@ -3402,29 +3402,35 @@
 
 		StatementContext stmctx = getStatementContext();
 
-		// Since the statement is an invocation, it will now be
-		// associated with the pushed SQLSessionContext (and no longer
-		// just share that of its caller (or top).  The statement
-		// contexts of nested connection statements will inherit sc so
-		// the SQL session context is available when nested statements
-		// are compiled (and executed, for the most part).  However,
-		// for dynamic result sets, the relevant statement context
-		// (originating result set) is no longer available for
-		// execution time references to the SQL session context, so we
-		// rely on the activation of the caller for accessing it,
-		// cf. e.g. overload variants of
-		// getDefaultSchema/setDefaultSchema.  If such nested
-		// connections themselves turn out to be invocations, they in
-		// turn get a new SQLSessionContext associated with them etc.
+		// Since the statement is an invocation (iff push=true), it will now be
+		// associated with the pushed SQLSessionContext (and no longer just
+		// share that of its caller (or top).  The statement contexts of nested
+		// connection statements will inherit statement context so the SQL
+		// session context is available through it when nested statements are
+		// compiled (and executed, for the most part).  However, for dynamic
+		// result sets, the relevant statement context (originating result set)
+		// is no longer available for execution time references to the SQL
+		// session context, so we rely on the activation of the caller for
+		// accessing it, cf. e.g. overload variants of
+		// getDefaultSchema/setDefaultSchema.  If such nested connections
+		// themselves turn out to be invocations, they in turn get a new
+		// SQLSessionContext associated with them etc.
 		stmctx.setSQLSessionContext(sc);
 	}
 
 
 	/**
-	 * Get the value of topLevelSSC, possibly initializing it first.
+	 * @see LanguageConnectionContext#setupSubStatementSessionContext(Activation a)
+	 */
+	public void setupSubStatementSessionContext(Activation a) {
+		setupSessionContextMinion(a, false);
+	}
+
+
+	/**
 	 * @see GenericLanguageConnectionContext#topLevelSSC
 	 */
-	private SQLSessionContext getTopLevelSQLSessionContext() {
+	public SQLSessionContext getTopLevelSQLSessionContext() {
 		if (topLevelSSC == null) {
 			topLevelSSC = new SQLSessionContextImpl(
 				getInitialDefaultSchemaDescriptor());

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/AlterTableConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/AlterTableConstantAction.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/AlterTableConstantAction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/AlterTableConstantAction.java Thu Oct  9 16:53:03 2008
@@ -3101,7 +3101,7 @@
         // This is a substatement; for now, we do not set any timeout
         // for it. We might change this behaviour later, by linking
         // timeout to its parent statement's timeout settings.
-		ResultSet rs = ps.execute(lcc, true, 0L);
+		ResultSet rs = ps.executeSubStatement(lcc, true, 0L);
 		rs.close();
 	}
 
@@ -3122,7 +3122,7 @@
 
         // This is a substatement, for now we do not set any timeout for it
         // We might change this later by linking timeout to parent statement
-		ResultSet rs = ps.execute(lcc, false, 0L);
+		ResultSet rs = ps.executeSubStatement(lcc, false, 0L);
 		DataValueDescriptor[] rowArray = rs.getNextRow().getRowArray();
 		rs.close();
 		rs.finish();

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java Thu Oct  9 16:53:03 2008
@@ -173,37 +173,31 @@
 	protected UUID   UUIDValue;
 
 	/**
-	 * The 'callActivation' of an activation of a statement executing in
+	 * The 'parentActivation' of an activation of a statement executing in
 	 * the root connection is null.
 	 *
-	 * A non-null 'callActivation' represents the activation of the
-	 * calling statement.
+	 * A non-null 'parentActivation' represents the activation of the calling
+	 * statement (if we are in a nested connection of a stored routine), or the
+	 * activation of the parent statement (if we are executing a substatement)
 	 *
-	 * That is, if we are executing an SQL statement ('this'
-	 * activation) inside a stored procedure or function in a nested
-	 * connection, then 'callActivation' will be non-null.
-	 *
-	 * 'callActivation' is set when this activation is created (@see
+	 * 'parentActivation' is set when this activation is created (@see
 	 * GenericPreparedStatement#getActivation) based on the top of the
 	 * dynamic call stack of execution, which is tracked by
 	 * StatementContext. The nested SQL session context is initialized
-	 * by code generated for the call, after parsameters are evaluated
+	 * by code generated for the call, after parameters are evaluated
+	 * or just substatement execution starts.
 	 * @see org.apache.derby.impl.sql.compile.StaticMethodCallNode#generateSetupNestedSessionContext
+	 * @see org.apache.derby.impl.sql.GenericPreparedStatement#executeSubStatement
 	 *
 	 */
-	private Activation callActivation;
+	private Activation parentActivation;
 
 	/**
-	 * The SQL session context of a call is kept here. Also, @see
-	 * BaseActivation#callActivation.
-
-	 * A nested execution maintains its session context,
-	 * nestedSQLSessionContext, in the activation of the calling
-	 * statement's activation ('this'). While not inside a stored
-	 * procedure or function, SQL session state state is held by the
-	 * LanguageConnectionContext.
+	 * The SQL session context to be used inside a nested connection in a
+	 * stored routine or in a substatement. In the latter case, it is an alias
+	 * to the superstatement's session context.
 	 */
-	private SQLSessionContext nestedSQLSessionContext;
+	private SQLSessionContext sqlSessionContextForChildren;
 
 	//Following is the position of the session table names list in savedObjects in compiler context
 	//This is updated to be the correct value at cursor generate time if the cursor references any session table names.
@@ -1379,37 +1373,64 @@
 	}
 
 	/**
-	 * Return the current SQL session context for all immediately
-	 * nested connections stemming from the call or function
-	 * invocation of the statement corresponding to this activation.
+	 * @see org.apache.derby.iapi.sql.Activation#getSQLSessionContextForChildren
+	 */
+	public SQLSessionContext getSQLSessionContextForChildren() {
+
+		if (SanityManager.DEBUG) {
+			SanityManager.ASSERT
+				(sqlSessionContextForChildren != null,
+				 "Expected sqlSessionContextForChildren to be non-null");
+		}
+
+		return sqlSessionContextForChildren;
+	}
+
+	/**
+	 * @see org.apache.derby.iapi.sql.Activation#setupSQLSessionContextForChildren
 	 */
-	public SQLSessionContext getNestedSQLSessionContext() {
+	public SQLSessionContext setupSQLSessionContextForChildren(boolean push) {
 
-		if (nestedSQLSessionContext == null) {
-			nestedSQLSessionContext = lcc.createSQLSessionContext();
+		if (push) {
+			// Nested connection, so need to push a new context: SQL 2003,
+			// 4.37.1: "An SQL-session is associated with an
+			// SQL-connection.
+			sqlSessionContextForChildren = lcc.createSQLSessionContext();
+		} else {
+			// Substatement, so use current one
+			if (parentActivation != null) {
+				// The parent statement performing the substatement is
+				// itself inside a nested connection (stored routine)
+				sqlSessionContextForChildren =
+					parentActivation.getSQLSessionContextForChildren();
+			} else {
+				// The parent statement performing the substatement is on
+				// top level
+				sqlSessionContextForChildren =
+					lcc.getTopLevelSQLSessionContext();
+			}
 		}
 
-		return nestedSQLSessionContext;
+		return sqlSessionContextForChildren;
 	}
 
 	/**
-	 * This activation is created in a dynamic call context, remember
-	 * its caller's activation.
+	 * This activation is created in a dynamic call context or a substatement
+	 * execution context, make note of its parent statements activation (a).
 	 *
-	 * @param a The caller's activation
+	 * @param a The caller's or superstatement's activation
 	 */
-	public void setCallActivation(Activation a) {
-		callActivation = a;
+	public void setParentActivation(Activation a) {
+		parentActivation = a;
 	}
 
 	/**
-	 * This activation is created in a dynamic call context, get its
-	 * caller's activation.
+	 * Get the activation of the calling statement or parent statement.
 	 *
-	 * @return The caller's activation
+	 * @return The parent's activation
 	 */
-	public Activation getCallActivation() {
-		return callActivation;
+	public Activation getParentActivation() {
+		return parentActivation;
 	}
 
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ConstraintConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ConstraintConstantAction.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ConstraintConstantAction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ConstraintConstantAction.java Thu Oct  9 16:53:03 2008
@@ -288,7 +288,7 @@
             // This is a substatement; for now, we do not set any timeout
             // for it. We might change this behaviour later, by linking
             // timeout to its parent statement's timeout settings.
-			rs = ps.execute(lcc, false, 0L);
+			rs = ps.executeSubStatement(lcc, false, 0L);
 			ExecRow row = rs.getNextRow();
 			if (SanityManager.DEBUG)
 			{

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericTriggerExecutor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericTriggerExecutor.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericTriggerExecutor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericTriggerExecutor.java Thu Oct  9 16:53:03 2008
@@ -156,7 +156,8 @@
                 // This is a substatement; for now, we do not set any timeout
                 // for it. We might change this behaviour later, by linking
                 // timeout to its parent statement's timeout settings.
-                ResultSet rs = ps.execute(spsActivation, false, 0L);
+				ResultSet rs = ps.executeSubStatement
+					(activation, spsActivation, false, 0L);
                 if( rs.returnsRows())
                 {
                     // Fetch all the data to ensure that functions in the select list or values statement will

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java Thu Oct  9 16:53:03 2008
@@ -28,6 +28,7 @@
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
 import java.util.ArrayList;
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -60,6 +61,7 @@
     private final static String CONSTRAINTDROPPED    = "01500";
     private final static String VIEWDROPPED          = "01501";
     private final static String TRIGGERDROPPED       = "01502";
+    private final static String UNRELIABLE           = "42Y39";
 
     private final static String[] users = {"test_dbo", "DonaldDuck"};
 
@@ -165,6 +167,20 @@
                          "primary key (c1,c2,c3))");
                     // We made columns all unique so we can test references
                     // privilege for all columns.
+                    s.execute(
+                        "create procedure s1.calledNested()" +
+                        "  language java parameter style java" +
+                        "  external name " +
+                        "'org.apache.derbyTesting.functionTests.tests.lang." +
+                        "RolesConferredPrivilegesTest.calledNested' " +
+                        "  modifies sql data");
+                    s.execute
+                        ("create function s1.getCurrentRole() " +
+                         "returns varchar(30)" +
+                         "language java parameter style java external name " +
+                         "'org.apache.derbyTesting.functionTests.tests.lang." +
+                         "RolesConferredPrivilegesTest.getCurrentRole' " +
+                         " reads sql data");
                 }
             };
 
@@ -1361,6 +1377,129 @@
 
 
     /**
+     * Test that DEFAULT CURRENT_ROLE works as expected
+     * See DERBY-3897.
+     */
+    public void testDefaultCurrentRole() throws SQLException {
+        Connection dboConn = getConnection();
+        Statement s = dboConn.createStatement();
+        s.execute("grant h to DonaldDuck");
+
+        Connection c = openUserConnection("DonaldDuck");
+        Statement cStmt = c.createStatement();
+        setRole(c, "h");
+
+        // CREATE TABLE
+        cStmt.executeUpdate
+            ("create table t(role varchar(128) default current_role)");
+        cStmt.executeUpdate("insert into t values default");
+        ResultSet rs = cStmt.executeQuery("select * from t");
+        JDBC.assertSingleValueResultSet(rs, "\"H\"");
+        rs.close();
+        cStmt.executeUpdate("drop table t");
+
+        // ALTER TABLE
+        cStmt.executeUpdate("create table t(i int)");
+        cStmt.executeUpdate("insert into t values 1");
+        cStmt.executeUpdate
+            ("alter table t " +
+             "add column role varchar(10) default current_role");
+        rs = cStmt.executeQuery("select * from t");
+        JDBC.assertFullResultSet(rs, new String[][]{{"1",  "\"H\""}});
+        rs.close();
+        cStmt.executeUpdate("drop table t");
+
+        // do the same from within a stored procedure
+        s.execute("grant execute on procedure s1.calledNested to DonaldDuck");
+        cStmt.executeUpdate("call s1.calledNested()");
+        setRole(c, "none");
+
+        cStmt.close();
+        s.execute("revoke h from DonaldDuck");
+        s.execute("revoke execute on procedure s1.calledNested " +
+                  "from DonaldDuck restrict");
+        s.close();
+
+        c.close();
+        dboConn.close();
+    }
+
+
+    /**
+     * Test that CURRENT_ROLE works as expected in some miscellaneous contexts.
+     * See DERBY-3897.
+     */
+    public void testCurrentRoleInWeirdContexts() throws SQLException {
+        Connection dboConn = getConnection();
+        Statement s = dboConn.createStatement();
+        setRole(dboConn, "a1");
+        s.execute("create table trackCreds(usr varchar(30), role varchar(30))");
+        s.executeUpdate("create table t(i int)");
+        s.execute("grant insert on t to DonaldDuck");
+        s.execute("grant h to DonaldDuck");
+
+        // From within a trigger body:
+        s.execute("create trigger tr after insert on t " +
+                  "insert into trackCreds values (current_user, current_role)");
+
+        Connection c = openUserConnection("DonaldDuck");
+        Statement cStmt = c.createStatement();
+        setRole(c, "h");
+        cStmt.executeUpdate("insert into test_dbo.t values 1");
+
+        ResultSet rs = s.executeQuery("select * from trackCreds");
+        JDBC.assertFullResultSet(rs, new String[][]{{"DONALDDUCK",  "\"H\""}});
+        rs.close();
+        setRole(c, "none");
+        cStmt.close();
+
+        // From within a CHECK constraint, that we get an error
+        try {
+            s.execute("create table strange(role varchar(30) " +
+                      "check (role = current_role))");
+            fail("current_role inside a check constraint should be denied");
+        } catch (SQLException e) {
+            assertSQLState(UNRELIABLE, e);
+        }
+
+        // From within a function, called from a CHECK constraint
+        // executed as a substatement as part of ALTER TABLE.
+        // In this case, the session context stack contains two elements
+        // referenced from the three activations thus:
+        //
+        // top level "alter table" act.          -> top level session context
+        // substatement (check constraint act.)  -> top level session context
+        // nested connnection in getCurrentRole,
+        //            "values current_role" act. -> pushed session context
+        //
+        // Before DERBY-3897 the call to s1.getCurrentRole would yield null
+        // because the pushed session context for getCurrentRole would inherit
+        // a wrong (newly created) session context from the CHECK constraint
+        // substatement's activation. After DERBY-3897, the substatement
+        // correctly inherits the session context of "alter table"s
+        // activation, so the pushed session context for getCurrentRole will
+        // be correct, too.
+        //
+        s.execute("create table strange(i int)");
+        s.execute("insert into strange values null");
+        s.execute("alter table strange " +
+                  "add constraint s check (s1.getCurrentRole() = '\"A1\"')");
+
+        s.execute("revoke h from DonaldDuck");
+        s.execute("revoke insert on t from DonaldDuck");
+        setRole(dboConn, "none");
+        s.execute("drop table trackCreds");
+        s.execute("drop table t");
+        s.execute("drop table strange");
+        s.close();
+
+        c.close();
+        dboConn.close();
+    }
+
+
+
+    /**
      * stored function: s1.f1
      */
     public static int s1f1() {
@@ -2481,5 +2620,70 @@
             fail(b.toString());
         }
     }
+
+    public static void calledNested()
+            throws SQLException
+    {
+        Connection c = null;
+
+        try {
+            c = DriverManager.getConnection("jdbc:default:connection");
+            Statement cStmt = c.createStatement();
+
+            // CREATE TABLE
+            cStmt.executeUpdate
+                ("create table t(role varchar(128) default current_role)");
+            cStmt.executeUpdate("insert into t values default");
+            ResultSet rs = cStmt.executeQuery("select * from t");
+            JDBC.assertSingleValueResultSet(rs, "\"H\"");
+            rs.close();
+            cStmt.executeUpdate("drop table t");
+
+            // ALTER TABLE
+            cStmt.executeUpdate("create table t(i int)");
+            cStmt.executeUpdate("insert into t values 1");
+            cStmt.executeUpdate
+                ("alter table t " +
+                 "add column role varchar(10) default current_role");
+            rs = cStmt.executeQuery("select * from t");
+            JDBC.assertFullResultSet(rs, new String[][]{{"1",  "\"H\""}});
+            rs.close();
+            cStmt.executeUpdate("drop table t");
+            cStmt.close();
+        } finally {
+            if (c != null) {
+                try {
+                    c.close();
+                } catch (Exception e) {
+                }
+            }
+        }
+    }
+
+
+    public static String getCurrentRole()
+            throws SQLException
+    {
+        Connection c = null;
+
+        try {
+            c = DriverManager.getConnection("jdbc:default:connection");
+            Statement cStmt = c.createStatement();
+
+            ResultSet rs = cStmt.executeQuery("values current_role");
+            rs.next();
+            String result = rs.getString(1);
+            rs.close();
+            cStmt.close();
+            return result;
+        } finally {
+            if (c != null) {
+                try {
+                    c.close();
+                } catch (Exception e) {
+                }
+            }
+        }
+    }
 }
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SQLSessionContextTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SQLSessionContextTest.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SQLSessionContextTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SQLSessionContextTest.java Thu Oct  9 16:53:03 2008
@@ -270,6 +270,13 @@
         for (int i= 0; i < variableKeywords.length; i++) {
             ps[i].close();
         }
+
+        // DERBY-3897: See
+        //
+        // RolesConferredPrivilegesTest#testDefaultCurrentRole and
+        // RolesConferredPrivilegesTest#testCurrentRoleInWeirdContexts
+        //
+        // which are also relevant tests for SQLSessionContext.
     }
 
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java?rev=703295&r1=703294&r2=703295&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java Thu Oct  9 16:53:03 2008
@@ -764,7 +764,7 @@
     /**
      * Asserts a ResultSet returns a single row with a single
      * column equal to the passed in String value. The value can
-     * be null to indicate SQL NULL. The comparision is make
+     * be null to indicate SQL NULL. The comparision is made
      * using assertFullResultSet in trimmed string mode.
      *  As a side effect, this method closes the ResultSet.
      */