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 ka...@apache.org on 2010/06/21 11:05:48 UTC

svn commit: r956504 - in /db/derby/code/trunk/java/engine/org/apache/derby: iapi/sql/PreparedStatement.java impl/sql/GenericActivationHolder.java impl/sql/GenericPreparedStatement.java impl/sql/GenericStatement.java

Author: kahatlen
Date: Mon Jun 21 09:05:47 2010
New Revision: 956504

URL: http://svn.apache.org/viewvc?rev=956504&view=rev
Log:
DERBY-4279: Statement cache deadlock

When requesting a recompile of a statement, don't wait for other
threads compiling the same statement, but instead create a new
instance of the statement.

Patch contributed by Brett Wooldridge <br...@gmail.com>.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/PreparedStatement.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/GenericStatement.java

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=956504&r1=956503&r2=956504&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 Mon Jun 21 09:05:47 2010
@@ -70,10 +70,10 @@ public interface PreparedStatement
 	 * then we will not be able to recompile the statement.
 	 *
 	 * @param lcc			The LanguageConnectionContext.
-	 *
+	 * @return the re-prepared statement (may be a new PreparedStatement)
 	 * @exception StandardException thrown if unable to perform
 	 */
-	void rePrepare(LanguageConnectionContext lcc) 
+	PreparedStatement rePrepare(LanguageConnectionContext lcc)
 		throws StandardException;
 
 	/**

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=956504&r1=956503&r2=956504&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 Mon Jun 21 09:05:47 2010
@@ -258,82 +258,81 @@ final public class GenericActivationHold
 		** the statement at the same time we're trying to execute it.
 		*/
 		// synchronized (ps)
