db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mi...@apache.org
Subject svn commit: r1242007 - in /db/derby/code/branches/10.8/java: engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
Date Wed, 08 Feb 2012 17:44:09 GMT
Author: mikem
Date: Wed Feb  8 17:44:09 2012
New Revision: 1242007

URL: http://svn.apache.org/viewvc?rev=1242007&view=rev
Log:
DERBY-3823 NullPointerException in stress.multi test

backported changes #1183192 and #1182570 from trunk to 10.8

This patch fixes a race condition in
EmbedPreparedStatement#getMetaData: if we are trying to retrieve the
metadata for a prepared statement while it is being recompiled there
is a time window during which the activation class is null. The
existing code could therefore cause an NPE. The new code plugs the
race condition. This NPE led to intermittent errors in
stressTestMulti. 


Modified:
    db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
    db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java

Modified: db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java?rev=1242007&r1=1242006&r2=1242007&view=diff
==============================================================================
--- db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
(original)
+++ db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
Wed Feb  8 17:44:09 2012
@@ -64,6 +64,7 @@ import java.sql.Types;
 import org.apache.derby.iapi.jdbc.BrokeredConnectionControl;
 import org.apache.derby.iapi.jdbc.EngineParameterMetaData;
 import org.apache.derby.iapi.jdbc.EnginePreparedStatement;
+import org.apache.derby.iapi.services.loader.GeneratedClass;
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.types.StringDataValue;
 import org.apache.derby.iapi.util.InterruptStatus;
