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);
+ }
}