|
| 1 | +#coding:utf-8 |
| 2 | + |
| 3 | +""" |
| 4 | +ID: issue-7426 |
| 5 | +ISSUE: https://github.com/FirebirdSQL/firebird/issues/7426 |
| 6 | +TITLE: Ensure the DDL trigger requests are cached |
| 7 | +DESCRIPTION: |
| 8 | + Test prepares trace config with requrement to see TRIGGERS compilation. |
| 9 | + We create two DDL triggers for logging any DDL statement, before and after it occurs. |
| 10 | + Then we create table with two constraints and one index, and make several alter statements. |
| 11 | +
|
| 12 | + Finally, we parse trace log and filter only lines containing name of DDL triggers. |
| 13 | + Only ONE occurence must be for each of DDL trigger (totally 2). |
| 14 | + Before this ticked was fixed, every time we did some DDL appropriate trigger was compiled |
| 15 | + and this could be seen in a trace log (checked on 5.0.0.1182). |
| 16 | +
|
| 17 | + No errors must present in the trace log. |
| 18 | +NOTES: |
| 19 | + [07-sep-2023] pzotov |
| 20 | + ::: NB ::: |
| 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. Currently there is no way to specify in the trace what EXACT type of DDL trigger fired. It is shown as "AFTER DDL". |
| 24 | + |
| 25 | + Checked on 5.0.0.1190. |
| 26 | +""" |
| 27 | +import locale |
| 28 | +import re |
| 29 | +import pytest |
| 30 | +from firebird.qa import * |
| 31 | + |
| 32 | +db = db_factory() |
| 33 | + |
| 34 | +act = python_act('db') |
| 35 | + |
| 36 | +trace = ['log_initfini = false', |
| 37 | + 'log_errors = true', |
| 38 | + 'log_trigger_compile = true', |
| 39 | + ] |
| 40 | + |
| 41 | +allowed_patterns = [ ' ERROR AT ', 'Trigger TRG_ANY_DDL_STATEMENT_', ] |
| 42 | +allowed_patterns = [ re.compile(r, re.IGNORECASE) for r in allowed_patterns] |
| 43 | + |
| 44 | +@pytest.mark.version('>=5.0') |
| 45 | +def test_1(act: Action, capsys): |
| 46 | + |
| 47 | + test_script = f""" |
| 48 | + recreate table log_ddl_triggers_activity ( |
| 49 | + id int generated by default as identity constraint pk_log_ddl_triggers_activity primary key |
| 50 | + ,ddl_trigger_name varchar(64) |
| 51 | + ,event_type varchar(25) not null |
| 52 | + ,object_type varchar(25) not null |
| 53 | + ,ddl_event varchar(25) not null |
| 54 | + ,object_name varchar(64) not null |
| 55 | + ,dts timestamp default 'now' |
| 56 | + ); |
| 57 | +
|
| 58 | + set autoddl off; |
| 59 | +
|
| 60 | + set term ^; |
| 61 | + execute block as |
| 62 | + begin |
| 63 | + rdb$set_context('USER_SESSION', 'SKIP_DDL_TRG', '1'); |
| 64 | + end |
| 65 | + ^ |
| 66 | + create or alter trigger trg_any_ddl_statement_alter active after any ddl statement as |
| 67 | + begin |
| 68 | + if (rdb$get_context('USER_SESSION', 'SKIP_DDL_TRG') is null) then |
| 69 | + execute statement |
| 70 | + ('insert into log_ddl_triggers_activity(ddl_trigger_name, event_type, object_type, ddl_event, object_name) values( ?, ?, ?, ?, ? )' ) |
| 71 | + ( 'ANY_DDL_STATEMENT_AFTER' |
| 72 | + ,rdb$get_context('DDL_TRIGGER', 'EVENT_TYPE') |
| 73 | + ,rdb$get_context('DDL_TRIGGER', 'OBJECT_TYPE') |
| 74 | + ,rdb$get_context('DDL_TRIGGER', 'DDL_EVENT') |
| 75 | + ,rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME') |
| 76 | + ); |
| 77 | + end |
| 78 | + ^ |
| 79 | + create or alter trigger trg_any_ddl_statement_before active before any ddl statement as |
| 80 | + begin |
| 81 | + if (rdb$get_context('USER_SESSION', 'SKIP_DDL_TRG') is null) then |
| 82 | + execute statement |
| 83 | + ('insert into log_ddl_triggers_activity(ddl_trigger_name, event_type, object_type, ddl_event, object_name) values( ?, ?, ?, ?, ? )' ) |
| 84 | + ( 'ANY_DDL_STATEMENT_BEFORE' |
| 85 | + ,rdb$get_context('DDL_TRIGGER', 'EVENT_TYPE') |
| 86 | + ,rdb$get_context('DDL_TRIGGER', 'OBJECT_TYPE') |
| 87 | + ,rdb$get_context('DDL_TRIGGER', 'DDL_EVENT') |
| 88 | + ,rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME') |
| 89 | + ); |
| 90 | + end |
| 91 | + ^ |
| 92 | + execute block as |
| 93 | + begin |
| 94 | + rdb$set_context('USER_SESSION', 'SKIP_DDL_TRG', null); |
| 95 | + end |
| 96 | + ^ |
| 97 | + commit |
| 98 | + ^ |
| 99 | +
|
| 100 | + ---------- |
| 101 | + create table test(id int not null, x smallint, y int, z int, name varchar(10)) |
| 102 | + ^ |
| 103 | + --/* |
| 104 | + alter table test add constraint test_pk primary key(id) using descending index test_id_desc |
| 105 | + ^ |
| 106 | + alter table test add constraint test_unq unique(x,y) using descending index test_x_y_unq_desc |
| 107 | + ^ |
| 108 | + create index test_name on test(name) |
| 109 | + ^ |
| 110 | + alter index test_name inactive |
| 111 | + ^ |
| 112 | + alter index test_name active |
| 113 | + ^ |
| 114 | + drop index test_name |
| 115 | + ^ |
| 116 | + alter table test drop constraint test_pk, drop constraint test_unq |
| 117 | + ^ |
| 118 | + alter table test alter x type int, alter y type bigint, alter name type varchar(20), drop z, add u varchar(10) |
| 119 | + ^ |
| 120 | + --*/ |
| 121 | + commit |
| 122 | + ^ |
| 123 | + set term ;^ |
| 124 | +
|
| 125 | + """ |
| 126 | + |
| 127 | + with act.trace(db_events=trace, encoding = locale.getpreferredencoding(), encoding_errors='utf8'): |
| 128 | + act.isql(switches = ['-q'], input = test_script, combine_output = True, io_enc = locale.getpreferredencoding()) |
| 129 | + |
| 130 | + # Process trace |
| 131 | + for line in act.trace_log: |
| 132 | + if line.rstrip().split(): |
| 133 | + for p in allowed_patterns: |
| 134 | + if p.search(line): |
| 135 | + print(line.strip()) |
| 136 | + |
| 137 | + expected_stdout = f""" |
| 138 | + Trigger TRG_ANY_DDL_STATEMENT_BEFORE (BEFORE DDL): |
| 139 | + Trigger TRG_ANY_DDL_STATEMENT_ALTER (AFTER DDL): |
| 140 | + """ |
| 141 | + |
| 142 | + act.expected_stdout = expected_stdout |
| 143 | + act.stdout = capsys.readouterr().out |
| 144 | + assert act.clean_stdout == act.clean_expected_stdout |
0 commit comments