@@ -1077,43 +1078,48 @@ public abstract class EmbedPreparedState
 			setupContextStack(); // make sure there's context
 
 			try {
-				//bug 4579 - if the statement is invalid, regenerate the metadata info
-				if (preparedStatement.isValid() == false)
-				{
-					//need to revalidate the statement here, otherwise getResultDescription would
-					//still have info from previous valid statement
-					preparedStatement.rePrepare(lcc);
-					rMetaData = null;
-				}
 				//bug 4579 - gcDuringGetMetaData will be null if this is the first time
 				//getMetaData call is made.
 				//Second check - if the statement was revalidated since last getMetaData call,
 				//then gcDuringGetMetaData wouldn't match with current generated class name
-				if (gcDuringGetMetaData == null || gcDuringGetMetaData.equals(execp.getActivationClass().getName())
== false)
-				{
-					rMetaData = null;
-					gcDuringGetMetaData = execp.getActivationClass().getName();
-				}
-				if (rMetaData == null)
-				{
-					ResultDescription resd = preparedStatement.getResultDescription();
-					if (resd != null)
-					{
-						// Internally, the result description has information
-						// which is used for insert, update and delete statements
-						// Externally, we decided that statements which don't
-						// produce result sets such as insert, update and delete
-						// should not return ResultSetMetaData.  This is enforced
-						// here
-						String statementType = resd.getStatementType();
-						if (statementType.equals("INSERT") ||
-								statementType.equals("UPDATE") ||
-								statementType.equals("DELETE"))
-							rMetaData = null;
-						else
-				    		rMetaData = newEmbedResultSetMetaData(resd);
-					}
-				}
+
+                GeneratedClass currAc = null;
+                ResultDescription resd = null;
+
+                synchronized(execp) {
+                    // DERBY-3823 Some other thread may be repreparing
+                    do {
+                        while (!execp.upToDate()) {
+                            execp.rePrepare(lcc);
+                        }
+
+                        currAc = execp.getActivationClass();
+                        resd = execp.getResultDescription();
+                    } while (currAc == null);
+                }
+
+                if (gcDuringGetMetaData == null ||
+                        !gcDuringGetMetaData.equals(currAc.getName())) {
+                    rMetaData = null;
+                    gcDuringGetMetaData = currAc.getName();
+                }
+
+                if (rMetaData == null && resd != null) {
+                    // Internally, the result description has information
+                    // which is used for insert, update and delete statements
+                    // Externally, we decided that statements which don't
+                    // produce result sets such as insert, update and delete
+                    // should not return ResultSetMetaData.  This is enforced
+                    // here
+                    String statementType = resd.getStatementType();
+                    if (statementType.equals("INSERT") ||
+                            statementType.equals("UPDATE") ||
+                            statementType.equals("DELETE"))
+                        rMetaData = null;
+                    else
+                        rMetaData = newEmbedResultSetMetaData(resd);
+                }
+
                 InterruptStatus.restoreIntrFlagIfSeen(lcc);
 			} catch (Throwable t) {
 				throw handleException(t);

Modified: db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java?rev=1242007&r1=1242006&r2=1242007&view=diff
==============================================================================
--- db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
(original)
+++ db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
Wed Feb  8 17:44:09 2012
@@ -1789,6 +1789,65 @@ public final class AlterTableTest extend
 
         st.executeUpdate(
                 "rename column renc_schema_2.renc_8.b to b2");
+        
+        //DERBY-3823 While a resulset is still open, network server allows
+        // ALTER TABLE to change the length of the column in the resultset,
+        // but that length is not reflected in resultset's metadata. This
+        // most likely is happening because of the pre-fetching by the 
+        // server. Related jiras are DERBY-3839 and DERBY-4373.
+        //Once DERBY-3823 is fixed, we should see the change in metadata
+        // reflected in resultset's metadata. A fix for DERBY-3823 will
+        // cause the following test to fail. Right now, the following
+        // test accepts the incorrect metadata length obtained through
+        // the resultset's metadata after ALTER TABLE has been performed.
+        conn.setAutoCommit(false);
+        //Create table and load data
+        st.executeUpdate(
+                "create table derby_3823_t1 (c11 int, c12 varchar(5))");
+        PreparedStatement ps = prepareStatement(
+        		"insert into derby_3823_t1 values(?,'aaaaa')");
+        for (int i = 0; i < 1000; i++) { 
+        	ps.setInt(1, i); 
+        	ps.executeUpdate(); 
+    	} 
+        conn.commit();
+        //Open a resultset on the table which will be altered because
+        // the resultset has been exhausted. The alter table will fail
+        // in embedded mode because of the open resulset but will succeed
+        // in network server because of the pre-fetching.
+        rs = st.executeQuery("select * from derby_3823_t1");
+        //Just get first 100 rows rather than going through all the rows
+        //Next, we will attempt to change the column length of one of the
+        // columns in the resultset and see what happens
+        for (int i = 0; i < 100; i++) { 
+        	rs.next(); 
+    	}
+        rsmd = rs.getMetaData();
+        //The column c12's length at this point is 2
+        assertEquals(5, rsmd.getColumnDisplaySize(2));
+        Statement st1 = createStatement();
+        // This should fail, as c12's column length at this point is 2 and
+        //  data being inserted is 8 characters in length
+        assertStatementError("22001", st1, "insert into derby_3823_t1 values(99,'12345678')");
+        if (usingEmbedded()) 
+        {
+        	//ALTER TABLE will fail in embedded because of the open resulset
+            assertStatementError("X0X95", st1,
+                    "alter table derby_3823_t1 alter column c12 set data type varchar(8)");
+        } else {
+        	//ALTER TABLE does not fail in network server because of pre-fetching
+            st1.execute("alter table derby_3823_t1 alter column c12 set data type varchar(8)");

+            //BUG - but the following metadata of the resultset does not show
+            //  the new column length for C12 which is 8 rather than 2
+            rsmd = rs.getMetaData(); 
+            //Following is incorrect. The column length should have been 8
+            // rather than 5
+            assertEquals(5, rsmd.getColumnDisplaySize(2));
+            //Following shows that we are able to enter 8character string after
+            // alter table alter column. It is the resulset metadata which does
+            // not reflect the change in length
+            st1.executeUpdate("insert into derby_3823_t1 values(99,'12345678')"); 
+        }
     }
     
     // DERBY-5120 Make sure that sysdepends will catch trigger



Mime
View raw message