6
0
mirror of https://github.com/FirebirdSQL/firebird-qa.git synced 2025-01-22 21:43:06 +01:00
firebird-qa/tests/bugs/core_5808_test.py

164 lines
5.8 KiB
Python

#coding:utf-8
"""
ID: issue-6070
ISSUE: 6070
TITLE: Support backup of encrypted databases
DESCRIPTION:
Test creates two tables, adds data, and drops one of them (thus there are deallocated pages in the database).
Then it creates lot of sequences with different initial values and increments.
Database is encrypted after this step.
After this, we get initial content of: firebird.log, metadata('isql -x') and sequences value and store them
in variables fblog_1, meta_1 and sequ_1.
Then we run backup and restore, and validate database.
Further, we extract again metadata and sequences value of restored DB, and store them in fblog_2, meta_2 and sequ_2.
Difference between fblog_1 and fblog_2 must look like this: "Validation finished: 0 errors, 0 warnings, 0 fixed".
NO difference must be between meta_1 vs meta_2, and sequ_1 vs sequ_2
JIRA: CORE-5808
FBTEST: bugs.core_5808
NOTES:
[12.06.2022] pzotov
Checked on 4.0.1.2692 - both on Linux and Windows.
NB: duration on Linux ~40 s; on Windows ~22 s.
"""
import re
import time
import datetime as py_dt
from datetime import timedelta
from io import BytesIO
from difflib import unified_diff
import pytest
from firebird.qa import *
from firebird.driver import DatabaseError
from firebird.driver import SrvRestoreFlag, SrvRepairFlag
N_ROWS = 15
init_script = f"""
create table test1(s varchar(784) unique, b blob);
create table test2(s varchar(784) unique, b blob);
commit;
insert into test1(s) select lpad('',784,uuid_to_char(gen_uuid())) from (select 1 i from rdb$types rows {N_ROWS}),(select 1 i from rdb$types rows {N_ROWS});
insert into test2(s) select lpad('',784,uuid_to_char(gen_uuid())) from (select 1 i from rdb$types rows {N_ROWS});
update test1 set b = (select list( (select gen_uuid() from rdb$database) ) from (select 1 i from rdb$types rows {N_ROWS}),(select 1 i from rdb$types rows {N_ROWS}));
update test2 set b = (select list( (select gen_uuid() from rdb$database) ) from (select 1 i from rdb$types rows {N_ROWS}),(select 1 i from rdb$types rows {N_ROWS}));
commit;
drop table test1;
commit;
set term ^;
execute block as
declare n int = 16000;
declare i bigint = -2147483648;
declare b bigint = 9223372036854775807;
begin
while (n > 0) do
begin
execute statement 'create sequence gen_' || n || ' start with ' || (b-abs(i)) || ' increment by ' || (i+1) ;
n = n - 1;
i = i + 1;
end
end ^
set term ;^
commit;
"""
db = db_factory(init = init_script)
act = python_act('db')
MAX_ENCRYPT_DECRYPT_MS = 5000
ENCRYPTION_PLUGIN = 'fbSampleDbCrypt'
ENCRYPTION_KEY = 'Red'
@pytest.mark.encryption
@pytest.mark.version('>=4.0')
def test_1(act: Action, capsys):
encryption_finished = False
with act.db.connect() as con:
t1=py_dt.datetime.now()
d1 = t1-t1
sttm = f'alter database encrypt with "{ENCRYPTION_PLUGIN}" key "{ENCRYPTION_KEY}"'
try:
con.execute_immediate(sttm)
con.commit()
except DatabaseError as e:
print( e.__str__() )
while True:
t2=py_dt.datetime.now()
d1=t2-t1
if d1.seconds*1000 + d1.microseconds//1000 > MAX_ENCRYPT_DECRYPT_MS:
print('TIMEOUT EXPIRATION: encryption took {d1.seconds*1000 + d1.microseconds//1000} ms which exceeds limit = {MAX_ENCRYPT_DECRYPT_MS} ms.')
break
# Possible output:
# Database not encrypted
# Database encrypted, crypt thread not complete
act.isql(switches=['-q'], input = 'show database;', combine_output = True)
if 'Database encrypted' in act.stdout:
if 'not complete' in act.stdout:
pass
else:
encryption_finished = True
break
act.reset()
act.expected_stdout = ''
act.stdout = capsys.readouterr().out
assert act.clean_stdout == act.clean_expected_stdout
act.reset()
if encryption_finished:
# Extract metadata from initial DB
act.isql(switches=['-x'])
meta_1 = act.stdout
act.reset()
act.isql(switches=['-q'], input = 'show sequ;', combine_output = True)
sequ_1 = act.stdout
act.reset()
# Snippet from core-1725:
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.REPLACE)
# Get FB log before validation, run validation and get FB log after it:
fblog_1 = act.get_firebird_log()
srv.database.repair(database=act.db.db_path, flags=SrvRepairFlag.CORRUPTION_CHECK)
fblog_2 = act.get_firebird_log()
# Extract metadata from restored DB
act.isql(switches=['-x'])
meta_2 = act.stdout
act.reset()
act.isql(switches=['-q'], input = 'show sequ;', combine_output = True)
sequ_2 = act.stdout
act.reset()
diff_meta = ''.join(unified_diff(meta_1.splitlines(), meta_2.splitlines()))
p_diff = re.compile('Validation finished: \\d+ errors, \\d+ warnings, \\d+ fixed')
validation_result = ''
for line in unified_diff(fblog_1, fblog_2):
if line.startswith('+') and p_diff.search(line):
validation_result =line.strip().replace('\t', ' ')
break
assert diff_meta == ''
assert validation_result == '+ Validation finished: 0 errors, 0 warnings, 0 fixed'
assert sequ_1 == sequ_2