| /* |
| * Derby - Class org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.derbyTesting.functionTests.tests.lang; |
| |
| import java.sql.Connection; |
| import java.sql.DriverManager; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import junit.framework.Test; |
| import org.apache.derbyTesting.junit.BaseJDBCTestCase; |
| import org.apache.derbyTesting.junit.BaseTestSuite; |
| import org.apache.derbyTesting.junit.CleanDatabaseTestSetup; |
| import org.apache.derbyTesting.junit.JDBC; |
| import org.apache.derbyTesting.junit.TestConfiguration; |
| |
| public class ProcedureInTriggerTest extends BaseJDBCTestCase { |
| |
| public ProcedureInTriggerTest(String name) { |
| super(name); |
| |
| } |
| |
| /** |
| * Test triggers that fire procedures with no sql |
| * |
| * @throws SQLException |
| */ |
| public void testTriggerNoSql() throws SQLException { |
| Statement s = createStatement(); |
| s.execute("create trigger after_stmt_trig_no_sql AFTER insert on t2 for each STATEMENT call proc_no_sql()"); |
| //insert 2 rows. check that trigger is fired - procedure should be called once |
| zeroArgCount = 0; |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| checkAndResetZeroArgCount(1); |
| ResultSet rs = s.executeQuery("SELECT * FROM T2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| // Check that a procedure used by a trigger cannot be dropped. |
| assertStatementError("X0Y25", s, "drop procedure proc_no_sql"); |
| s.execute("insert into t2 values (3,6)"); |
| checkAndResetZeroArgCount(1); |
| rs = s.executeQuery("SELECT * FROM T2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"},{"3","6"}}); |
| s.execute("create trigger after_row_trig_no_sql AFTER delete on t2 for each ROW call proc_no_sql()"); |
| //--- delete all rows. check that trigger is fired - procedure should be called 3 times |
| s.execute("delete from t2"); |
| checkAndResetZeroArgCount(3); |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger after_stmt_trig_no_sql"); |
| s.execute("drop trigger after_row_trig_no_sql"); |
| s.execute("create trigger before_stmt_trig_no_sql no cascade BEFORE insert on t2 for each STATEMENT call proc_no_sql()"); |
| //--- insert 2 rows. check that trigger is fired - procedure should be called once |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| checkAndResetZeroArgCount(1); |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| // Check that a procedure used by a trigger cannot be dropped. |
| assertStatementError("X0Y25", s, "drop procedure proc_no_sql"); |
| s.execute("insert into t2 values (3,6)"); |
| checkAndResetZeroArgCount(1); |
| // check inserts are successful |
| rs = s.executeQuery("SELECT * FROM T2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"},{"3","6"}}); |
| s.execute("create trigger before_row_trig_no_sql no cascade BEFORE delete on t2 for each ROW call proc_no_sql()"); |
| // delete and check trigger fired |
| s.execute("delete from t2"); |
| checkAndResetZeroArgCount(3); |
| // check delete is successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger before_stmt_trig_no_sql"); |
| s.execute("drop trigger before_row_trig_no_sql"); |
| s.close(); |
| } |
| |
| /** |
| * Test CONTAINS SQL triggers (neither reads no writes data) |
| * procedure does just a getConnection and that's it. |
| * |
| * @throws SQLException |
| */ |
| public void testTriggerContainsSql() throws SQLException{ |
| Statement s = createStatement(); |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| s.execute("create trigger after_row_trig_contains_sql AFTER update on t2 for each ROW call proc_contains_sql()"); |
| // --- update 2 rows. check that trigger is fired - procedure should be called twice |
| s.execute("update t2 set x = x*2"); |
| checkAndResetGetConnectionProcCount(2); |
| //--- check updates are successful |
| ResultSet rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"2","2"},{"4","4"}}); |
| s.execute("create trigger before_stmt_trig_contains_sql no cascade BEFORE delete on t2 for each STATEMENT call proc_contains_sql()"); |
| //--- delete 2 rows. check that trigger is fired - procedure should be called once |
| s.execute("delete from t2"); |
| checkAndResetGetConnectionProcCount(1); |
| //--- check delete is successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger after_row_trig_contains_sql"); |
| s.execute("drop trigger before_stmt_trig_contains_sql"); |
| s.close(); |
| } |
| |
| /** |
| * Test triggers for procedures that READ SQL DATA |
| * @throws SQLException |
| */ |
| public void testTriggerReadsSql() throws SQLException { |
| Statement s = createStatement(); |
| //--- create a row in t1 for use in select in the procedure |
| s.execute("insert into t1 values (1, 'one')"); |
| s.execute("create trigger after_stmt_trig_reads_sql AFTER insert on t2 for each STATEMENT call proc_reads_sql(1)"); |
| //--- insert 2 rows. check that trigger is fired - procedure should be called once |
| selectRowsCount = 0; |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| checkAndResetSelectRowsCount(1); |
| //--- check inserts are successful |
| ResultSet rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger after_stmt_trig_reads_sql"); |
| s.execute("create trigger before_row_trig_reads_sql no cascade BEFORE delete on t2 for each ROW call proc_reads_sql(1)"); |
| //--- delete 2 rows. check that trigger is fired - procedure should be called twice |
| s.execute("delete from t2"); |
| checkAndResetSelectRowsCount(2); |
| // --- check delete is successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger before_row_trig_reads_sql"); |
| //--- empty t1 |
| s.execute("delete from t1"); |
| s.close(); |
| } |
| |
| /** |
| * Test triggers that MODIFY SQL DATA |
| * |
| * @throws SQLException |
| */ |
| public void testModifiesSql() throws SQLException { |
| Statement s = createStatement(); |
| s.execute("create trigger after_stmt_trig_modifies_sql_insert_op AFTER insert on t2 for each STATEMENT call proc_modifies_sql_insert_op(1, 'one')"); |
| //--- insert 2 rows |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| //--- check trigger is fired. insertRow should be called once |
| ResultSet rs = s.executeQuery("select * from t1"); |
| JDBC.assertFullResultSet(rs, new String[][]{{"1","one"}}); |
| //--- check inserts are successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| |
| s.execute("create trigger after_row_trig_modifies_sql_update_op AFTER update of x on t2 for each ROW call proc_modifies_sql_update_op(2)"); |
| //--- update all rows |
| s.execute("update t2 set x=x*2"); |
| // --- check row trigger was fired. value of i should be 5 |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertFullResultSet(rs, new String[][]{{"5","one"}}); |
| //--- check update successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"2","2"},{"4","4"}}); |
| s.execute("create trigger after_stmt_trig_modifies_sql_delete_op AFTER delete on t2 for each STATEMENT call proc_modifies_sql_delete_op(5)"); |
| //--- delete from t2 |
| s.execute("delete from t2"); |
| // --- check trigger is fired. table t1 should be empty |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertEmpty(rs); |
| // check delete successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger after_stmt_trig_modifies_sql_insert_op"); |
| s.execute("drop trigger after_row_trig_modifies_sql_update_op"); |
| s.execute("drop trigger after_stmt_trig_modifies_sql_delete_op"); |
| s.execute("create trigger refer_new_row_trig AFTER insert on t2 REFERENCING NEW as new for each ROW call proc_modifies_sql_insert_op(new.x, 'new')"); |
| //--- insert a row |
| s.execute("insert into t2 values (25, 50)"); |
| //--- check trigger is fired. insertRow should be called once |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"25","new"}}); |
| // --- check inserts are successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"25","50"}}); |
| s.execute("create trigger refer_old_row_trig AFTER delete on t2 REFERENCING OLD as old for each ROW call proc_modifies_sql_delete_op(old.x)"); |
| // --- delete a row |
| s.execute("delete from t2 where x=25"); |
| //--- check trigger is fired. deleteRow should be called once |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertEmpty(rs); |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger refer_new_row_trig"); |
| s.execute("drop trigger refer_old_row_trig"); |
| //--- create a before trigger that calls a procedure that modifies sql data. |
| //--- trigger creation should fail |
| assertStatementError("42Z9D",s,"create trigger before_trig_modifies_sql no cascade BEFORE insert on t2 for each STATEMENT call proc_modifies_sql_insert_op(1, 'one')"); |
| //--- in a BEFORE trigger, call a procedure which actually modifies SQL data |
| //--- trigger creation will pass but firing should fail |
| s.execute("create trigger bad_before_trig no cascade BEFORE insert on t2 for each STATEMENT call proc_wrongly_defined_as_no_sql(50, 'fifty')"); |
| //--- try to insert 2 rows |
| try { |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| } catch (SQLException se) { |
| assertSQLState("38000", se); |
| se = se.getNextException(); |
| assertSQLState("38001", se); |
| } |
| //--- check trigger is not fired. |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertEmpty(rs); |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger bad_before_trig"); |
| //--- procedures which insert/update/delete into trigger table |
| s.execute("create trigger insert_trig AFTER update on t1 for each STATEMENT call proc_modifies_sql_insert_op(1, 'one')"); |
| s.execute("insert into t1 values(2, 'two')"); |
| s.execute("update t1 set i=i+1"); |
| //--- Check that update and insert successful. t1 should have 2 rows |
| rs = s.executeQuery("select * from t1"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"3","two"},{"1","one"}}); |
| s.execute("drop trigger insert_trig"); |
| s.execute("create trigger update_trig AFTER insert on t1 for each STATEMENT call proc_modifies_sql_update_op(2)"); |
| s.execute("insert into t1 values (4,'four')"); |
| //--- Check that insert successful and trigger fired. |
| rs = s.executeQuery("select * from t1"); |
| String [][] expectedRows = {{"5","two "}, |
| {"3","one "}, |
| {"6","four "}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| |
| s.execute("drop trigger update_trig"); |
| s.execute("create trigger delete_trig AFTER insert on t1 for each STATEMENT call proc_modifies_sql_delete_op(3)"); |
| s.execute("insert into t1 values (8,'eight')"); |
| //-- Check that insert was successful and trigger was fired |
| rs = s.executeQuery("select * from t1"); |
| expectedRows = new String [][] |
| {{"5","two "},{"6","four "},{"8","eight "}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| s.execute("drop trigger delete_trig"); |
| //--- Procedures with schema name |
| s.execute("create trigger call_proc_in_default_schema AFTER insert on t2 for each STATEMENT call APP.proc_no_sql()"); |
| //--- insert 2 rows. check that trigger is fired - procedure should be called once |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| //--- check inserts are successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] { {"1","2"}, {"2","4"}}); |
| s.execute("drop trigger call_proc_in_default_schema"); |
| s.execute("create trigger call_proc_in_default_schema no cascade BEFORE delete on t2 for each ROW call APP.proc_no_sql()"); |
| //--- delete 2 rows. check that trigger is fired - procedure should be called twice |
| s.execute("delete from t2"); |
| //--- check delete is successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger call_proc_in_default_schema"); |
| s.execute("create trigger call_proc_in_new_schema no cascade BEFORE insert on t2 for each STATEMENT call new_schema.proc_in_new_schema()"); |
| //--- insert 2 rows. check that trigger is fired - procedure should be called once |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| //--- check inserts are successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger call_proc_in_new_schema"); |
| s.execute("create trigger call_proc_in_new_schema AFTER delete on t2 for each ROW call new_schema.proc_in_new_schema()"); |
| //--- delete 2 rows. check that trigger is fired - procedure should be called twice |
| s.execute("delete from t2"); |
| //--- check delete is successful |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertEmpty(rs); |
| s.execute("drop trigger call_proc_in_new_schema"); |
| s.close(); |
| } |
| |
| |
| /** |
| * Some misc negative tests for procedures in triggers. |
| * |
| * @throws SQLException |
| */ |
| public void testTriggerNegative() throws SQLException { |
| Statement s = createStatement(); |
| |
| // Insert some test data. |
| s.execute("insert into t1 values (5,'two'), (6,'four'), (8,'eight')"); |
| |
| ResultSet rs; |
| assertStatementError("42Y03",s,"create trigger call_non_existent_proc1 AFTER insert on t2 for each ROW call non_existent_proc()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_NON_EXISTENT_PROC1'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42Y03",s,"create trigger call_proc_with_non_existent_proc2 AFTER insert on t2 for each ROW call new_schema.non_existent_proc()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_NON_EXISTENT_PROC2'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42Y07",s,"create trigger call_proc_in_non_existent_schema AFTER insert on t2 for each ROW call non_existent_schema.non_existent_proc()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_PROC_IN_NON_EXISTENT_SCHEMA'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42X50",s,"create trigger call_proc_using_non_existent_method AFTER insert on t2 for each ROW call proc_using_non_existent_method()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername as VARCHAR(128))='CALL_PROC_WITH_NON_EXISTENT_METHOD'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42Y03",s,"create trigger call_non_existent_proc1 no cascade BEFORE insert on t2 for each ROW call non_existent_proc()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_NON_EXISTENT_PROC1'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42Y07",s,"create trigger call_proc_in_non_existent_schema no cascade BEFORE insert on t2 for each ROW call non_existent_schema.non_existent_proc()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_PROC_IN_NON_EXISTENT_SCHEMA'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| assertStatementError("42X50",s,"create trigger call_proc_using_non_existent_method no cascade BEFORE insert on t2 for each ROW call proc_using_non_existent_method()"); |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='CALL_PROC_WITH_NON_EXISTENT_METHOD'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| //--- triggers must not allow dynamic parameters (?) |
| |
| assertStatementError("42Y27",s,"create trigger update_trig AFTER insert on t1 for each STATEMENT call proc_modifies_sql_update_op(?)"); |
| s.execute("insert into t2 values (1,2), (2,4)"); |
| // --- use procedure with commit |
| s.execute("create trigger commit_trig AFTER delete on t2 for each STATEMENT call commit_proc()"); |
| assertStatementError("38000",s,"delete from t2"); |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger commit_trig"); |
| s.execute("create trigger commit_trig no cascade BEFORE delete on t2 for each STATEMENT call commit_proc()"); |
| // -- should fail |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs,new String[][] {{"1","2"}, {"2","4"}}); |
| s.execute("drop trigger commit_trig"); |
| //--- use procedure with rollback |
| s.execute("create trigger rollback_trig AFTER delete on t2 for each STATEMENT call rollback_proc()"); |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger rollback_trig"); |
| s.execute("create trigger rollback_trig no cascade BEFORE delete on t2 for each STATEMENT call rollback_proc()"); |
| //--- should fail |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger rollback_trig"); |
| //--- use procedure which changes isolation level |
| s.execute("create trigger set_isolation_trig AFTER delete on t2 for each STATEMENT call set_isolation_proc()"); |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger set_isolation_trig"); |
| s.execute("create trigger set_isolation_trig no cascade BEFORE delete on t2 for each STATEMENT call set_isolation_proc()"); |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1","2"},{"2","4"}}); |
| s.execute("drop trigger set_isolation_trig"); |
| // --- call procedure that selects from same trigger table |
| s.execute("create trigger select_from_trig_table AFTER insert on t1 for each STATEMENT call proc_reads_sql(1)"); |
| //--- insert 2 rows check that trigger is fired - procedure should be called once |
| s.execute("insert into t1 values (10, 'ten')"); |
| //--- check inserts are successful |
| rs = s.executeQuery("select * from t1"); |
| String [][] expectedRows = { {"5","two"},{"6","four"},{"8","eight"},{"10","ten"}}; |
| JDBC.assertFullResultSet(rs,expectedRows ); |
| s.execute("drop trigger select_from_trig_table"); |
| s.execute("create trigger select_from_trig_table no cascade before delete on t1 for each STATEMENT call proc_reads_sql(1)"); |
| //--- delete a row. check that trigger is fired - procedure should be called once |
| //RESOLVE: How to check |
| s.execute("delete from t1 where i=10"); |
| // --- check delete is successful |
| rs = s.executeQuery("select * from t1"); |
| expectedRows = new String[][] { {"5","two"},{"6","four"},{"8","eight"}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| s.execute("drop trigger select_from_trig_table"); |
| //--- use procedures which alter/drop trigger table and some other table |
| s.execute("create trigger alter_table_trig AFTER delete on t1 for each STATEMENT call alter_table_proc()"); |
| assertStatementError("38000",s,"delete from t1"); |
| // check delete failed |
| rs = s.executeQuery("select * from t1"); |
| expectedRows = new String[][] { {"5","two"},{"6","four"},{"8","eight"}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| s.execute("create trigger drop_table_trig AFTER delete on t2 for each STATEMENT call drop_table_proc()"); |
| // should fail |
| assertStatementError("38000",s,"delete from t2"); |
| // check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs,new String[][] {{"1","2"}, {"2","4"}}); |
| s.execute("drop trigger drop_table_trig"); |
| //--- use procedures which create/drop trigger on trigger table and some other table |
| s.execute("create trigger create_trigger_trig AFTER delete on t1 for each STATEMENT call create_trigger_proc()"); |
| // -- should fail |
| assertStatementError("38000",s,"delete from t1"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t1"); |
| expectedRows = new String[][] { {"5","two"},{"6","four"},{"8","eight"}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| //--- check trigger is not created |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='TEST_TRIG'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"0"}}); |
| s.execute("drop trigger create_trigger_trig"); |
| //--- create a trigger to test we cannot drop it from a procedure called by a trigger |
| s.execute("create trigger test_trig AFTER delete on t1 for each STATEMENT insert into t1 values(20, 'twenty')"); |
| s.execute("create trigger drop_trigger_trig AFTER delete on t2 for each STATEMENT call drop_trigger_proc()"); |
| assertStatementError("38000",s,"delete from t2"); |
| //--- check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs,new String[][] {{"1","2"}, {"2","4"}}); |
| //--- check trigger is not dropped |
| rs = s.executeQuery("select count(*) from SYS.SYSTRIGGERS where CAST(triggername AS VARCHAR(128))='TEST_TRIG'"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1"}}); |
| s.execute("drop trigger drop_trigger_trig"); |
| //- use procedures which create/drop index on trigger table and some other table |
| s.execute("create trigger create_index_trig AFTER delete on t2 for each STATEMENT call create_index_proc()"); |
| // -- should fail |
| assertStatementError("38000",s,"delete from t2"); |
| // check delete failed |
| rs = s.executeQuery("select * from t2"); |
| JDBC.assertFullResultSet(rs,new String[][] {{"1","2"}, {"2","4"}}); |
| // -- check index is not created |
| rs = s.executeQuery("select count(*) from SYS.SYSCONGLOMERATES where CAST(CONGLOMERATENAME AS VARCHAR(128))='IX' and ISINDEX"); |
| JDBC.assertFullResultSet(rs, new String [][] {{"0"}}); |
| s.execute("drop trigger create_index_trig"); |
| //--- create an index to test we cannot drop it from a procedure called by a trigger |
| s.execute("create index ix on t1(i,b)"); |
| s.execute("create trigger drop_index_trig AFTER delete on t1 for each STATEMENT call drop_index_proc()"); |
| assertStatementError("38000",s,"delete from t1"); |
| // -- check delete failed |
| rs = s.executeQuery("select * from t1"); |
| expectedRows = new String[][] { {"5","two"},{"6","four"},{"8","eight"}}; |
| JDBC.assertFullResultSet(rs, expectedRows); |
| // -- check index is not dropped |
| rs = s.executeQuery("select count(*) from SYS.SYSCONGLOMERATES where CAST(CONGLOMERATENAME AS VARCHAR(128))='IX' and ISINDEX"); |
| JDBC.assertFullResultSet(rs, new String[][] {{"1"}}); |
| |
| // Clean up objects created by the test case. |
| s.execute("drop trigger alter_table_trig"); |
| s.execute("drop trigger test_trig"); |
| s.execute("drop trigger drop_index_trig"); |
| s.execute("drop index ix"); |
| |
| s.close(); |
| } |
| |
| |
| |
| private static Test basesuite() { |
| Test basesuite = new BaseTestSuite(ProcedureInTriggerTest.class); |
| Test clean = new CleanDatabaseTestSetup(basesuite) { |
| protected void decorateSQL(Statement s) throws SQLException { |
| s.execute("create table t1 (i int primary key, b char(15))"); |
| s.execute("create table t2 (x integer, y integer)"); |
| s.execute("create procedure proc_no_sql() parameter style java language java NO SQL external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.zeroArg'"); |
| s.execute("create procedure proc_contains_sql() parameter style java language java CONTAINS SQL external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.getConnectionProc'"); |
| s.execute("create procedure proc_reads_sql(i integer) parameter style java language java READS SQL DATA external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.selectRows' dynamic result sets 1"); |
| s.execute("create procedure proc_modifies_sql_insert_op(p1 int, p2 char(10)) parameter style java language java MODIFIES SQL DATA external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.insertRow'"); |
| s.execute("create procedure proc_modifies_sql_update_op(p1 int) parameter style java language java MODIFIES SQL DATA external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.updateRow'"); |
| s.execute("create procedure proc_modifies_sql_delete_op(p1 int) parameter style java language java MODIFIES SQL DATA external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.deleteRow'"); |
| s.execute("create procedure alter_table_proc() parameter style java language java external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.alterTable'"); |
| s.execute("create procedure drop_table_proc() parameter style java language java external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.dropTable'"); |
| s.execute("create procedure commit_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.doConnCommit'"); |
| s.execute("create procedure rollback_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.doConnRollback'"); |
| s.execute("create procedure set_isolation_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.doConnectionSetIsolation'"); |
| s.execute("create procedure create_index_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.createIndex'"); |
| s.execute("create procedure drop_index_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.dropIndex'"); |
| s.execute("create procedure create_trigger_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.createTrigger'"); |
| s.execute(" create procedure drop_trigger_proc() parameter style java dynamic result sets 0 language java contains sql external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.dropTrigger'"); |
| s.execute("create procedure proc_wrongly_defined_as_no_sql(p1 int, p2 char(10)) parameter style java language java NO SQL external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.insertRow'"); |
| // create a new schema and put a procedure in it. |
| s.execute("create schema new_schema"); |
| s.execute("create procedure new_schema.proc_in_new_schema() parameter style java language java NO SQL external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.zeroArg'"); |
| // procedure which uses non-existant method |
| s.execute("create procedure proc_using_non_existent_method() parameter style java language java NO SQL external name 'org.apache.derbyTesting.functionTests.tests.lang.ProcedureInTriggerTest.nonexistentMethod'"); |
| |
| |
| }}; |
| |
| return clean; |
| } |
| |
| public static Test suite() { |
| BaseTestSuite suite = new BaseTestSuite(); |
| if (!JDBC.vmSupportsJSR169()) { |
| suite.addTest(basesuite()); |
| suite.addTest(TestConfiguration.clientServerDecorator(basesuite())); |
| } |
| return suite; |
| } |
| |
| /** |
| * Tear down the test environment. |
| */ |
| protected void tearDown() throws Exception { |
| rollback(); |
| |
| // Remove all rows in the test tables, so that each test case |
| // sees the same initial state. |
| Statement stmt = createStatement(); |
| stmt.execute("truncate table t1"); |
| stmt.execute("truncate table t2"); |
| commit(); |
| |
| // Reset the counters. |
| zeroArgCount = 0; |
| getConnectionProcCount = 0; |
| selectRowsCount = 0; |
| |
| super.tearDown(); |
| } |
| |
| private void checkAndResetZeroArgCount(int count) { |
| assertEquals(count, zeroArgCount); |
| zeroArgCount = 0; |
| } |
| |
| // PROCEDURES |
| private static int zeroArgCount = 0; |
| public static void zeroArg() { |
| zeroArgCount++; |
| } |
| |
| private static int getConnectionProcCount = 0; |
| |
| private void checkAndResetGetConnectionProcCount(int count) { |
| assertEquals(count, getConnectionProcCount); |
| getConnectionProcCount = 0; |
| } |
| |
| public static void getConnectionProc() throws Throwable |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| Statement s = conn.createStatement(); |
| conn.close(); |
| getConnectionProcCount++; |
| } |
| |
| private static int selectRowsCount = 0; |
| |
| private void checkAndResetSelectRowsCount(int count) { |
| assertEquals(count, selectRowsCount); |
| selectRowsCount = 0; |
| } |
| |
| public static void selectRows(int p1, ResultSet[] data) throws SQLException { |
| |
| |
| |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("select * from t1 where i = ?"); |
| ps.setInt(1, p1); |
| data[0] = ps.executeQuery(); |
| conn.close(); |
| selectRowsCount++; |
| } |
| |
| public static void selectRows(int p1, int p2, ResultSet[] data1, ResultSet[] data2) throws SQLException { |
| |
| |
| |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("select * from t1 where i = ?"); |
| ps.setInt(1, p1); |
| data1[0] = ps.executeQuery(); |
| |
| ps = conn.prepareStatement("select * from t1 where i >= ?"); |
| ps.setInt(1, p2); |
| data2[0] = ps.executeQuery(); |
| |
| if (p2 == 99) |
| data2[0].close(); |
| |
| // return no results |
| if (p2 == 199) { |
| data1[0].close(); |
| data1[0] = null; |
| data2[0].close(); |
| data2[0] = null; |
| } |
| |
| // swap results |
| if (p2 == 299) { |
| ResultSet rs = data1[0]; |
| data1[0] = data2[0]; |
| data2[0] = rs; |
| } |
| |
| conn.close(); |
| selectRowsCount++; |
| } |
| |
| // select all rows from a table |
| public static void selectRows(String table, ResultSet[] rs) |
| throws SQLException |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| Statement stmt = conn.createStatement(); |
| rs[0] = stmt.executeQuery("SELECT * FROM " + table); |
| conn.close(); |
| selectRowsCount++; |
| } |
| |
| |
| public static void insertRow(int p1) throws SQLException { |
| insertRow(p1, "int"); |
| } |
| |
| public static void insertRow(int p1, String p2) throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("insert into t1 values (?, ?)"); |
| ps.setInt(1, p1); |
| ps.setString(2, p2); |
| ps.executeUpdate(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void updateRow(int p1) throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("update t1 set i=i+?"); |
| ps.setInt(1, p1); |
| ps.executeUpdate(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void deleteRow(int p1) throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("delete from t1 where i=?"); |
| ps.setInt(1, p1); |
| ps.executeUpdate(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void alterTable() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("alter table t1 add column test integer"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void dropTable() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("drop table t1"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| public static int doConnCommitInt() throws Throwable |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| conn.commit(); |
| return 1; |
| } |
| |
| public static void doConnCommit() throws Throwable |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| conn.commit(); |
| } |
| |
| public static void doConnRollback() throws Throwable |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| conn.rollback(); |
| } |
| |
| public static void doConnectionSetIsolation() throws Throwable |
| { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); |
| } |
| |
| public static void createIndex() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("create index ix on t1(i,b)"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void dropIndex() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("drop index ix"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| public static void createTrigger() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("create trigger test_trig" + |
| " AFTER delete on t1 for each STATEMENT insert into" + |
| " t1 values(20, 'twenty')"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| |
| public static void dropTrigger() throws SQLException { |
| Connection conn = DriverManager.getConnection("jdbc:default:connection"); |
| PreparedStatement ps = conn.prepareStatement("drop trigger test_trig"); |
| ps.execute(); |
| ps.close(); |
| conn.close(); |
| } |
| |
| } |