Skip to content

Commit 947c2bf

Browse files
committed
Added/Updated tests\bugs\gh_7466_test.py: Checked on 5.0.0.1164. See notes.
1 parent 2cad81b commit 947c2bf

File tree

1 file changed

+277
-0
lines changed

1 file changed

+277
-0
lines changed

tests/bugs/gh_7466_test.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
#coding:utf-8
2+
3+
"""
4+
ID: issue-7466
5+
ISSUE: https://github.com/FirebirdSQL/firebird/issues/7466
6+
TITLE: Add COMPILE trace events for procedures/functions/triggers
7+
DESCRIPTION:
8+
Test prepares trace config with requrement to see events related to units compilation.
9+
We create standalone procedure, standalone function and package with procedure and function.
10+
Also, we create three type of triggers: for a table, for DB-level event ('on connect') and for any DDL statement.
11+
Then test launches trace session and runs ISQL with appropriate script with above mentioned actions.
12+
13+
Finally, we parse trace log and filter only lines containing names of created PSQL units which we know.
14+
No errors must present in the trace log. All created units must be specified in blocks related to compilation.
15+
NOTES:
16+
[17-aug-2023] pzotov
17+
::: NB :::
18+
0. This test DOES NOT check tracking of plans for queries inside those PSQL modules (i.e. strarting ticket issue,
19+
see: https://github.com/FirebirdSQL/firebird/pull/7466#issue-1564439735 ).
20+
SEPARATE TEST WILL BE IMPLEMENTED FOR THAT.
21+
1. It must be noted that the term 'COMPILE' means parsing of BLR code into an execution tree, i.e. this action
22+
occurs when unit code is loaded into metadata cache.
23+
2. Procedures and functions are loaded into metadata cache immediatelly when they are created.
24+
3. Triggers are loaded into metadata cache in 'deferred' way, when something occurs that causes trigger to fire.
25+
So, DML trigger will fire when we do (for example) INSERT, DB_level trigger - when we do some action on DB level
26+
(e.g. connect/disconnect), and similar to DDL trigger.
27+
4. Currently there is no way to specify in the trace what EXACT type of DDL trigger fired. It is shown as "AFTER DDL".
28+
5. Lot of system-related triggers are displayed in the trace log during creating user-defined units:
29+
Trigger RDB$TRIGGER_26 FOR RDB$RELATION_CONSTRAINTS
30+
Trigger RDB$TRIGGER_18 FOR RDB$INDEX_SEGMENTS (BEFORE UPDATE)
31+
Trigger RDB$TRIGGER_8 FOR RDB$USER_PRIVILEGES (BEFORE DELETE)
32+
etc. Test ignores them and takes in account only triggers that have been creates by "our" SQL script.
33+
6. User-defined DDL trigger will be loaded into metadata cache MULTIPLE times (three in this test: for create view,
34+
its altering and its dropping - although there is no re-connect between these actions). This is conisdered as bug,
35+
see: https://github.com/FirebirdSQL/firebird/pull/7426 (currently it is not yet fixed).
36+
37+
Thanks to dimitr for explanations.
38+
Discussed with dimitr, letters 17.08.2023.
39+
40+
Checked on 5.0.0.1164
41+
"""
42+
import locale
43+
import re
44+
import pytest
45+
from firebird.qa import *
46+
47+
db = db_factory()
48+
49+
act = python_act('db')
50+
51+
trace = ['log_initfini = false',
52+
'log_errors = true',
53+
'log_procedure_compile = true',
54+
'log_function_compile = true',
55+
'log_trigger_compile = true',
56+
]
57+
58+
allowed_patterns = [ ' ERROR AT ', 'Trigger TRG_', 'Procedure (SP_TEST|PG_TEST.PG_SP_WORKER)', 'Function (FN_TEST|PG_TEST.PG_FN_WORKER)' ]
59+
allowed_patterns = [ re.compile(r, re.IGNORECASE) for r in allowed_patterns]
60+
61+
@pytest.mark.version('>=5.0')
62+
def test_1(act: Action, capsys):
63+
64+
test_script = f"""
65+
set autoddl off;
66+
67+
recreate sequence g;
68+
69+
create table att_log (
70+
msg varchar(60)
71+
,dts timestamp default 'now'
72+
);
73+
74+
recreate table ddl_log (
75+
id integer,
76+
ddl_event varchar(25),
77+
sql blob sub_type text
78+
);
79+
80+
recreate table test(id int primary key, x int, y int);
81+
create index test_x on test(x);
82+
create index test_y on test(y);
83+
commit;
84+
85+
create or alter view v_init as
86+
select count(*) as cnt from test group by x
87+
rows 1
88+
;
89+
90+
create or alter view v_worker as
91+
select count(*) as cnt
92+
from test
93+
group by y
94+
plan (TEST ORDER TEST_Y)
95+
union all
96+
select cnt from v_init
97+
;
98+
commit;
99+
100+
set term ^;
101+
102+
--##################################################################
103+
104+
create or alter procedure sp_test (
105+
a_table varchar(63)
106+
,a_field varchar(63)
107+
) returns (
108+
o_field_len int
109+
) as
110+
declare procedure sp_test_inner(a_x int) returns(o_y int) as
111+
begin
112+
for select y from test where x = :a_x into o_y do suspend;
113+
end
114+
begin
115+
for
116+
select f.rdb$field_length
117+
from rdb$relation_fields rf
118+
join rdb$fields f on rf.rdb$field_source=f.rdb$field_name
119+
where rf.rdb$relation_name = upper(:a_table) and rf.rdb$field_name=upper(:a_field)
120+
into o_field_len
121+
do
122+
suspend;
123+
end
124+
^
125+
126+
--##################################################################
127+
128+
create or alter function fn_test (
129+
a_table varchar(63)
130+
,a_field varchar(63)
131+
) returns int
132+
as
133+
declare function fn_test_inner(a_x int) returns int as
134+
begin
135+
return ( select count(*) from test where x = :a_x );
136+
end
137+
begin
138+
return (
139+
select first 1 f.rdb$field_length
140+
from rdb$relation_fields rf
141+
join rdb$fields f on rf.rdb$field_source=f.rdb$field_name
142+
where rf.rdb$relation_name = upper(:a_table) and rf.rdb$field_name=upper(:a_field)
143+
);
144+
end
145+
^
146+
147+
--##################################################################
148+
149+
create or alter package pg_test as
150+
begin
151+
function pg_fn_worker returns int;
152+
procedure pg_sp_worker;
153+
end
154+
^
155+
recreate package body pg_test as
156+
begin
157+
function pg_fn_worker returns int as
158+
declare function fn_test_inner_pg(a_x int) returns int as
159+
begin
160+
return ( select count(*) from test where x = :a_x );
161+
end
162+
begin
163+
return (
164+
select sum(cnt)
165+
from (
166+
select count(*) as cnt
167+
from test group by x
168+
plan (TEST ORDER TEST_X)
169+
union all
170+
select cnt from v_worker
171+
)
172+
);
173+
end
174+
175+
procedure pg_sp_worker as
176+
declare c int;
177+
declare procedure sp_test_inner_pg(a_x int) returns(o_y int) as
178+
begin
179+
for select y from test where x = :a_x into o_y do suspend;
180+
end
181+
begin
182+
select sum(cnt)
183+
from (
184+
select count(*) as cnt
185+
from test group by x
186+
plan (TEST ORDER TEST_X)
187+
union all
188+
select cnt from v_worker
189+
)
190+
into c
191+
;
192+
end
193+
194+
end
195+
^
196+
197+
--##################################################################
198+
199+
-- DML trigger:
200+
create trigger trg_test_biu for test before insert or update as
201+
begin
202+
if (inserting) then
203+
new.id = coalesce(new.id, gen_id(g,1));
204+
new.y = minvalue(new.y, new.x * new.x);
205+
end
206+
^
207+
208+
--##################################################################
209+
210+
-- DB level trigger:
211+
create trigger trg_db_conn on connect
212+
as
213+
begin
214+
if (current_user = 'SYSDBA') then
215+
begin
216+
in autonomous transaction
217+
do
218+
begin
219+
insert into att_log (msg) values ( current_user || ' connected');
220+
end
221+
end
222+
end
223+
^
224+
225+
--##################################################################
226+
227+
-- DDL trigger:
228+
create or alter trigger trg_ddl after any ddl statement
229+
as
230+
begin
231+
insert into ddl_log(sql, ddl_event)
232+
values (rdb$get_context('DDL_TRIGGER', 'SQL_TEXT'),
233+
rdb$get_context('DDL_TRIGGER', 'DDL_EVENT') );
234+
end
235+
^
236+
237+
set term ;^
238+
commit;
239+
240+
set autoddl on;
241+
242+
connect '{act.db.dsn}';
243+
244+
insert into test(x, y) select rand()*100, rand()*100 from rdb$types rows 10;
245+
commit;
246+
247+
create view v_test as select * from test;
248+
alter view v_test as select * from rdb$database;
249+
drop view v_test;
250+
commit;
251+
"""
252+
253+
with act.trace(db_events=trace, encoding = locale.getpreferredencoding(), encoding_errors='utf8'):
254+
act.isql(switches = ['-q'], input = test_script, combine_output = True, io_enc = locale.getpreferredencoding())
255+
256+
# Process trace
257+
for line in act.trace_log:
258+
if line.rstrip().split():
259+
for p in allowed_patterns:
260+
if p.search(line):
261+
print(line.strip())
262+
263+
expected_stdout = f"""
264+
Procedure SP_TEST:
265+
Procedure PG_TEST.PG_SP_WORKER:
266+
Function FN_TEST:
267+
Function PG_TEST.PG_FN_WORKER:
268+
Trigger TRG_DB_CONN (ON CONNECT):
269+
Trigger TRG_TEST_BIU FOR TEST (BEFORE INSERT):
270+
Trigger TRG_DDL (AFTER DDL):
271+
Trigger TRG_DDL (AFTER DDL):
272+
Trigger TRG_DDL (AFTER DDL):
273+
"""
274+
275+
act.expected_stdout = expected_stdout
276+
act.stdout = capsys.readouterr().out
277+
assert act.clean_stdout == act.clean_expected_stdout

0 commit comments

Comments
 (0)