#coding:utf-8 """ ID: issue-5116 ISSUE: 5116 TITLE: EXECUTE PROCEDURE's RETURNING_VALUES and EXECUTE STATEMENT's INTO does not check validity of assignments targets leading to bugcheck DESCRIPTION: JIRA: CORE-4819 FBTEST: bugs.core_4819 """ import pytest from firebird.qa import * db = db_factory() test_script = """ create or alter procedure sp_test(a_x int) as begin end; recreate table test(id int primary key, x bigint, y bigint); insert into test values(1, 3, 7); set list on; --------------------- select 'initial state' msg, t.* from test t; --------------------- commit; set term ^; create or alter procedure sp_test(a_x int) returns(o_y int) as begin o_y = 7 * a_x; suspend; end ^ commit ^ create or alter trigger test_aud1 for test active after insert or update or delete as begin -- Since WI-T3.0.0.31846 following must produce: -- Statement failed, SQLSTATE = 42000 -- attempted update of read-only column if (not inserting) then begin execute procedure sp_test(old.x) returning_values(old.x); execute procedure sp_test(old.x) returning_values(old.y); end else begin execute procedure sp_test(new.x) returning_values(new.x); execute procedure sp_test(new.x) returning_values(new.y); end rdb$set_context( 'USER_SESSION', 'X_TRG_AIUD1', iif( inserting, new.x, old.x ) ); rdb$set_context( 'USER_SESSION', 'Y_TRG_AIUD1', iif( inserting, new.y, old.y ) ); end ^ create or alter trigger test_aud2 for test active after insert or update or delete as begin -- Since WI-T3.0.0.31846 following must produce: -- Statement failed, SQLSTATE = 42000 -- attempted update of read-only column if (not inserting) then begin execute statement ('execute procedure sp_test( ? )') ( 2 * old.x ) into old.x; execute statement ('execute procedure sp_test( ? )') ( 2 * old.x ) into old.y; end else begin execute statement ('execute procedure sp_test( ? )') ( 2 * new.x ) into new.x; execute statement ('execute procedure sp_test( ? )') ( 2 * new.x ) into new.y; end rdb$set_context( 'USER_SESSION', 'X_TRG_AIUD2', iif( inserting, new.x, old.x ) ); rdb$set_context( 'USER_SESSION', 'Y_TRG_AIUD2', iif( inserting, new.y, old.y ) ); end ^ set term ;^ commit; -- ###################################################################### -- 1. Check when 'execute proc returning_values' is called from trigger, -- making invalid update of old. & new. variables: delete from test where id = 1 returning 'delete-returning:' as msg, x as old_x, y as old_y; select 'after delete' msg, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD1') as int) as X_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD1') as int) as Y_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD2') as int) as X_TRG_AIUD2, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD2') as int) as Y_TRG_AIUD2 from rdb$database; rollback; update test set x = 17 where id = 1 returning 'update-returning:' as msg, old.x as old_x, new.x as new_x, old.y as old_y, new.y as new_y; select 'after update' msg, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD1') as int) as X_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD1') as int) as Y_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD2') as int) as X_TRG_AIUD2, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD2') as int) as Y_TRG_AIUD2, t.* from test t; rollback; --------------------- insert into test(id, x) values(2, 19) returning 'insert-returning:' as msg, x as new_x, y as new_y; select 'after insert' msg, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD1') as int) as X_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD1') as int) as Y_TRG_AIUD1, cast(rdb$get_context('USER_SESSION', 'X_TRG_AIUD2') as int) as X_TRG_AIUD2, cast(rdb$get_context('USER_SESSION', 'Y_TRG_AIUD2') as int) as Y_TRG_AIUD2, t.* from test t where id=2; rollback; -- ###################################################################### -- 2. Check when call 'execute proc returning_values' inside implicit cursor: set term ^; execute block returns(msg varchar(20), old_y int, new_y int) as begin msg='within cursor-1:'; for select x, y from test as cursor ce do begin old_y = ce.y; -- WI-T3.0.0.31845. Following leads to: -- Statement failed, SQLSTATE = XX000 -- internal Firebird consistency check (EVL_assign_to: invalid operation (229), file: evl.cpp line: 205) -- Since WI-T3.0.0.31846 must produce: -- Statement failed, SQLSTATE = 42000 -- attempted update of read-only column execute procedure sp_test(ce.x) returning_values(ce.y); new_y = ce.y; suspend; end end ^ set term ;^ rollback; -- ###################################################################### -- 3. Check when call 'execute statement' inside implicit cursor: set term ^; execute block returns(msg varchar(20), old_y int, new_y int) as begin msg='within cursor-2:'; for select x, y from test as cursor ce do begin old_y = ce.y; -- Since WI-T3.0.0.31846 following must produce: -- Statement failed, SQLSTATE = 42000 -- attempted update of read-only column execute statement ('execute procedure sp_test( ? )') ( ce.x ) into ce.y; new_y = ce.y; suspend; end end ^ set term ;^ rollback; """ act = isql_act('db', test_script) expected_stdout = """ MSG initial state ID 1 X 3 Y 7 MSG after delete X_TRG_AIUD1 Y_TRG_AIUD1 X_TRG_AIUD2 Y_TRG_AIUD2 MSG after update X_TRG_AIUD1 Y_TRG_AIUD1 X_TRG_AIUD2 Y_TRG_AIUD2 ID 1 X 3 Y 7 """ # version: 3.0 expected_stderr_1 = """ Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column Statement failed, SQLSTATE = 42000 attempted update of read-only column """ @pytest.mark.version('>=3.0,<4.0') def test_1(act: Action): act.expected_stdout = expected_stdout act.expected_stderr = expected_stderr_1 act.execute() assert (act.clean_stderr == act.clean_expected_stderr and act.clean_stdout == act.clean_expected_stdout) # version: 4.0 expected_stderr_2 = """ Statement failed, SQLSTATE = 42000 attempted update of read-only column TEST.X Statement failed, SQLSTATE = 42000 attempted update of read-only column TEST.X Statement failed, SQLSTATE = 42000 attempted update of read-only column TEST.X Statement failed, SQLSTATE = 42000 attempted update of read-only column TEST.X Statement failed, SQLSTATE = 42000 attempted update of read-only column TEST.X Statement failed, SQLSTATE = 42000 attempted update of read-only column CE.Y Statement failed, SQLSTATE = 42000 attempted update of read-only column CE.Y """ @pytest.mark.version('>=4.0') def test_2(act: Action): act.expected_stdout = expected_stdout act.expected_stderr = expected_stderr_2 act.execute() assert (act.clean_stderr == act.clean_expected_stderr and act.clean_stdout == act.clean_expected_stdout)