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

166 lines
5.8 KiB
Python
Raw Normal View History

#coding:utf-8
"""
ID: issue-6947
ISSUE: 6947
TITLE: Query to mon$ tables does not return data when the encryption/decryption thread is running
DESCRIPTION:
Test creates table with wide indexed column and add some data to it.
Volume of data must be big enough so that the encryption thread will not complete instantly.
Then 'ALTER DATABASE ENCRYPT...' is issued and we start loop 'select mon$crypt_page from mon$database'.
This loop must return NOT LESS than <MIN_DISTINCT_ENCRYPTED_PAGES> values greater than zero (i.e. it must
show progress of this process; zero values mean that encryption either did not start or already finished).
On each iteration we also check whether encryption completed (use ISQL call with 'show database' command)
or may be it is too slow and we have to cancel loop because of expiration <MAX_WAITING_ENCR_FINISH> limit.
When this loop is finished, we check number of elements in the encrypted_pages_set (unique values returned
by query from mon$database) and show outcome message.
Confirmed bug on 5.0.0.219 (Windows), 5.0.0.236 (Linux).
Checked on 5.0.0.240 (Windows; SS/Cs), 5.0.0.241 (Linux; SS/CS).
FBTEST: bugs.core_6947
NOTES:
[20.06.2022] pzotov
Settings for encryption are taken from act.files_dir/'test_config.ini' file.
Checked on 5.0.0.509 - both Windows and Linux.
[11.03.2023] pzotov
Marked as SKIPPED because covered by core_6048_test.
Probably will be deleted soon.
"""
import os
import datetime as py_dt
from datetime import timedelta
import time
import pytest
from firebird.qa import *
from firebird.driver import ShutdownMode, ShutdownMethod, DatabaseError
###########################
### S E T T I N G S ###
###########################
# QA_GLOBALS -- dict, is defined in qa/plugin.py, obtain settings
# from act.files_dir/'test_config.ini':
enc_settings = QA_GLOBALS['encryption']
# ACHTUNG: this must be carefully tuned on every new host:
#
MAX_WAITING_ENCR_FINISH = int(enc_settings['MAX_WAIT_FOR_ENCR_FINISH_WIN' if os.name == 'nt' else 'MAX_WAIT_FOR_ENCR_FINISH_NIX'])
assert MAX_WAITING_ENCR_FINISH > 0
ENCRYPTION_PLUGIN = enc_settings['encryption_plugin'] # fbSampleDbCrypt
ENCRYPTION_KEY = enc_settings['encryption_key'] # Red
FLD_LEN = 500
N_ROWS = 50000
MIN_DISTINCT_ENCRYPTED_PAGES = 3
init_ddl = f"""
recreate table test(s varchar({FLD_LEN}));
commit;
set term ^;
execute block as
declare n int = {N_ROWS};
begin
while (n>0) do
begin
insert into test(s) values(lpad('', {FLD_LEN}, uuid_to_char(gen_uuid())));
n = n - 1;
end
end
^
-- for debug, trace must be started with log_proc = true:
create procedure sp_tmp(a_page int) as
begin
-- nop --
end
^
set term ;^
commit;
create index test_s on test(s);
"""
db = db_factory(init = init_ddl)
act = python_act('db', substitutions=[('[ \t]+', ' ')])
expected_stdout = f"Expected: query to mon$database returned not less then {MIN_DISTINCT_ENCRYPTED_PAGES} encrypted pages."
@pytest.mark.encryption
@pytest.mark.version('>=5.0')
@pytest.mark.skip('Covered by bugs/core_6048_test')
def test_1(act: Action, capsys):
act.db.set_sync_write() # here we want DB be encrypted for some *valuable* time
encryption_started = False
encryption_finished = False
encrypted_pages_set = set()
with act.db.connect() as con:
t1=py_dt.datetime.now()
sttm = f'alter database encrypt with "{ENCRYPTION_PLUGIN}" key "{ENCRYPTION_KEY}"'
try:
con.execute_immediate(sttm)
con.commit()
encryption_started = True
except DatabaseError as e:
# -ALTER DATABASE failed
# -Crypt plugin fbSampleDbCrypt failed to load
# ==> no sense to do anything else, encryption_started remains False.
print( e.__str__() )
cur = con.cursor()
cu2 = con.cursor()
ps = cur.prepare('select mon$crypt_page from mon$database')
while encryption_started:
t2=py_dt.datetime.now()
d1=t2-t1
if d1.seconds*1000 + d1.microseconds//1000 > MAX_WAITING_ENCR_FINISH:
print(f'TIMEOUT EXPIRATION: encryption took {d1.seconds*1000 + d1.microseconds//1000} ms which exceeds limit = {MAX_WAITING_ENCR_FINISH} ms.')
break
cur.execute(ps)
p = cur.fetchone()[0]
cu2.callproc('sp_tmp', [ p, ] )
con.commit()
if p > 0:
encrypted_pages_set.add(p)
if len(encrypted_pages_set) >= MIN_DISTINCT_ENCRYPTED_PAGES:
# We got enough data from mon$database to conclude that encryption is in PROGRESS.
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()
if encryption_started:
if len(encrypted_pages_set) >= MIN_DISTINCT_ENCRYPTED_PAGES:
print(expected_stdout)
elif len(encrypted_pages_set) > 0:
print(f'PROBABLY ERROR OR TOO SLOW ENCRYPTION: found {len(encrypted_pages_set)} encrypted pages, minimal expected: {MIN_DISTINCT_ENCRYPTED_PAGES}')
else:
print(f'ERROR: query to mon$database was blocked during encryption.')
else:
print('ERROR: nothing was tested because encryption did not start.')
act.expected_stdout = expected_stdout
act.stdout = capsys.readouterr().out
assert act.clean_stdout == act.clean_expected_stdout