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 2013/10/31 08:30:25 UTC

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

Author: kahatlen
Date: Thu Oct 31 07:30:24 2013
New Revision: 1537393

URL: http://svn.apache.org/r1537393
Log:
DERBY-6348: NPE or assert failure in recursive trigger

Reset the activation in the statement context before setting up the
activation for the next trigger, so that the nested trigger activation
is a child of the top-level activation, and not a child of the
previous trigger's activation.

Modified:
    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/TriggerTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerWhenClauseTest.java

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=1537393&r1=1537392&r2=1537393&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 31 07:30:24 2013
@@ -157,6 +157,12 @@ abstract class GenericTriggerExecutor
 			*/
 			if (ps == null || recompile)
 			{
+                // The SPS activation will set its parent activation from
+                // the statement context. Reset it to the original parent
+                // activation first so that it doesn't use the activation of
+                // the previously executed SPS as parent. DERBY-6348.
+                lcc.getStatementContext().setActivation(activation);
+
 				/*
 				** We need to clone the prepared statement so we don't
 				** wind up marking that ps that is tied to sps as finished

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=1537393&r1=1537392&r2=1537393&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 Oct 31 07:30:24 2013
@@ -1916,4 +1916,34 @@ public class TriggerTest extends BaseJDB
         JDBC.assertSingleValueResultSet(
                 s.executeQuery("select * from t2"), "2");
     }
+
+    public void testDerby6348() throws SQLException {
+        setAutoCommit(false);
+        Statement s = createStatement();
+        s.execute("create table d6348(x int)");
+        s.execute("insert into d6348 values 1");
+        s.execute("create trigger d6348_tr1 after update on d6348 values 1");
+        s.execute("create trigger d6348_tr2 after update on d6348 "
+                + "for each row update d6348 set x = x + 1 where x < 3");
+
+        // Used to fail with assert failure or NullPointerException before
+        // DERBY-6348.
+        s.execute("update d6348 set x = x + 1");
+
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from d6348"),
+                "3");
+
+        rollback();
+
+        s.execute("create table d6348(x int)");
+        s.execute("create trigger d6348_tr1 after insert on d6348 "
+                + "values current_user");
+        s.execute("create trigger d6348_tr2 after insert on d6348 "
+                + "values current_user");
+
+        // Used to fail with assert failure or NullPointerException before
+        // DERBY-6348.
+        s.execute("insert into d6348 values 1");
+    }
 }

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerWhenClauseTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerWhenClauseTest.java?rev=1537393&r1=1537392&r2=1537393&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerWhenClauseTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerWhenClauseTest.java Thu Oct 31 07:30:24 2013
@@ -60,6 +60,7 @@ public class TriggerWhenClauseTest exten
     private static final String JAVA_EXCEPTION = "XJ001";
     private static final String NOT_SINGLE_COLUMN = "42X39";
     private static final String NON_SCALAR_QUERY = "21000";
+    private static final String TRIGGER_RECURSION = "54038";
 
     public TriggerWhenClauseTest(String name) {
         super(name);
@@ -790,4 +791,90 @@ public class TriggerWhenClauseTest exten
         assertStatementError(NON_SCALAR_QUERY, s, "insert into t1 values 2");
         assertTableRowCount("T2", 1);
     }
+
+    /**
+     * Test that a WHEN clause can call the CURRENT_USER function.
+     */
+    public void testCurrentUser() throws SQLException {
+        Statement s = createStatement();
+        s.execute("create table t1(x int)");
+        s.execute("create table t2(x varchar(10))");
+
+        // Create one trigger that should only fire when current user is U2,
+        // and one that should only fire when current user is different from
+        // U2.
+        s.execute("create trigger tr01 after insert on t1 "
+                + "when (current_user = 'U2') "
+                + "insert into t2 values 'TR01'");
+        s.execute("create trigger tr02 after insert on t1 "
+                + "when (current_user <> 'U2') "
+                + "insert into t2 values 'TR02'");
+        s.execute("grant insert on t1 to u2");
+
+        commit();
+
+        // Used to get an assert failure or a NullPointerException here before
+        // DERBY-6348. Expect it to succeed, and expect TR02 to have fired.
+        s.execute("insert into t1 values 1");
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from t2"), "TR02");
+
+        rollback();
+
+        // Now try the same insert as user U2.
+        Connection c2 = openUserConnection("u2");
+        c2.setAutoCommit(true);
+        Statement s2 = c2.createStatement();
+        s2.execute("insert into "
+            + JDBC.escape(TestConfiguration.getCurrent().getUserName(), "T1")
+            + " values 1");
+        s2.close();
+        c2.close();
+
+        // Since the insert was performed by user U2, expect TR01 to have fired.
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from t2"), "TR01");
+
+        // Cleanup.
+        dropTable("T1");
+        dropTable("T2");
+        commit();
+    }
+
+    /**
+     * Test that a trigger with a WHEN clause can be recursive.
+     */
+    public void testRecursiveTrigger() throws SQLException {
+        Statement s = createStatement();
+        s.execute("create table t(x int)");
+        s.execute("create trigger tr1 after insert on t "
+                + "referencing new as new for each row "
+                + "when (new.x > 0) insert into t values new.x - 1");
+
+        // Now fire the trigger. This used to cause an assert failure or a
+        // NullPointerException before DERBY-6348.
+        s.execute("insert into t values 15, 1, 2");
+
+        // The row trigger will fire three times, so that the above statement
+        // will insert the values { 15, 14, 13, ... , 0 }, { 1, 0 } and
+        // { 2, 1, 0 }.
+        String[][] expectedRows = {
+            {"0"}, {"0"}, {"0"}, {"1"}, {"1"}, {"1"}, {"2"}, {"2"}, {"3"},
+            {"4"}, {"5"}, {"6"}, {"7"}, {"8"}, {"9"}, {"10"}, {"11"},
+            {"12"}, {"13"}, {"14"}, {"15"}
+        };
+
+        JDBC.assertFullResultSet(s.executeQuery("select * from t order by x"),
+                                 expectedRows);
+
+        // Now fire the trigger with a value so that the maximum trigger
+        // recursion depth (16) is exceeded, and verify that we get the
+        // expected error.
+        assertStatementError(TRIGGER_RECURSION, s, "insert into t values 16");
+
+        // The contents of the table should not have changed, since the
+        // above statement failed and was rolled back.
+        JDBC.assertFullResultSet(s.executeQuery("select * from t order by x"),
+                                 expectedRows);
+    }
 }