mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 13:33:07 +01:00
Added/Updated tests\bugs\core_1725_test.py: fully re-implemented - see notes.
This commit is contained in:
parent
df802f3bfc
commit
38bdbb14ef
@ -5,17 +5,57 @@ ID: issue-2149
|
||||
ISSUE: 2149
|
||||
TITLE: Unable to restore a database with inactive indices if any SP/trigger contains an explicit plan
|
||||
DESCRIPTION:
|
||||
We create table and indices for it.
|
||||
Then we create trigger for this table, view, procedure, function and package - and all of them have DDL
|
||||
which explicitly uses 'TEST ORDER <index>' in execution plan.
|
||||
Such database then backed up and restored with command switch '-i(nactive)'.
|
||||
Restore is logged and we check that this log does not contain 'gbak:error' message (and eventually completes OK).
|
||||
Restored database must contain all created DB objects, i.e. we must have ability to explicitly specify them in SQL.
|
||||
Table trigger (containing explicit PLAN clause in its DDL) also must exist and remain active.
|
||||
|
||||
Before this bug was fixed:
|
||||
1) log of restore contained:
|
||||
gbak: ERROR:Error while parsing function FN_WORKER's BLR
|
||||
gbak: ERROR: index TEST_X cannot be used in the specified plan
|
||||
2) restored database had NO indices that were explicitly specified in any DDL and any attempt to use appropriate
|
||||
DB object failed with SQLSTATE = 42S02/39000/42000 ('Table/Procedure/Function} unknown').
|
||||
|
||||
JIRA: CORE-1725
|
||||
FBTEST: bugs.core_1725
|
||||
NOTES:
|
||||
[28.10.2024] pzotov
|
||||
1. Test fully re-implemented.
|
||||
We do NOT extract metadata before and after restore (in order to compare it):
|
||||
in FB 6.x 'gbak -i' leads to 'create INACTIVE index ...' statements in generated SQL
|
||||
(see https://github.com/FirebirdSQL/firebird/issues/8091 - "Ability to create an inactive index").
|
||||
|
||||
Comparison of metadata that was before and after restore has no much sense.
|
||||
Rather, we have to check SQL/DML that attempt to use DB object which DDL contain
|
||||
explicitly specified execution plan.
|
||||
All such actions must raise error related to invalid BLR, but *not* error about missing DB object.
|
||||
|
||||
BTW: it looks strange that such messages contain "-there is no index TEST_X for table TEST".
|
||||
Such index definitely DOES exist but it is inactive.
|
||||
|
||||
2. Bug existed up to 17-jan-2019.
|
||||
It was fixed by commits related to other issues, namely:
|
||||
3.x: a74130019af89012cc1e04ba18bbc9c4a69e1a5d // 17.01.2019
|
||||
4.x: fea7c61d9741dc142fa020bf3aa93af7e52e2002 // 17.01.2019
|
||||
5.x: fea7c61d9741dc142fa020bf3aa93af7e52e2002 // 18.01.2019
|
||||
("Attempted to fix CORE-2440, CORE-5118 and CORE-5900 together (expression indices contain NULL keys after restore).")
|
||||
|
||||
Checked on:
|
||||
6.0.0.511-c4bc943; 5.0.2.1547-1e08f5e; 4.0.0.1384-fea7c61 (17-jan-2019, just after fix); 3.0.13.33793-3e62713
|
||||
"""
|
||||
|
||||
import locale
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from difflib import unified_diff
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from firebird.qa import *
|
||||
from firebird.driver import SrvRestoreFlag, SrvRepairFlag
|
||||
from io import BytesIO
|
||||
from difflib import unified_diff
|
||||
|
||||
substitutions_1 = [('[ \t]+', ' ')]
|
||||
|
||||
init_script = """
|
||||
set bail on;
|
||||
@ -28,7 +68,6 @@ create or alter function fn_init returns int as begin end;
|
||||
create or alter function fn_main returns int as begin end;
|
||||
create or alter function fn_worker returns int as begin end;
|
||||
|
||||
|
||||
create table test(id int primary key, x int, y int);
|
||||
create index test_x on test(x);
|
||||
create descending index test_y on test(y);
|
||||
@ -155,8 +194,7 @@ begin
|
||||
|
||||
end
|
||||
^
|
||||
|
||||
create or alter trigger trg_attach active on connect position 0 as
|
||||
create or alter trigger test_bi for test active before insert position 0 as
|
||||
declare c int;
|
||||
begin
|
||||
if ( rdb$get_context('USER_SESSION','INITIAL_DDL') is null ) then
|
||||
@ -177,41 +215,180 @@ set term ;^
|
||||
commit;
|
||||
"""
|
||||
|
||||
db = db_factory(init=init_script)
|
||||
substitutions = [('[ \t]+', ' '), ('(-)?invalid request BLR at offset \\d+', 'invalid request BLR at offset')]
|
||||
|
||||
act = python_act('db')
|
||||
db = db_factory(init = init_script)
|
||||
act = python_act('db', substitutions = substitutions)
|
||||
|
||||
tmp_fbk= temp_file('tmp_core_1725.fbk')
|
||||
tmp_fdb = temp_file('tmp_core_1725.fdb')
|
||||
|
||||
@pytest.mark.version('>=3.0.6')
|
||||
def test_1(act: Action):
|
||||
# Extract metadata from initial DB
|
||||
act.isql(switches=['-nod', '-x'])
|
||||
meta_1 = act.stdout
|
||||
def test_1(act: Action, tmp_fbk: Path, tmp_fdb: Path, capsys):
|
||||
|
||||
outcomes_map = defaultdict(str)
|
||||
|
||||
act.gbak(switches=['-b', act.db.dsn, str(tmp_fbk)])
|
||||
|
||||
# restore _WITHOUT_ building indices:
|
||||
act.gbak(switches=['-rep', '-i', '-v', str(tmp_fbk), str(tmp_fdb) ], combine_output = True, io_enc = locale.getpreferredencoding())
|
||||
|
||||
watching_patterns = [re.compile(x, re.IGNORECASE) for x in (r'gbak:\s?ERROR(:)?\s?', r'gbak:finis.*\s+going home', r'gbak:adjust.*\s+flags')]
|
||||
|
||||
for line in act.clean_stdout.splitlines():
|
||||
for p in watching_patterns:
|
||||
if p.search(line):
|
||||
outcomes_map['restore_log'] += line+'\n'
|
||||
act.reset()
|
||||
# backup + restore _WITHOUT_ building indices:
|
||||
backup = BytesIO()
|
||||
with act.connect_server() as srv:
|
||||
srv.database.local_backup(database=act.db.db_path, backup_stream=backup)
|
||||
backup.seek(0)
|
||||
srv.database.local_restore(backup_stream=backup, database=act.db.db_path,
|
||||
flags=SrvRestoreFlag.DEACTIVATE_IDX | SrvRestoreFlag.REPLACE)
|
||||
# Get FB log before validation, run validation and get FB log after it:
|
||||
log_before = act.get_firebird_log()
|
||||
srv.database.repair(database=act.db.db_path, flags=SrvRepairFlag.CORRUPTION_CHECK)
|
||||
#act.gfix(switches=['-v', '-full', act.db.dsn])
|
||||
log_after = act.get_firebird_log()
|
||||
# Extract metadata from restored DB
|
||||
act.isql(switches=['-nod', '-x'])
|
||||
meta_2 = act.stdout
|
||||
|
||||
###########################################################################
|
||||
|
||||
check_metadata = """
|
||||
set list on;
|
||||
set count on;
|
||||
|
||||
select ri.rdb$index_name, ri.rdb$index_inactive from rdb$indices ri where ri.rdb$relation_name = upper('test') and ri.rdb$index_name starting with upper('test');
|
||||
|
||||
select p.rdb$package_name, p.rdb$procedure_name as sp_name, p.rdb$valid_blr as sp_valid_blr
|
||||
from rdb$procedures p
|
||||
where p.rdb$system_flag is distinct from 1
|
||||
order by p.rdb$package_name, p.rdb$procedure_name
|
||||
;
|
||||
|
||||
select f.rdb$package_name, f.rdb$function_name as fn_name, f.rdb$valid_blr as fn_valid_blr
|
||||
from rdb$functions f
|
||||
where f.rdb$system_flag is distinct from 1
|
||||
order by f.rdb$package_name, f.rdb$function_name
|
||||
;
|
||||
|
||||
select rt.rdb$trigger_name, rt.rdb$trigger_inactive, rt.rdb$valid_blr as tg_valid_blr
|
||||
from rdb$triggers rt
|
||||
where
|
||||
rt.rdb$system_flag is distinct from 1 and
|
||||
rt.rdb$relation_name = upper('test')
|
||||
;
|
||||
|
||||
set count off;
|
||||
"""
|
||||
act.isql(switches=['-nod', '-q', str(tmp_fdb)], input = check_metadata, credentials = True, charset = 'utf8', connect_db = False, combine_output = True, io_enc = locale.getpreferredencoding())
|
||||
for line in act.clean_stdout.splitlines():
|
||||
outcomes_map['check_metadata'] += line+'\n'
|
||||
act.reset()
|
||||
|
||||
###########################################################################
|
||||
|
||||
check_avail_db_objects = """
|
||||
set list on;
|
||||
set echo on;
|
||||
select * from v_worker;
|
||||
|
||||
execute procedure sp_main;
|
||||
|
||||
select fn_main() from rdb$database;
|
||||
|
||||
execute procedure pg_test.pg_sp_worker;
|
||||
|
||||
select pg_test.pg_fn_worker() from rdb$database;
|
||||
|
||||
insert into test(id, x, y) values(-1, -1, -1) returning id, x, y;
|
||||
"""
|
||||
act.isql(switches=['-nod', '-q', str(tmp_fdb)], input = check_avail_db_objects, credentials = True, charset = 'utf8', connect_db = False, combine_output = True, io_enc = locale.getpreferredencoding())
|
||||
|
||||
for line in act.clean_stdout.splitlines():
|
||||
outcomes_map['check_avail_db_objects'] += line+'\n'
|
||||
act.reset()
|
||||
|
||||
for k,v in outcomes_map.items():
|
||||
print(k)
|
||||
for p in v.splitlines():
|
||||
print(p)
|
||||
print('')
|
||||
|
||||
###########################################################################
|
||||
|
||||
act.expected_stdout = """
|
||||
restore_log
|
||||
gbak:finishing, closing, and going home
|
||||
gbak:adjusting the ONLINE and FORCED WRITES flags
|
||||
|
||||
check_metadata
|
||||
RDB$INDEX_NAME TEST_X
|
||||
RDB$INDEX_INACTIVE 1
|
||||
RDB$INDEX_NAME TEST_Y
|
||||
RDB$INDEX_INACTIVE 1
|
||||
Records affected: 2
|
||||
|
||||
RDB$PACKAGE_NAME <null>
|
||||
SP_NAME SP_INIT
|
||||
SP_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME <null>
|
||||
SP_NAME SP_MAIN
|
||||
SP_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME <null>
|
||||
SP_NAME SP_WORKER
|
||||
SP_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME PG_TEST
|
||||
SP_NAME PG_SP_WORKER
|
||||
SP_VALID_BLR 1
|
||||
Records affected: 4
|
||||
|
||||
RDB$PACKAGE_NAME <null>
|
||||
FN_NAME FN_INIT
|
||||
FN_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME <null>
|
||||
FN_NAME FN_MAIN
|
||||
FN_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME <null>
|
||||
FN_NAME FN_WORKER
|
||||
FN_VALID_BLR 1
|
||||
RDB$PACKAGE_NAME PG_TEST
|
||||
FN_NAME PG_FN_WORKER
|
||||
FN_VALID_BLR 1
|
||||
Records affected: 4
|
||||
|
||||
RDB$TRIGGER_NAME TEST_BI
|
||||
RDB$TRIGGER_INACTIVE 0
|
||||
TG_VALID_BLR 1
|
||||
Records affected: 1
|
||||
|
||||
check_avail_db_objects
|
||||
select * from v_worker;
|
||||
Statement failed, SQLSTATE = 42000
|
||||
invalid request BLR at offset 35
|
||||
-there is no index TEST_Y for table TEST
|
||||
|
||||
execute procedure sp_main;
|
||||
Statement failed, SQLSTATE = 2F000
|
||||
Error while parsing procedure SP_MAIN's BLR
|
||||
-Error while parsing procedure SP_WORKER's BLR
|
||||
-invalid request BLR at offset 66
|
||||
-there is no index TEST_X for table TEST
|
||||
|
||||
select fn_main() from rdb$database;
|
||||
Statement failed, SQLSTATE = 2F000
|
||||
Error while parsing function FN_MAIN's BLR
|
||||
-Error while parsing function FN_WORKER's BLR
|
||||
-invalid request BLR at offset 72
|
||||
-there is no index TEST_X for table TEST
|
||||
|
||||
execute procedure pg_test.pg_sp_worker;
|
||||
Statement failed, SQLSTATE = 2F000
|
||||
Error while parsing procedure PG_TEST.PG_SP_WORKER's BLR
|
||||
-invalid request BLR at offset 66
|
||||
-there is no index TEST_X for table TEST
|
||||
|
||||
select pg_test.pg_fn_worker() from rdb$database;
|
||||
Statement failed, SQLSTATE = 2F000
|
||||
Error while parsing function PG_TEST.PG_FN_WORKER's BLR
|
||||
-invalid request BLR at offset 72
|
||||
-there is no index TEST_X for table TEST
|
||||
|
||||
insert into test(id, x, y) values(-1, -1, -1) returning id, x, y;
|
||||
Statement failed, SQLSTATE = 42000
|
||||
invalid request BLR at offset
|
||||
-there is no index TEST_X for table TEST
|
||||
"""
|
||||
|
||||
act.stdout = capsys.readouterr().out
|
||||
assert act.clean_stdout == act.clean_expected_stdout
|
||||
act.reset()
|
||||
# Restore with indices. This is necessary to drop the database safely otherwise connect
|
||||
# to drop will fail in test treadown as connect trigger referes to index tat was not activated
|
||||
with act.connect_server() as srv:
|
||||
backup.seek(0)
|
||||
srv.database.local_restore(backup_stream=backup, database=act.db.db_path,
|
||||
flags=SrvRestoreFlag.REPLACE)
|
||||
#
|
||||
diff_meta = ''.join(unified_diff(meta_1.splitlines(), meta_2.splitlines()))
|
||||
diff_log = [line for line in unified_diff(log_before, log_after) if line.startswith('+') and 'Validation finished:' in line]
|
||||
# Checks
|
||||
assert diff_meta == ''
|
||||
assert diff_log == ['+\tValidation finished: 0 errors, 0 warnings, 0 fixed\n']
|
||||
|
Loading…
Reference in New Issue
Block a user