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 2014/04/10 08:26:47 UTC

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

Author: kahatlen
Date: Thu Apr 10 06:26:46 2014
New Revision: 1586226

URL: http://svn.apache.org/r1586226
Log:
DERBY-6540: Schema-qualified table names could be mistaken for transition tables

Don't treat schema-qualified table names as transition tables, as
transition table names are always unqualified.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=1586226&r1=1586225&r2=1586226&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java Thu Apr 10 06:26:46 2014
@@ -4879,9 +4879,8 @@ public final class	DataDictionaryImpl
             for (ColumnReference ref : refs)
 			{
 				TableName tableName = ref.getQualifiedTableName();
-				if ((tableName == null) ||
-					((oldReferencingName == null || !oldReferencingName.equals(tableName.getTableName())) &&
-					(newReferencingName == null || !newReferencingName.equals(tableName.getTableName()))))
+                if (!isTransitionVariable(
+                        tableName, oldReferencingName, newReferencingName))
 				{
 					continue;
 				}
@@ -4970,9 +4969,8 @@ public final class	DataDictionaryImpl
         for (ColumnReference ref : refs)
 		{
 			TableName tableName = ref.getQualifiedTableName();
-			if ((tableName == null) ||
-				((oldReferencingName == null || !oldReferencingName.equals(tableName.getTableName())) &&
-				(newReferencingName == null || !newReferencingName.equals(tableName.getTableName()))))
+            if (!isTransitionVariable(
+                    tableName, oldReferencingName, newReferencingName))
 			{
 				continue;
 			}
@@ -5052,6 +5050,37 @@ public final class	DataDictionaryImpl
 		return newText.toString();
 	}
 
+    /**
+     * Check if a table name is actually a transition variable.
+     *
+     * @param tableName the table name to check
+     * @param oldReferencingName the name of the old transition variable
+     * @param newReferencingName the name of the new transition variable
+     * @return {@code true} if the table name is a transition variable,
+     *   {@code false} otherwise
+     */
+    private static boolean isTransitionVariable(TableName tableName,
+            String oldReferencingName, String newReferencingName) {
+        if (tableName != null) {
+            if (tableName.hasSchema()) {
+                // DERBY-6540: Schema-qualified names are not transition
+                // variables.
+                return false;
+            }
+
+            // If there is no schema, and the name is equal to the old or
+            // the new transition variable, then it is a transition variable.
+            String name = tableName.getTableName();
+            if (name != null) {
+                return name.equals(oldReferencingName)
+                        || name.equals(newReferencingName);
+            }
+        }
+
+        // Otherwise, it is not a transition variable.
+        return false;
+    }
+
 	/*
 	 * The arrary passed will have either -1 or a column position as it's 
 	 * elements. If the array only has -1 as for all it's elements, then

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java?rev=1586226&r1=1586225&r2=1586226&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java Thu Apr 10 06:26:46 2014
@@ -21,7 +21,6 @@
 
 package	org.apache.derby.impl.sql.compile;
 
-import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -724,9 +723,7 @@ class CreateTriggerNode extends DDLState
         Collections.sort(tabs, OFFSET_COMPARATOR);
         for (FromBaseTable fromTable : tabs) {
             String baseTableName = fromTable.getBaseTableName();
-            if (baseTableName == null
-                    || (!baseTableName.equals(oldTableName)
-                            && !baseTableName.equals(newTableName))) {
+            if (!isTransitionTable(fromTable)) {
                 // baseTableName is not the NEW or OLD table, so no need
                 // to do anything. Skip this table.
                 continue;
@@ -763,6 +760,28 @@ class CreateTriggerNode extends DDLState
         return newText.toString();
     }
 
+    /**
+     * Check if a table represents one of the transition tables.
+     *
+     * @param fbt the table to check
+     * @return {@code true} if {@code fbt} represents either the old or
+     *   the new transition table, {@code false} otherwise
+     */
+    private boolean isTransitionTable(FromBaseTable fbt) {
+        // DERBY-6540: It can only be a transition table if the name
+        // is not schema qualified.
+        if (!fbt.getOrigTableName().hasSchema()) {
+            String baseTableName = fbt.getBaseTableName();
+            if (baseTableName != null) {
+                return baseTableName.equals(oldTableName) ||
+                        baseTableName.equals(newTableName);
+            }
+        }
+
+        // Table name didn't match a transition table.
+        return false;
+    }
+
     /*
      * Forbid references to generated columns in the actions of BEFORE triggers.
      * This is DERBY-3948, enforcing the following section of the SQL standard:

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java?rev=1586226&r1=1586225&r2=1586226&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java Thu Apr 10 06:26:46 2014
@@ -102,8 +102,7 @@ public class TableName extends QueryTree
 	 *
 	 * @return true if this instance was initialized with not null schemaName
 	 */
-	
-    boolean hasSchema(){
+    public boolean hasSchema() {
 		return hasSchema;
 	}
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java?rev=1586226&r1=1586225&r2=1586226&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java Thu Apr 10 06:26:46 2014
@@ -2324,4 +2324,92 @@ public class TriggerTest extends BaseJDB
     public static ResultSet dummyTableFunction() {
         return null;
     }
+
+    public void testDerby6540TransitionTableNameClash() throws SQLException {
+        setAutoCommit(false);
+        Statement s = createStatement();
+        s.execute("create table d6540_t1(x int)");
+        s.execute("create table d6540_t2(y int)");
+        s.execute("create table d6540_t3(z int)");
+
+        // Test name clash for statement level triggers.
+
+        // The following statement used to fail before DERBY-6540 because
+        // APP.D6540_T2 was mistaken for the transition table D6540_T2. Since
+        // the transition table does not have a column Y, it would fail with
+        // an error message saying that column Y is not in any table in the
+        // FROM list.
+        s.execute("create trigger d6540_tr after insert on d6540_t1 "
+                + "referencing new table as d6540_t2 "
+                + "insert into d6540_t3 select x from d6540_t2 "
+                + "union all select y from app.d6540_t2");
+
+        // Verify that the trigger does what it is supposed to do.
+        PreparedStatement selT3
+                = prepareStatement("select * from d6540_t3 order by z");
+        JDBC.assertEmpty(selT3.executeQuery());
+
+        s.execute("insert into d6540_t1 values 1");
+        JDBC.assertSingleValueResultSet(selT3.executeQuery(), "1");
+
+        s.execute("insert into d6540_t2 values 2, 3");
+        s.execute("insert into d6540_t1 values 4, 5");
+        JDBC.assertFullResultSet(
+                selT3.executeQuery(),
+                new String[][] { {"1"}, {"2"}, {"3"}, {"4"}, {"5"} });
+
+        // Revert tables to clean state before we go on.
+        s.execute("truncate table d6540_t1");
+        s.execute("truncate table d6540_t2");
+        s.execute("truncate table d6540_t3");
+        s.execute("drop trigger d6540_tr");
+
+        // Test name clash for row level triggers.
+
+        // The following statement used to fail before DERBY-6540, with an
+        // error message saying that column Y was not in any of the tables
+        // in the FROM list.
+        s.execute("create trigger d6540_tr after insert on d6540_t1 "
+                + "referencing new as d6540_t2 for each row "
+                + "insert into d6540_t3 select * from app.d6540_t2 "
+                + "where d6540_t2.x = app.d6540_t2.y");
+
+        // Verify that the trigger works.
+        JDBC.assertEmpty(selT3.executeQuery());
+
+        s.execute("insert into d6540_t1 values 1");
+        JDBC.assertEmpty(selT3.executeQuery());
+
+        s.execute("insert into d6540_t2 values 1, 2, 3");
+        s.execute("insert into d6540_t1 values 2, 3, 4");
+        JDBC.assertFullResultSet(
+                selT3.executeQuery(), new String[][] { {"2"}, {"3"} });
+
+        // Verify that row level triggers still don't need to qualify
+        // table names that are the same as a transition variable, if they
+        // appear in the from list (since the transition variable cannot be
+        // used in the from list, so there is no ambiguity).
+        s.execute("drop trigger d6540_tr");
+        s.execute("create table d6540_t4(c1 int, c2 int)");
+        s.execute("create trigger d6540_tr after insert on d6540_t1 "
+                + "referencing new as d6540_t2 for each row "
+                + "insert into d6540_t4 select y, d6540_t2.x from d6540_t2");
+        s.execute("insert into d6540_t1 values 1");
+        JDBC.assertFullResultSet(
+                s.executeQuery("select * from d6540_t4 order by c1"),
+                new String[][] {
+                    { "1", "1" },
+                    { "2", "1" },
+                    { "3", "1" },
+                });
+
+        // Finally, verify that a transition table or transition variable
+        // cannot have a schema.
+        assertCompileError(SYNTAX_ERROR,
+                "create trigger d6540_tr1 after insert on d6540_t1 "
+                + "referencing new table as app.n values 1");
+        assertCompileError(SYNTAX_ERROR,
+                "create trigger d6540_tr2 after insert on d6540_t1 "
+                + "referencing new as app.n for each row values 1");
+    }
 }