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 mi...@apache.org on 2006/09/18 05:25:34 UTC

svn commit: r447212 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/sql/compile/ testing/org/apache/derbyTesting/functionTests/master/ testing/org/apache/derbyTesting/functionTests/tests/lang/

Author: mikem
Date: Sun Sep 17 20:25:34 2006
New Revision: 447212

URL: http://svn.apache.org/viewvc?view=rev&rev=447212
Log:
DERBY-1706
contributed by Mamta Satoor

This fix addresses the null pointer reported in DERBY-1706.

SESSION schema is a special schema which is used for global temporary tables. 
In order to handle global temporary table, Derby creates a in-memory SESSION 
schema descriptor which does not have any uuid associated with it. A physical 
SESSION schema(with it's uuid set properly) will be created *only* if there is
a persistent object created in it by a user. Global temporary tables can only 
reside in SESSION schema and Derby documentation recommends that SESSION schema
should not be used for other persistent objects. This is because the same 
object name could be referencing different objects within SESSION schema 
depending on in what order they got created.

For instance
create table session.t1(c11 int);
-- the following select will get data from the persistent table t1 in SESSION schema
select * from session.t1;
declare global temporary table session.t1(c11 int, c12 int) on commit delete rows not logged;
-- the following select this time will return data from the temporary table t1 rather than persistent table t1
-- This is because, at any time, if there is a global temporary table by the table referenced by a statement,
-- then Derby will always pick up the global temporary table. If no global temporary table by that name is
-- found, then Derby will look for persistent table in SESSION schema. If none found, then error will be thrown
select * from session.t1;
-- following will drop the temporary table t1 and not the persistent table t1
drop table session.t1;
-- the following select will get data from the persistent table t1 in SESSION schema because temporary table
-- doesn't exist anymore
select * from session.t1;

So, as can be seen from the example above, the statements referencing SESSION schema objects could have different meanings depending on what kind of objects exist in SESSION schema. Because of this, the compiled plans of statements referencing SESSION schema are not saved in the statement cache, rather they get compiled everytime they are executed. In order to enforce this, in the compilation phase, Derby checks if the statement at hand is referencing a SESSION schema object by calling referencesSessionSchema method. If this method returns true, the statement's compiled plan will not be saved in the statement cache.

Now, taking the script provided by Yip which results in NPE
set schema session;
create table t1 (i int);

Derby calls referencesSessionSchema while compiling "create table t1 (i int); " to see if it references SESSION schema object. Since, there is no schema associated with the table t1, Derby will check for the compilation schema which in this case is SESSION schema because we used "set schema session; ". (This happens in QueryTreeNode.getSchemaDescriptor(String schemaName, boolean raiseError) line 1486). The method
then tries to call an equal method on the UUID associated with the SESSION schema descriptor but since it is null(because no physical SESSION schema has been created yet), we end up with a null pointer exception. This will happen only if no physical SESSION schema has been created yet and user tries to create a first persistent object inside SESSION schema after doing a set schema session.

Following will not give a NPE because user hand created SESSION schema before doing set schema SESSION and creating an object inside it.
create schema session;
set schema session;
create table t1 (i int);
The hand creation of SESSION schema causes Derby to have a schema descriptor for SESSION schema with it's uuid set correctly.

The fix for the NPE is simple and that is to check if the UUID is null.


Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/declareGlobalTempTableJava.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/declareGlobalTempTableJava.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java?view=diff&rev=447212&r1=447211&r2=447212
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/QueryTreeNode.java Sun Sep 17 20:25:34 2006
@@ -1506,7 +1506,12 @@
 			getLanguageConnectionContext().getTransactionCompile(), raiseError);
 
 		if (isCurrent || isCompilation) {
-			if (sdCatalog != null)
+			//if we are dealing with a SESSION schema and it is not physically
+			//created yet, then it's uuid is going to be null. DERBY-1706
+			//Without the getUUID null check below, following will give NPE
+			//set schema session; -- session schema has not been created yet
+			//create table t1(c11 int);
+			if (sdCatalog != null && sdCatalog.getUUID() != null)
 			{
 				// different UUID for default (current) schema than in catalog,
 				// so reset default schema.

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/declareGlobalTempTableJava.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/declareGlobalTempTableJava.out?view=diff&rev=447212&r1=447211&r2=447212
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/declareGlobalTempTableJava.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/declareGlobalTempTableJava.out Sun Sep 17 20:25:34 2006
@@ -1,4 +1,6 @@
 Test declaredGlobalTempTableJava starting
+TEST-DERBY1706 : Create a persistent object in SESSION schema w/o first creating the schema
+TEST-DERBY1706 PASSED
 TEST1 : global temporary tables can only be in SESSION schema
 Expected message: The qualifier for a declared global temporary table name must be SESSION.
 TEST1 PASSED

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/declareGlobalTempTableJava.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/declareGlobalTempTableJava.java?view=diff&rev=447212&r1=447211&r2=447212
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/declareGlobalTempTableJava.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/declareGlobalTempTableJava.java Sun Sep 17 20:25:34 2006
@@ -68,6 +68,10 @@
 			con1.setAutoCommit(false);
 			con2.setAutoCommit(false);
 
+			/* Test this before other tests because this test requires
+			 that session schema has not been created yet */
+			passed = testDERBY1706(con1, s) && passed;
+
 			/* Test various schema and grammar related cases */
 			passed = testSchemaNameAndGrammar(con1, s) && passed;
 
@@ -88,6 +92,49 @@
 			System.out.println("PASS");
 
 		System.out.println("Test declaredGlobalTempTable finished");
+	}
+
+	/**
+	 * Test switching to session schema (it doesn't yet exist because
+	 * no create schema session has been issued yet) & then try to create 
+	 * first persistent object in it. This used to cause null pointer 
+	 * exception (DERBY-1706).
+	 *
+	 * @param conn	The Connection
+	 * @param s		A Statement on the Connection
+	 *
+	 * @return	true if it succeeds, false if it doesn't
+	 *
+	 * @exception SQLException	Thrown if some unexpected error happens
+	 */
+
+	static boolean testDERBY1706(Connection con1, Statement s)
+					throws SQLException {
+		boolean passed = true;
+
+		try
+		{
+			System.out.print("TEST-DERBY1706 : Create a persistent object");
+			System.out.print(" in SESSION schema w/o first creating the");
+			System.out.println(" schema");
+
+			s.executeUpdate("set schema SESSION");
+			s.executeUpdate("create table DERBY1706(c11 int)");
+			s.executeUpdate("drop table DERBY1706");
+			s.executeUpdate("set schema APP");
+			s.executeUpdate("drop schema SESSION restrict");
+
+			con1.commit();
+			System.out.println("TEST-DERBY1706 PASSED");
+		} catch (Throwable e)
+		{
+			System.out.println("Unexpected message: " + e.getMessage());
+			con1.rollback();
+			passed = false; //we shouldn't have reached here. Set passed to false to indicate failure
+			System.out.println("TEST-DERBY1706 FAILED");
+		}
+
+		return passed;
 	}
 
 	/**