	set wng off;
	set term ^;
	execute block as
	begin
		-- do NOT remote! This can be useful later for debug purposes!
		--rdb$set_context('USER_SESSION','DEBUG_RDB_TABLE', 'RDB$PROCEDURES');
	end
	^
	set term ;^

	create or alter user %(non_privileged_name)s password '123';
	create or alter user %(dba_privileged_name)s password '123';
	commit;

	revoke all on all from %(non_privileged_name)s;
	revoke all on all from %(dba_privileged_name)s;
	commit;

	grant RDB$ADMIN to %(dba_privileged_name)s;
	commit;

	grant create TABLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create VIEW to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create ROLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant create FILTER to %(non_privileged_name)s, %(dba_privileged_name)s;

	grant alter any TABLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any VIEW to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any ROLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant alter any FILTER to %(non_privileged_name)s, %(dba_privileged_name)s;

	grant drop any TABLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any VIEW to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any PROCEDURE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any FUNCTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any PACKAGE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any GENERATOR to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any SEQUENCE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any DOMAIN to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any EXCEPTION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any ROLE to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any CHARACTER SET to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any COLLATION to %(non_privileged_name)s, %(dba_privileged_name)s;
	grant drop any FILTER to %(non_privileged_name)s, %(dba_privileged_name)s;

	commit;

	create or alter view v_passed as select 1 id from rdb$database;
	commit;

	recreate table vulnerable_on_sys_tables(
		sys_table rdb$relation_name, 
		ord_pos smallint, -- for proper sorting
		ret_dbkey smallint default 0, -- 1 ==> this expression CONTAINS 'returning [t.]rdb$dbkey, otherwise 0
		sys_dbkey char(8) character set octets, -- value of returned rdb$db_key (to be sure that change really occured)
		vulnerable_type char(1),
		vulnerable_gdscode int,
		vulnerable_sqlcode int,
		vulnerable_sqlstate char(5),
		vulnerable_expr varchar(1024),
		dml_where varchar(255),
		actual_role varchar(20),
		id int generated by default as identity constraint [REDACTED]primary key
	);
	commit;

	create or alter view v_passed as
	select vulnerable_expr -- || ' rollback;' 
		   || iif( 
				   v.ret_dbkey = 1,
				   ' -- length of returned rdb$dbkey='|| coalesce(octet_length(sys_dbkey),'0'), 
				   '' 
				 )
		   as vulnerable_expr
	from vulnerable_on_sys_tables v 
	where v.vulnerable_gdscode < 0
	and (
		  v.ret_dbkey is distinct from 1
		  or 
		  coalesce(octet_length(sys_dbkey),0) > 0 
		)
	order by 
	     sys_table
	    ,vulnerable_expr -- before 23.10.2015: "order by  sys_table, id" --> FAILED on comparison with expected stdout because 'id' was not included in output
	;
	commit;

	recreate table dml_expr(s varchar(1024));
	commit;
	insert into dml_expr(s)
	select 'create sequence g_common' from rdb$database union all
	select 'create exception ex_bad_argument ''argument @1 is invalid''' from rdb$database union all
	select 'create collation nums_coll for utf8 from unicode case insensitive ''NUMERIC-SORT=1''' from rdb$database union all
	select 'create domain dm_int as int' from rdb$database union all
	select 'create domain dm_nums as varchar(20) character set utf8 collate nums_coll' from rdb$database union all
	select 'create table test_master(id dm_int primary key using index test_master_pk, txt dm_nums, z computed by( char_length(txt) ), constraint chk_01 check(txt is not null) )' from rdb$database union all
	select 'create table test_detail(id dm_int primary key using index test_detail_pk, pid dm_int, constraint fk_01 foreign key(pid) references test_master(id) on delete cascade using index test_detail_fk )' from rdb$database union all
	select 'create index test_master_txt on test_master(txt)' from rdb$database union all
	select 'create view v_test_master as select * from test_master' from rdb$database union all
	select 'create procedure sp_test_master(a_id dm_int) returns(o_txt dm_nums) as begin for select txt from test_master where id=:a_id into o_txt do suspend; end' from rdb$database union all
	select 'create function fn_test_master(a_id dm_int) returns dm_nums as begin return (select txt from test_master where id=:a_id); end' from rdb$database union all
	select 'create trigger test_master_bi for test_master active before insert as begin new.id=gen_id(g_common,1); end' from rdb$database union all
	select 'create package test_pkg as begin procedure pkg_test(a_id dm_int) returns(o_txt dm_nums); end' from rdb$database union all 
	select 'create package body test_pkg as begin procedure pkg_test(a_id dm_int) returns(o_txt dm_nums) as begin for select txt from test_master where id=:a_id into o_txt do suspend; end end'
	from rdb$database 
	;
	commit;

	set term ^;
	execute block as
		declare v_dbname type of column mon$database.mon$database_name;
		declare v_usr type of column sec$users.sec$user_name = '%(dba_privileged_name)s';
		declare v_pwd varchar(20) = '123';
		declare v_role varchar(20) = 'RDB$ADMIN';
	begin
		v_dbname = 'localhost:' ||  rdb$get_context('SYSTEM','DB_NAME');

		for select s from dml_expr as cursor c
		do
		execute statement (c.s)
				on external (v_dbname)
				as user  upper(v_usr) password (v_pwd) role (v_role);
	end
	^
	set term ;^
	commit;

	-- ##################################################################
	-- [ 1 ] attempts to create new tables with FKs ref. to SYSTEM tables 
	-- ##################################################################

	set term ^;

	create or alter procedure sp_gen_expr_for_creating_fkeys as
		declare rel_name varchar(31) character set unicode_fss collate unicode_fss;
		declare vulnerable_expr varchar(1024) character set unicode_fss collate unicode_fss;
		declare v_fk1 varchar(8192) character set unicode_fss collate unicode_fss;
		declare v_fks varchar(8192) character set unicode_fss collate unicode_fss;
		declare v_mf varchar(8192) character set unicode_fss collate unicode_fss;
	begin

		for
			with
			a as (
			select
				lower(rc.rdb$relation_name) tab_name
				--,rc.rdb$constraint_name cnt_name
				,lower(rc.rdb$index_name) idx_name
				--,rc.rdb$constraint_type
				--,ri.rdb$index_id
				,rs.rdb$field_position fld_pos
				,lower(rs.rdb$field_name) fld_name
				--,rf.rdb$field_name
				,rf.rdb$field_source fld_src
				,case ff.rdb$field_type
					when 7 then 'smallint'
					when 8 then 'integer'
					when 10 then 'float'
					when 14 then 'char' --(' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')'
					when 16 then -- dialect 3 only
					  --case ff.rdb$field_sub_type
					  case coalesce(ff.rdb$field_sub_type,0) -- null found for rdb$generators.rdb$initial_value AND rdb$triggers.rdb$trigger_type
						when 0 then 'bigint'
						when 1 then 'numeric' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
						when 2 then 'decimal' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
						else 'unknown'
					  end
					when 12 then 'date'
					when 13 then 'time'
					when 27 then -- dialect 1 only
					  case ff.rdb$field_scale
						when 0 then 'double precision'
						else 'numeric' -- (15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
					  end
					when 35 then iif(m.dia=1, 'date', 'timestamp')
					when 37 then 'varchar' -- (' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')'
					when 261 then 'blob' -- sub_type ' || f.rdb$field_sub_type || ' segment size ' || f.rdb$segment_length
					else 'unknown'
				end as fld_base_type
			   --,ff.rdb$field_length fld_len
			   ,cast(ff.rdb$field_length / iif(ce.rdb$character_set_name in ( upper('utf8'), upper('un~icode_fss') ), 4, 1) as int) fld_len
			   ,lower(ce.rdb$character_set_name) cset
			   ,lower(co.rdb$collation_name) coll
			   ,dense_rank()over(partition by rc.rdb$relation_name order by rc.rdb$index_name, rs.rdb$field_position) dr1
			   ,dense_rank()over(partition by rc.rdb$relation_name, rc.rdb$index_name order by rs.rdb$field_position) dr2
			   ,dense_rank()over(partition by rc.rdb$relation_name, rc.rdb$index_name order by rs.rdb$field_position desc) dr2d
			   ,count(*)over(partition by rc.rdb$relation_name) fld_cnt
			from (select m.mon$sql_dialect as dia from mon$database m) m
			cross join rdb$relation_constraints rc
			join rdb$relations rr on rc.rdb$relation_name = rr.rdb$relation_name
			join rdb$indices ri on rc.rdb$index_name = ri.rdb$index_name
			join rdb$index_segments rs on ri.rdb$index_name = rs.rdb$index_name
			left join rdb$relation_fields rf on rc.rdb$relation_name = rf.rdb$relation_name and rs.rdb$field_name = rf.rdb$field_name
			left join rdb$fields ff on rf.rdb$field_source = ff.rdb$field_name
			left join rdb$collations co on ff.rdb$character_set_id=co.rdb$character_set_id and ff.rdb$collation_id=co.rdb$collation_id
			left join rdb$character_sets ce on co.rdb$character_set_id=ce.rdb$character_set_id
			where
				--rc.rdb$relation_name --starting with upper('rdb$')
				coalesce(rr.rdb$system_flag,0) is distinct from 0 -- ###  S Y S T E M    t a b l e s  ###
				and rr.rdb$relation_type = 0 -- exclude: views, GTTs, external tables
				and rr.rdb$relation_name not starting with 'MON$'
				AND (RR.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null)
			)
		   ,b as (
				select
					tab_name,
					idx_name,
					fld_pos,
					fld_name,
					fld_src,
					fld_base_type,
					fld_len,
					cset,
					coll,
					dr1,
					dr2,
					dr2d,
					fld_cnt,
					trim( replace( fld_name, '$','_') )||' '||trim(fld_base_type)
					||iif( fld_base_type in ('char','varchar', 'numeric'),
						  '(' || fld_len || ')' || iif( fld_base_type='numeric', '', ' '||trim(coalesce(' character set '||cset, ''))||' '||trim(coalesce(' collate '||coll, '')))
						  ,''
						 ) fld_full
				from a
		   )
		   ,c as (
				select
					tab_name,
					idx_name,
					fld_pos,
					fld_name,
					fld_src,
					fld_base_type,
					fld_len,
					cset,
					coll,
					dr1,
					dr2,
					dr2d,
					fld_cnt,
					fld_full,
					(select list(distinct fld_full) from b x where x.tab_name = b.tab_name) fld_list
				from b
		   )
		select *
		from c
		order by tab_name, idx_name, dr1

		as cursor c
		do begin
		   if ( c.dr1 = 1 ) then
		   begin
			   vulnerable_expr = 'recreate table ' || trim(replace(c.tab_name,'$','_')) || '(' || c.fld_list;
			   v_fks = '';
		   end

		   if ( c.dr2 = 1 ) then begin
			   v_fk1 = '';
			   v_mf = '';
		   end

		   if ( c.dr2 = 1 ) then
			   v_fk1 = v_fk1 || '  ,constraint fk_'
							 || lower(trim(replace(c.idx_name,'$','_')))
							 || '   foreign key(';

		   v_fk1 = v_fk1 || iif( c.dr2 = 1, '', ', ') || trim( replace( c.fld_name, '$', '_' ) );
		   v_mf = v_mf || iif( c.dr2 = 1, '', ', ') || trim( c.fld_name );

		   if ( c.dr2d = 1 ) then
		   begin
			   v_fk1 = v_fk1 ||  ')' || '   references ' || trim(lower(c.tab_name)) || '(' || v_mf || ')';
			   v_fks = v_fks || v_fk1;
		   end

		   if ( c.dr1 = c.fld_cnt ) then
		   begin
			   vulnerable_expr = vulnerable_expr || v_fks || ');';

			   rel_name = c.tab_name;

			   insert into vulnerable_on_sys_tables( sys_table, vulnerable_type, vulnerable_expr)
											 values( :rel_name, 'R',            :vulnerable_expr);
		   end
		end
	end

	^

	-- #################################################################
	-- [ 2 ] attempts to change SYSTEM tables with DML or DDL statements 
	-- #################################################################

	create or alter procedure sp_gen_expr_for_direct_change as
	begin

		for
			------------------

			with recursive
			c0 as(
				select
					r.rdb$relation_name rel_name
					,rf.rdb$field_position fld_pos
					,rf.rdb$field_name fld_name
					,case f.rdb$field_type
						when 7 then 'smallint'
						when 8 then 'integer'
						when 10 then 'float'
						when 14 then 'char' --(' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')'
						when 16 then -- dialect 3 only
						  case coalesce(f.rdb$field_sub_type,0) -- null found for rdb$generators.rdb$initial_value AND rdb$triggers.rdb$trigger_type
							when 0 then 'bigint'
							when 1 then 'numeric' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
							when 2 then 'decimal' --(15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
							else 'unknown'
						  end
						when 12 then 'date'
						when 13 then 'time'
						when 27 then -- dialect 1 only
						  case f.rdb$field_scale
							when 0 then 'double precision'
							else 'numeric' -- (15,' || cast(-f.rdb$field_scale as varchar(6)) || ')'
						  end
						when 35 then iif(m.dia=1, 'date', 'timestamp')
						when 37 then 'varchar' -- (' || cast(cast(f.rdb$field_length/iif(ce.rdb$character_set_name=upper('utf8'),4,1) as int) as varchar(5)) || ')'
						when 261 then 'blob' -- sub_type ' || f.rdb$field_sub_type || ' segment size ' || f.rdb$segment_length
						else 'unknown'
					end as fld_base_type
					,iif( coalesce(rf.rdb$null_flag,0) = 0, 1, 0) fld_nullable
					,count(*)over(partition by r.rdb$relation_name) fld_count
					,sum(iif(rf.rdb$field_name = upper('rdb$system_flag'),1,0))over(partition by r.rdb$relation_name) has_sys_flag
				from (select m.mon$sql_dialect as dia from mon$database m) m
				join rdb$relations r on 1=1
				join rdb$relation_fields rf on r.rdb$relation_name = rf.rdb$relation_name
				join rdb$fields f on rf.rdb$field_source = f.rdb$field_name
				left join rdb$collations co on f.rdb$character_set_id=co.rdb$character_set_id and f.rdb$collation_id=co.rdb$collation_id
				left join rdb$character_sets ce on co.rdb$character_set_id=ce.rdb$character_set_id
				where
					coalesce(r.rdb$system_flag,0) is distinct from 0 --------------- ### S Y S T E M    t a b l e s  ###
					and r.rdb$relation_type = 0 -- exclude: views, GTTs, external tables
					and r.rdb$relation_name not starting with 'MON$'
					and f.rdb$computed_source is null ------------------------------ ### exclude COMPUTED BY fields! ###
				AND (R.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null)

				order by rel_name,fld_pos
			)
			,c1 as (
				select
					rel_name
					,has_sys_flag
					,fld_pos
					,fld_name
					,fld_base_type
					,fld_nullable
					,fld_count
					-- these are values which we'll try to write in insert or update statements
					-- if corresponding fields are NOT null:
					,decode( fld_base_type,
							 'smallint',  '32767',
							 'integer', '2147483647',
							 'bigint', '9223372036854775807',
							 'float', '0e0',
							 'numeric', '0.00',
							 'decimal', '0.00',
							 'double precision', '0e0',
							 'date', 'current_date',
							 'time', 'current_time',
							 'timestamp', 'current_timestamp',
							 'char', '''C''',
							 'varchar', '''V''',
							 'blob', '''test_for_blob'''
						  ) ins_default
				from c0
			)
			-- select * from c1

			,c2 as (
				select
					rel_name
					,fld_pos
					,fld_name
					,fld_base_type
					,fld_nullable
					,fld_count
					,ins_default
					,cast( 'select ' || trim(fld_name)
						   || iif( c1.fld_pos+1 = c1.fld_count, ' from '||trim(rel_name)||' rows 1 with lock;', '')  as varchar(1024)
						 ) as lok_stt
					,cast( 'insert into '
						   || trim(rel_name)
						   || '('
						   || trim(fld_name)
						   || iif( c1.fld_pos+1 = c1.fld_count, ')', '')  as varchar(1024)
						 )  as ins_stt
					,cast( 'values('
						   || trim(iif(fld_nullable=1, 'null', ins_default))
						   || iif( c1.fld_pos+1 = c1.fld_count, ') returning rdb$db_key;', '') as varchar(1024)
						 ) as ins_val
					,cast( 'delete from ' || trim(rel_name) || ' t '
						   || trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)=0', ''))
						   || ' rows 1'
						   || ' returning t.rdb$db_key'
						   || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
						   as varchar(1024)
						 ) del_stt
					,cast( 'alter table '|| trim(rel_name) || ' add rdb$my_field char(31)'  as varchar(1024)) add_fld
					,cast( 'alter table '|| trim(rel_name) || ' alter '||trim(fld_name)|| ' DROP NOT null'  as varchar(1024)) set_nul
					,cast( 'alter table '|| trim(rel_name) || ' add constraint '||left(trim(fld_name)||'_dummy',31) ||' check ('||trim(fld_name) ||' is not distinct from '||trim(fld_name)||')'  as varchar(1024)) set_chk
					,cast( 'alter table '|| trim(rel_name) || ' alter '||trim(fld_name)|| ' set default '||ins_default as varchar(1024)) set_def
					,cast( 'create descending index '||trim(replace(rel_name,'$','_'))||' on '||trim(rel_name)||'('||trim(fld_name)||')' as varchar(1024)) idx_tab
					,cast( 'alter table '|| trim(rel_name) || ' drop '||trim(fld_name) as varchar(1024)) kil_fld
					,cast( 'drop table '|| trim(rel_name) as varchar(1024)) kil_tab
					,iif( c1.fld_pos+1 = c1.fld_count, 1, 0) cmd_end
				from c1
				where fld_pos = 0
			
				UNION ALL
			
				select
					c1.rel_name
					,c1.fld_pos
					,c1.fld_name
					,c1.fld_base_type
					,c1.fld_nullable
					,c1.fld_count
					,c1.ins_default
					,c2.lok_stt || ', ' || trim(c1.fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ' from '||trim(c2.rel_name)||' rows 1 with lock;', '')
					,c2.ins_stt || ', ' || trim(c1.fld_name) || iif( c1.fld_pos+1 = c1.fld_count, ')', '')
					,trim(c2.ins_val) || ', ' 
					  || trim(iif(c1.fld_nullable=1, 'null', c1.ins_default)) 
					  || iif( c1.fld_pos+1 = c1.fld_count, ') returning rdb$db_key;', '')
					,trim(c2.del_stt) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.add_fld) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.set_nul) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.set_chk) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.set_def) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.idx_tab) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.kil_fld) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,trim(c2.kil_tab) || iif( c1.fld_pos+1 = c1.fld_count, ';', '')
					,iif( c1.fld_pos+1 = c1.fld_count, 1, 0) cmd_end
				from  c2 join c1 on c2.rel_name = c1.rel_name and c1.fld_pos = c2.fld_pos + 1
			)
			--select * from c2

			,c3 as (
				select
					c2.rel_name --, c2.ins_stt || ' ' ||c2.ins_val insert_statement, c2.upd_stt update_statement, c2.del_stt delete_statement
				   ,n.op
				   ,decode(
						n.op
						,'I' ,c2.ins_stt || ' ' ||c2.ins_val
						,'L' ,c2.lok_stt
						--,'U' ,c2.upd_stt
						,'D' ,c2.del_stt
						,'A' ,c2.add_fld
						,'N' ,c2.set_nul
						,'C' ,c2.set_chk
						,'F' ,c2.set_def
						,'X' ,c2.idx_tab
						,'K' ,c2.kil_fld
						,'Z' ,c2.kil_tab
					) vulnerable_expr
				   ,n.ord_pos
				   ,n.ret_dbkey
				from c2
				cross join(
					select 'I' as op, 10 as ord_pos, 1 as ret_dbkey from rdb$database -- DML, insert
					union all select 'L',       20,            0 from rdb$database -- DML, select WITH LOCK
					union all select 'D',       40,            1 from rdb$database -- DML, delete
					union all select 'A',       50,            0 from rdb$database -- DDL, add column
					union all select 'N',       60,            0 from rdb$database -- DDL, alter column set NULL flag
					union all select 'C',       65,            0 from rdb$database -- DDL, alter column add new constraint on it
					union all select 'F',       70,            0 from rdb$database -- DDL, alter column set DEFAULT value
					-- ### commented 11-04-2018: no need anymore because now one may to create index on system tables:
					-- ### union all select 'X',       75,            0 from rdb$database -- DDL, CREATE INDEX
					union all select 'K',       80,            0 from rdb$database -- DDL, drop column
					union all select 'Z',       90,            0 from rdb$database -- DDL, drop RDB$-table
				) n
				where cmd_end=1
				order by rel_name, n.ord_pos
			)
			,r1 as(
				-- Add statements which will try to drop constrains, starting from FK:
				select rc.rdb$relation_name rel_name, rc.rdb$constraint_name cnt_name, rc.rdb$constraint_type cnt_type
				from rdb$relation_constraints rc
				join rdb$relations rr on rc.rdb$relation_name = rr.rdb$relation_name
				where 
				rc.rdb$relation_name  not starting with 'MON$'
				and coalesce(rr.rdb$system_flag,0) is distinct from 0 --------------- ### S Y S T E M    t a b l e s  ###
				AND (RC.RDB$RELATION_NAME = rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') or rdb$get_context('USER_SESSION','DEBUG_RDB_TABLE') is null)

				order by decode(rc.rdb$constraint_type, 'FOREIGN KEY', 0, 1)
			)
			,r2 as (
				select
					 trim(r1.rel_name) as rel_name
					,'R' as op
					,'alter table '||trim(r1.rel_name)||' drop constraint '||trim(r1.cnt_name)||';' as vulnerable_expr
					,67 as ord_pos
					,0 as ret_dbkey
				from r1
			)
			--select * from r2

			,cu as (
				select
					rel_name
					,has_sys_flag
					,fld_pos
					,fld_name
					,fld_base_type
					,fld_nullable
					,fld_count
					,ins_default
					,cast( 'update ' || trim(rel_name) || ' t '
						   || ' set t.'||trim(fld_name)||' = '
						   || trim(iif(n.i=0, ins_default, 'null'))||' '
						   || trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)='||m.k, ''))
						   || ' rows 1'
						   || ' returning t.rdb$db_key'
						   || ';'
						   as varchar(1024)
						 ) upd_stt
					,trim(iif(has_sys_flag=1, 'where coalesce(rdb$system_flag,0)='||m.k, '')) dml_where
					,30 as ord_pos
					,1 as ret_dbkey
				from c1
				join (select 0 i from rdb$database union all select 1 from rdb$database) n
					 on n.i=0 or c1.fld_nullable=1 and n.i=1
				join (select 0 k from rdb$database union all select 1 from rdb$database) m
					 on m.k=0 or c1.has_sys_flag=1 and m.k=1
				-- core-4772: fields like rdb$procedure_source have to be updated AFTER all previous (except RDB$SYSTEM_FLAG - this is the LATEST):
				-- select 1 from rdb$database where 'RDB$TRIGGER_SOURCE' similar to upper( ascii_char(37) || '#_SOURCE' ) escape '#'; ==> must return 1
				order by rel_name, iif( trim( upper(fld_name) ) = upper('RDB$SYSTEM_FLAG'), 3, iif(  trim( upper(fld_name) ) similar to upper( ascii_char(37) || '#_SOURCE') escape '#', 2, 1) )

			)
			-- select * from cu

			select rel_name, ord_pos, ret_dbkey, 'U' as vulnerable_type, dml_where, upd_stt as vulnerable_expr
			from cu

			UNION ALL

			select rel_name, ord_pos, ret_dbkey, op, '' as dml_where, vulnerable_expr
			from c3

			UNION ALL

			select rel_name, ord_pos, ret_dbkey, op, '' as dml_where, vulnerable_expr
			from r2 -- alter table drop FK

			UNION ALL

			select 'RDB$GENERATORS', 99, 0, 'G', '', 'alter sequence '||trim(g.rdb$generator_name)||' restart with -1;'
			from rdb$generators g
			where g.rdb$system_flag = 1

			----------
			as cursor c
		do
			insert into vulnerable_on_sys_tables(  sys_table,   ord_pos,   ret_dbkey,   vulnerable_type,   vulnerable_expr,   dml_where)
										  values( c.rel_name, c.ord_pos, c.ret_dbkey, c.vulnerable_type, c.vulnerable_expr, c.dml_where);
	end

	^
	set term ;^

	commit;

	-- ###################################################################################
	--                       E X E C U T E     S T A T E M E N T S:
	-- A T T E M P T S    O F   D I R E C T    W R I T E   T O    S Y S T E M  T A B L E S
	-- ###################################################################################
	set term ^;
	create or alter procedure sp_run_vulnerable_expressions(
		a_usr type of column sec$users.sec$user_name
		,a_pwd varchar(20)
		,a_role varchar(20)
	) as
		declare v_dbname type of column mon$database.mon$database_name;
		declare v_actual_role varchar(20);
		declare v_dbkey char(8) character set octets;
	begin

		update vulnerable_on_sys_tables 
		set 
			vulnerable_gdscode = null
		   ,vulnerable_sqlcode = null
		   ,vulnerable_sqlstate = null
		   ,sys_dbkey = null
		   ,actual_role = null
		;

		v_dbname = 'localhost:' ||  rdb$get_context('SYSTEM','DB_NAME');

		execute statement 'select current_role from rdb$database'
			on external (v_dbname)
			as user upper(a_usr) password (a_pwd) role (a_role)
			into v_actual_role;

		for 
			select sys_table, vulnerable_expr, dml_where, vulnerable_type, ret_dbkey
			from vulnerable_on_sys_tables
			order by sys_table, ord_pos, id
			as cursor c
		do begin

				v_dbkey = null;
				if ( c.ret_dbkey = 1) then
					execute statement c.vulnerable_expr --  this statement ALWAYS should FAIL!
					on external (v_dbname)
					as user (a_usr) password (a_pwd) role (a_role)
					into v_dbkey;
				else
					execute statement c.vulnerable_expr -- this statement ALWAYS should FAIL!
					on external (v_dbname)
					as user (a_usr) password (a_pwd) role (a_role);

				-- this statement must NOT occur at all. If this record would be added then it means
				-- that system table can be DIRECTLY modified by user!
				update vulnerable_on_sys_tables set  
					vulnerable_gdscode = -1
					,vulnerable_sqlcode = -1
					,vulnerable_sqlstate = 'PASS!'
					,sys_dbkey = :v_dbkey
					,actual_role =  :v_actual_role
				where current of c;
			
		when any do
			begin
				update vulnerable_on_sys_tables 
				set  
					  vulnerable_gdscode = gdscode
					  ,vulnerable_sqlcode = sqlcode
					  ,vulnerable_sqlstate = sqlstate
					  ,sys_dbkey = null
					  ,actual_role = :v_actual_role
				where current of c;
			end
		end
	end
	^
	set term ;^
	commit;

	execute procedure sp_gen_expr_for_creating_fkeys;
	execute procedure sp_gen_expr_for_direct_change;
	-- result: table 'vulnerable_on_sys_tables ' will be filled up by ~1560 rows.
	commit;

    --                                    ||||||||||||||||||||||||||||
    -- ###################################|||  FB 4.0+, SS and SC  |||##############################
    --                                    ||||||||||||||||||||||||||||
    -- If we check SS or SC and ExtConnPoolLifeTime > 0 (config parameter FB 4.0+) then current
    -- DB (bugs.core_NNNN.fdb) will be 'captured' by firebird.exe process and fbt_run utility
    -- will not able to drop this database at the final point of test.
    -- Moreover, DB file will be hold until all activity in firebird.exe completed and AFTER this
    -- we have to wait for <ExtConnPoolLifeTime> seconds after it (discussion and small test see
    -- in the letter to hvlad and dimitr 13.10.2019 11:10).
    -- This means that one need to kill all connections to prevent from exception on cleanup phase:
    -- SQLCODE: -901 / lock time-out on wait transaction / object <this_test_DB> is in use
    -- #############################################################################################
    delete from mon$attachments where mon$attachment_id != current_connection;
    commit;    