-		{
-			/* Has the activation class changed or has the activation been
-			 * invalidated? */
-			if (gc != ps.getActivationClass() || !ac.isValid())
-			{
 
-                GeneratedClass newGC;
+		/* Has the activation class changed or has the activation been
+		 * invalidated? */
+		if (gc != ps.getActivationClass() || !ac.isValid())
+		{
+	        GeneratedClass newGC;
+	        ExecPreparedStatement newPS;
 
-				if (gc != ps.getActivationClass()) {
-					// ensure the statement is valid by rePreparing it.
-					// DERBY-3260: If someone else reprepares the statement at
-					// the same time as we do, there's a window between the
-					// calls to rePrepare() and getActivationClass() when the
-					// activation class can be set to null, leading to
-					// NullPointerException being thrown later. Therefore,
-					// synchronize on ps to close the window.
-					synchronized (ps) {
-						ps.rePrepare(getLanguageConnectionContext());
-						newGC = ps.getActivationClass();
-					}
-				} else {
-					// Reuse the generated class, we just want a new activation
-					// since the old is no longer valid.
-					newGC = gc;
+			if (gc != ps.getActivationClass()) {
+				// ensure the statement is valid by rePreparing it.
+				// DERBY-3260: If someone else reprepares the statement at
+				// the same time as we do, there's a window between the
+				// calls to rePrepare() and getActivationClass() when the
+				// activation class can be set to null, leading to
+				// NullPointerException being thrown later. Therefore,
+				// synchronize on ps to close the window.
+				synchronized (ps) {
+					newPS = (ExecPreparedStatement) ps.rePrepare(getLanguageConnectionContext());
+					newGC = newPS.getActivationClass();
 				}
-
-
-				/*
-				** If we get here, it means the Activation has been invalidated
-				** or the PreparedStatement has been recompiled.  Get a new
-				** Activation and check whether the parameters are compatible.
-				** If so, transfer the parameters from the old Activation to
-				** the new one, and make that the current Activation.  If not,
-				** throw an exception.
-				*/
-				BaseActivation		newAC = (BaseActivation) newGC.newInstance(lcc);
-
-				DataTypeDescriptor[]	newParamTypes = ps.getParameterTypes();
-
-				/*
-				** Link the new activation to the prepared statement.
-				*/
-				newAC.setupActivation(ps, ac.getScrollable());
-
-				newAC.setParameters(ac.getParameterValueSet(), paramTypes);
-
-
-				/*
-				** IMPORTANT
-				**
-				** Copy any essential state from the old activation
-				** to the new activation. This must match the state
-				** setup in EmbedStatement.
-				** singleExecution, cursorName, holdability, maxRows.
-				*/
-
-				if (ac.isSingleExecution())
-					newAC.setSingleExecution();
-
-				newAC.setCursorName(ac.getCursorName());
-
-				newAC.setResultSetHoldability(ac.getResultSetHoldability());
-				if (ac.getAutoGeneratedKeysResultsetMode()) //Need to do copy only if auto generated mode is on
-					newAC.setAutoGeneratedKeysResultsetInfo(ac.getAutoGeneratedKeysColumnIndexes(),
-					ac.getAutoGeneratedKeysColumnNames());
-				newAC.setMaxRows(ac.getMaxRows());
-
-				// break the link with the prepared statement
-				ac.setupActivation(null, false);
-				ac.close();
-
-				/* Remember the new class information */
-				ac = newAC;
-				gc = newGC;
-				paramTypes = newParamTypes;
 			}
+			else
+			{
+				newGC = gc;
+				newPS = ps;
+			}
+
+			/*
+			** If we get here, it means the Activation has been invalidated
+			** or the PreparedStatement has been recompiled.  Get a new
+			** Activation and check whether the parameters are compatible.
+			** If so, transfer the parameters from the old Activation to
+			** the new one, and make that the current Activation.  If not,
+			** throw an exception.
+			*/
+			BaseActivation newAC = (BaseActivation) newGC.newInstance(lcc);
+
+			DataTypeDescriptor[] newParamTypes = newPS.getParameterTypes();
+
+			/*
+			** Link the new activation to the prepared statement.
+			*/
+			newAC.setupActivation(newPS, ac.getScrollable());
+
+			newAC.setParameters(ac.getParameterValueSet(), paramTypes);
+
+			/*
+			** IMPORTANT
+			**
+			** Copy any essential state from the old activation
+			** to the new activation. This must match the state
+			** setup in EmbedStatement.
+			** singleExecution, cursorName, holdability, maxRows.
+			*/
+
+			if (ac.isSingleExecution())
+				newAC.setSingleExecution();
+
+			newAC.setCursorName(ac.getCursorName());
+
+			newAC.setResultSetHoldability(ac.getResultSetHoldability());
+			if (ac.getAutoGeneratedKeysResultsetMode()) //Need to do copy only if auto generated mode is on
+				newAC.setAutoGeneratedKeysResultsetInfo(ac.getAutoGeneratedKeysColumnIndexes(),
+						ac.getAutoGeneratedKeysColumnNames());
+			newAC.setMaxRows(ac.getMaxRows());
+
+			// break the link with the prepared statement
+			ac.setupActivation(null, false);
+			ac.close();
+
+			/* Remember the new class information */
+			ac = newAC;
+			gc = newGC;
+			ps = newPS;
+			paramTypes = newParamTypes;
 		}
 
 		String cursorName = ac.getCursorName();

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=956504&r1=956503&r2=956504&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 Mon Jun 21 09:05:47 2010
@@ -223,14 +223,14 @@ public class GenericPreparedStatement
         return isValid && (activationClass != null) && !compilingStatement;
     }
 
-	public void rePrepare(LanguageConnectionContext lcc) 
+	public PreparedStatement rePrepare(LanguageConnectionContext lcc)
 		throws StandardException {
 		if (!upToDate()) {
 			PreparedStatement ps = statement.prepare(lcc);
-
-			if (SanityManager.DEBUG)
-				SanityManager.ASSERT(ps == this, "ps != this");
+			return ps;
 		}
+
+		return this;
 	}
 
 	/**
@@ -247,7 +247,10 @@ public class GenericPreparedStatement
 			GeneratedClass gc = getActivationClass();
 
 			if (gc == null) {
-				rePrepare(lcc);
+				PreparedStatement ps = rePrepare(lcc);
+
+				if (SanityManager.DEBUG)
+					SanityManager.ASSERT(ps == this, "ps != this");
 				gc = getActivationClass();
 			}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericStatement.java?rev=956504&r1=956503&r2=956504&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericStatement.java Mon Jun 21 09:05:47 2010
@@ -171,11 +171,18 @@ public class GenericStatement
 		// cache of prepared statement objects...
 		synchronized (preparedStmt) 
 		{
+			for (;;)
+			{
+				if (preparedStmt.compilingStatement)
+				{
+					preparedStmt = new GenericPreparedStatement(this);
+					break;
+				}
 
-			for (;;) {
-
-				if (foundInCache) {
-					if (preparedStmt.referencesSessionSchema()) {
+				if (foundInCache)
+				{
+					if (preparedStmt.referencesSessionSchema())
+					{
 						// cannot use this state since it is private to a connection.
 						// switch to a new statement.
 						foundInCache = false;
@@ -189,15 +196,7 @@ public class GenericStatement
 					return preparedStmt;
 				}
 
-				if (!preparedStmt.compilingStatement) {
-					break;
-				}
-
-				try {
-					preparedStmt.wait();
-				} catch (InterruptedException ie) {
-					throw StandardException.interrupt(ie);
-				}
+				break;
 			}
 
 			preparedStmt.compilingStatement = true;
@@ -542,7 +541,6 @@ public class GenericStatement
 		{
 			synchronized (preparedStmt) {
 				preparedStmt.compilingStatement = false;
-				preparedStmt.notifyAll();
 			}
 		}