diff --git a/tests/bugs/core_0606_test.py b/tests/bugs/core_0606_test.py index 52bfaa93..738366e7 100644 --- a/tests/bugs/core_0606_test.py +++ b/tests/bugs/core_0606_test.py @@ -100,7 +100,7 @@ expected_stderr = """ -At block line: 3, col: 7 """ - +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action, cvc_user: User, for_role: Role, for_cvc_role: Role): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_1361_test.py b/tests/bugs/core_1361_test.py index ed8fe911..41ca10ae 100644 --- a/tests/bugs/core_1361_test.py +++ b/tests/bugs/core_1361_test.py @@ -146,6 +146,7 @@ expected_stdout = """ USING_INDEX 1 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_1606_test.py b/tests/bugs/core_1606_test.py index aca29362..0c737e25 100644 --- a/tests/bugs/core_1606_test.py +++ b/tests/bugs/core_1606_test.py @@ -120,6 +120,7 @@ expected_stdout = """ PID 1 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_1930_test.py b/tests/bugs/core_1930_test.py index 9194d6c3..49bb081c 100644 --- a/tests/bugs/core_1930_test.py +++ b/tests/bugs/core_1930_test.py @@ -209,6 +209,7 @@ expected_stderr_2 = """ -At block line """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_2(act_2: Action): act_2.expected_stderr = expected_stderr_2 diff --git a/tests/bugs/core_2018_test.py b/tests/bugs/core_2018_test.py index bd35e539..eeb1723e 100644 --- a/tests/bugs/core_2018_test.py +++ b/tests/bugs/core_2018_test.py @@ -119,6 +119,7 @@ expected_stdout = """ ATTACHES_I_CAN_SEE 11 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_2252_test.py b/tests/bugs/core_2252_test.py index a045fff2..61cd6660 100644 --- a/tests/bugs/core_2252_test.py +++ b/tests/bugs/core_2252_test.py @@ -77,6 +77,7 @@ expected_stderr = """ -At block line: 11, col: 9 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_2305_test.py b/tests/bugs/core_2305_test.py index 013b16d3..522fad94 100644 --- a/tests/bugs/core_2305_test.py +++ b/tests/bugs/core_2305_test.py @@ -110,6 +110,7 @@ expected_stdout = """ DISTINCT_STATEMENT_ID_THEY_SAW 1 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_2668_test.py b/tests/bugs/core_2668_test.py index f922eaac..e98a8921 100644 --- a/tests/bugs/core_2668_test.py +++ b/tests/bugs/core_2668_test.py @@ -63,6 +63,7 @@ select '-- shutdown me now --' from rdb$database; tmp_script = temp_file('work_script.sql') +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action, tmp_script: Path): tmp_script.write_text(test_script) diff --git a/tests/bugs/core_2731_test.py b/tests/bugs/core_2731_test.py index e0fd9efb..091e369c 100644 --- a/tests/bugs/core_2731_test.py +++ b/tests/bugs/core_2731_test.py @@ -85,6 +85,7 @@ expected_stderr = """ -At block line: 5, col: 5 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stderr = expected_stderr diff --git a/tests/bugs/core_2987_test.py b/tests/bugs/core_2987_test.py index e0e26b99..d6cf5907 100644 --- a/tests/bugs/core_2987_test.py +++ b/tests/bugs/core_2987_test.py @@ -152,6 +152,7 @@ expected_stdout = """ MEASURE_RESULT WINS >= 3.8x """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_3100_test.py b/tests/bugs/core_3100_test.py index 98a62c6b..39986d29 100644 --- a/tests/bugs/core_3100_test.py +++ b/tests/bugs/core_3100_test.py @@ -252,6 +252,7 @@ Statement failed, SQLSTATE = HY000 record not found for user: TMP$C3100B """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_3554_test.py b/tests/bugs/core_3554_test.py index 4a25115d..4c7ac629 100644 --- a/tests/bugs/core_3554_test.py +++ b/tests/bugs/core_3554_test.py @@ -99,6 +99,7 @@ expected_stderr = """ -At block line: 4, col: 7 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_3610_test.py b/tests/bugs/core_3610_test.py index dde3b409..c6fa76a1 100644 --- a/tests/bugs/core_3610_test.py +++ b/tests/bugs/core_3610_test.py @@ -80,6 +80,7 @@ expected_stderr = """ -At block line: 3, col: 9 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_3736_test.py b/tests/bugs/core_3736_test.py index a9918177..0ea3ed70 100644 --- a/tests/bugs/core_3736_test.py +++ b/tests/bugs/core_3736_test.py @@ -143,6 +143,7 @@ expected_stderr = """ 335544878 : concurrent transaction number is 806 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action, user_1_ro: User, user_1_ud: User): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4054_test.py b/tests/bugs/core_4054_test.py index c50a7f0e..29beea45 100644 --- a/tests/bugs/core_4054_test.py +++ b/tests/bugs/core_4054_test.py @@ -117,6 +117,7 @@ expected_stdout = """ WHAT_I_SEE 789654123 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4161_test.py b/tests/bugs/core_4161_test.py index fb3c8201..f5e4351a 100644 --- a/tests/bugs/core_4161_test.py +++ b/tests/bugs/core_4161_test.py @@ -142,6 +142,7 @@ expected_stdout = """ MINE_F01 300 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action, tmp_user: User): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4326_test.py b/tests/bugs/core_4326_test.py index c49125bc..e283ad7b 100644 --- a/tests/bugs/core_4326_test.py +++ b/tests/bugs/core_4326_test.py @@ -91,6 +91,7 @@ expected_stdout = """ LNAME Zeppelin """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action, tmp_user: User): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4396_test.py b/tests/bugs/core_4396_test.py index a930dc1d..37683f9c 100644 --- a/tests/bugs/core_4396_test.py +++ b/tests/bugs/core_4396_test.py @@ -73,6 +73,7 @@ expected_stdout = """ CNT 0 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4604_test.py b/tests/bugs/core_4604_test.py index 12e6d48f..78ff008f 100644 --- a/tests/bugs/core_4604_test.py +++ b/tests/bugs/core_4604_test.py @@ -91,6 +91,7 @@ expected_stdout = """ STR_SIZE 36 """ +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_4707_test.py b/tests/bugs/core_4707_test.py index fbf3be60..20f3ea96 100644 --- a/tests/bugs/core_4707_test.py +++ b/tests/bugs/core_4707_test.py @@ -68,6 +68,7 @@ expected_stdout = """ hang_script_file = temp_file('hang_script.sql') hang_output = temp_file('hang_script.out') +@pytest.mark.es_eds @pytest.mark.version('>=2.5.5') def test_1(act: Action, hang_script_file: Path, hang_output: Path, capsys, request): # Fializer for FB4 diff --git a/tests/bugs/core_4747_test.py b/tests/bugs/core_4747_test.py index 08ebe429..a7b781b1 100644 --- a/tests/bugs/core_4747_test.py +++ b/tests/bugs/core_4747_test.py @@ -35,6 +35,7 @@ substitutions=[ ('RUNNING_STT_ID[ ]+[0-9]+', 'RUNNING_STT_ID'), act = python_act('db', substitutions = substitutions) +@pytest.mark.es_eds @pytest.mark.version('>=3') def test_1(act: Action): diff --git a/tests/bugs/core_5225_test.py b/tests/bugs/core_5225_test.py index 9644e111..80c7debb 100644 --- a/tests/bugs/core_5225_test.py +++ b/tests/bugs/core_5225_test.py @@ -73,6 +73,7 @@ expected_stdout = """ WHOAMI_SRP TMP$C5225 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0.1') def test_1(act: Action, user_srp: User, user_leg: User): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_5229_test.py b/tests/bugs/core_5229_test.py index 030a8522..802d341d 100644 --- a/tests/bugs/core_5229_test.py +++ b/tests/bugs/core_5229_test.py @@ -30,6 +30,7 @@ expected_stdout = """ """ @pytest.mark.skip("FIXME: see notes") +@pytest.mark.es_eds @pytest.mark.version('>=3.0.1') def test_1(act: Action): sql_chk = f""" diff --git a/tests/bugs/core_5488_running_sttm_test.py b/tests/bugs/core_5488_running_sttm_test.py index 033bf751..eadc4003 100644 --- a/tests/bugs/core_5488_running_sttm_test.py +++ b/tests/bugs/core_5488_running_sttm_test.py @@ -185,6 +185,7 @@ expected_stderr = """ -Attachment level timeout expired. """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_5555_test.py b/tests/bugs/core_5555_test.py index 3c8824fa..c5f1b76c 100644 --- a/tests/bugs/core_5555_test.py +++ b/tests/bugs/core_5555_test.py @@ -87,6 +87,7 @@ expected_stdout = """ GDS_ON_SELECT_WITH_LOCK 335544336 """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0.3') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_5648_test.py b/tests/bugs/core_5648_test.py index 8d546c97..205d257b 100644 --- a/tests/bugs/core_5648_test.py +++ b/tests/bugs/core_5648_test.py @@ -40,6 +40,7 @@ expected_stdout = """ eds_script = temp_file('eds_script.sql') +@pytest.mark.es_eds @pytest.mark.version('>=3.0.3') def test_1(act: Action, eds_script: Path): eds_sql = f""" diff --git a/tests/bugs/core_5685_test.py b/tests/bugs/core_5685_test.py index 998dc02c..c53fd123 100644 --- a/tests/bugs/core_5685_test.py +++ b/tests/bugs/core_5685_test.py @@ -152,6 +152,7 @@ hang_stdout = temp_file('hang_script.out') hang_stderr = temp_file('hang_script.err') @pytest.mark.skipif(platform.system() != 'Windows', reason='FIXME: see notes') +@pytest.mark.es_eds @pytest.mark.version('>=3.0.3') def test_1(act: Action, hang_script: Path, hang_stdout: Path, hang_stderr: Path, capsys): diff --git a/tests/bugs/core_5704_test.py b/tests/bugs/core_5704_test.py index 38c27f95..4b8bccfe 100644 --- a/tests/bugs/core_5704_test.py +++ b/tests/bugs/core_5704_test.py @@ -44,6 +44,7 @@ eds_output = temp_file('eds_script.out') new_diff_file = temp_file('_new_diff_5704.tmp') new_main_file = temp_file('new_main_5704.tmp') +@pytest.mark.es_eds @pytest.mark.version('>=3.0.3') def test_1(act: Action, eds_script: Path, eds_output: Path, new_diff_file: Path, new_main_file: Path): diff --git a/tests/bugs/core_5898_test.py b/tests/bugs/core_5898_test.py index 8cb7770a..5879b2df 100644 --- a/tests/bugs/core_5898_test.py +++ b/tests/bugs/core_5898_test.py @@ -99,6 +99,7 @@ expected_stdout = """ WHAT_PROTOCOL_IM_USING """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0.4') def test_1(act: Action, tmp_user, tmp_role): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_5985_test.py b/tests/bugs/core_5985_test.py index 122bfe47..e9056066 100644 --- a/tests/bugs/core_5985_test.py +++ b/tests/bugs/core_5985_test.py @@ -85,6 +85,7 @@ expected_stdout = """ WHATS_MY_ROLE WORKER """ +@pytest.mark.es_eds @pytest.mark.version('>=3.0') def test_1(act: Action, user_foo, user_bar): act.expected_stdout = expected_stdout diff --git a/tests/bugs/core_5990_test.py b/tests/bugs/core_5990_test.py index e70a35bc..0d88ff3a 100644 --- a/tests/bugs/core_5990_test.py +++ b/tests/bugs/core_5990_test.py @@ -165,6 +165,7 @@ expected_stdout = """ DUP_CNT 5 """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action): if int(act.get_config('ExtConnPoolSize')) < 6 or int(act.get_config('ExtConnPoolLifeTime')) < 10: diff --git a/tests/bugs/core_6182_test.py b/tests/bugs/core_6182_test.py index 6169931c..0e85e364 100644 --- a/tests/bugs/core_6182_test.py +++ b/tests/bugs/core_6182_test.py @@ -105,7 +105,7 @@ def init_main_db(act: Action, eds_user: User): """ act.isql(switches=[], input=ddl_script) - +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action, db_a: Database, db_b: Database, db_c: Database, db_d: Database, eds_user: User, capsys): diff --git a/tests/bugs/gh_6886_test.py b/tests/bugs/gh_6886_test.py index 3ddc5f8c..292251bc 100644 --- a/tests/bugs/gh_6886_test.py +++ b/tests/bugs/gh_6886_test.py @@ -78,6 +78,7 @@ expected_stdout = """ Records affected: 0 """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0.1') def test_1(act: Action): act.expected_stdout = expected_stdout diff --git a/tests/bugs/gh_7350_test.py b/tests/bugs/gh_7350_test.py index e474e673..e4b6158b 100644 --- a/tests/bugs/gh_7350_test.py +++ b/tests/bugs/gh_7350_test.py @@ -448,6 +448,7 @@ expected_stdout = f""" ID=10 """ +@pytest.mark.es_eds @pytest.mark.version('>=5.0') def test_1(act: Action, capsys): diff --git a/tests/bugs/gh_7917_test-obj-in-use-on-drop-db.py b/tests/bugs/gh_7917_test-obj-in-use-on-drop-db.py new file mode 100644 index 00000000..ee66ad67 --- /dev/null +++ b/tests/bugs/gh_7917_test-obj-in-use-on-drop-db.py @@ -0,0 +1,419 @@ +#coding:utf-8 + +""" +ID: issue-7917 +ISSUE: https://github.com/FirebirdSQL/firebird/issues/7917 +TITLE: Hang in a case of error when sweep thread is attaching to database +DESCRIPTION: + Test uses preliminary created alias with KeyHolderPlugin that points to special configuration with name 'KH2'. + This configuration makes crypt plugin accept key ONLY from client app (in contrary to some other encryption-related tests). + DB-level triggers (ON CONNECT, ON DISCONNECT) are created for logging appropriate events (see table 'att_log'). + Then we run special SQL that uses autonomous transactions and ES/EDS. + This script will hang because of update-conflict after some number of transactions (see TX_NUMBER_BEFORE_HANG). + We run this script asynchronously, then reduce sweep interval to value SWP_INTERVAL_TO_CHECK that is less than TX_NUMBER_BEFORE_HANG, + change DB state to full shutdown and return it online. + At this point any connection to DB will fire AUTO SWEEP (normally this can be seen in firebird.log as 'Sweep is started by SWEEPER'). + We run ISQL that queries 'att_log' table and causes AUTO SWEEP. That ISQL has to normally detach from DB and we must see its results. +NOTES: + [28.12.2023] pzotov + 1. To make crypt plugin accept key only from client app, $FB_HOME/plugins.conf must contain following lines: + ================== + Plugin = KH2 { + Module = $(dir_plugins)/fbSampleKeyHolder + RegisterName = fbSampleKeyHolder + Config = KH2 + } + Config = KH2 { + Auto = false + } + ================== + QA-scenario (.bat and .sh) must add in advance content of $QA_ROOT/files/qa-plugins-supplement.conf to $FB_HOME/plugins.conf. + + 2. Demo-plugin (fbSampleKeyHolder) can transfer key over network only for default key which has no-name. + Because of this, command 'alter database encrypt with ' has no '... key ' tail. + See letter from Alex, 15.12.2023 16:16 + + 3. In case of regression caused by that bug, we have to be ready that FB will hang on this test! + + Great thanks to Alex for suggestions (discussion started 13.12.2023 13:18). + + Confirmed bug on 6.0.0.173. + Checked on 6.0.0.175, 5.0.0.1305, 4.0.5.3049 - but currenlyt only SuperServer works fine. + Classic has the same problem. Sent report to Alex, 28.12.2023 13:10. + + On 3.0.12.33726 error raises: + unsuccessful metadata update / -ALTER DATABASE failed / -Missing correct crypt key / -Plugin fbSampleDbCrypt: / -Crypt key not set + This problem not [yet] investogated. +""" +import os +import time +import datetime as py_dt +import subprocess +import locale +import re +from pathlib import Path + +import pytest +from firebird.qa import * +from firebird.driver import DatabaseError + +REQUIRED_ALIAS = 'tmp_gh_7917_alias' + +########################### +### 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 + +SWP_INTERVAL_TO_CHECK = 100 +TX_NUMBER_BEFORE_HANG = SWP_INTERVAL_TO_CHECK + 10 + +MAX_WAIT_FOR_ISQL_TERMINATE=11 + +db = db_factory(filename = '#' + REQUIRED_ALIAS, do_not_drop = True) + +act = python_act('db', substitutions = [('^((?!(ISQL|Attributes)).)*$', ''), ('[ \t]+', ' '), ('TCPv(4|6)$', 'TCP')]) +#act = python_act('db') + +tmp_sql_file = temp_file('tmp_7917.sql') +tmp_log_file = temp_file('tmp_7917.log') + +#----------------------------------------------------------------------- + +def run_encr_decr(act: Action, mode, max_wait_encr_thread_finish, capsys): + if mode == 'encrypt': + # See letter from Alex, 15.12.2023 16:16 demo-plugin can not transfer named key over network. + # Because of that, we have to use following command WITHOUT adding 'key "{ENCRYPTION_KEY}"': + # ::: NB ::: One need to be sure that $FB_HOME/plugins.conf contains following lines: + # Plugin = KH2 { + # Module = $(dir_plugins)/fbSampleKeyHolder + # RegisterName = fbSampleKeyHolder + # Config = KH2 + # } + # Config = KH2 { + # Auto = false + # } + # Otherwise error will raise: + # unsuccessful metadata update + # -ALTER DATABASE failed + # -Missing database encryption key for your attachment + # -Plugin fbSampleDbCrypt: + # -Crypt key not set + # + alter_db_sttm = f'alter database encrypt with "{ENCRYPTION_PLUGIN}"' + wait_for_state = 'Database encrypted' + elif mode == 'decrypt': + alter_db_sttm = 'alter database decrypt' + wait_for_state = 'Database not encrypted' + + + e_thread_started = False + e_thread_finished = False + with act.db.connect() as con: + + t1=py_dt.datetime.now() + d1 = t1-t1 + try: + con.execute_immediate(alter_db_sttm) + con.commit() + e_thread_started = True + except DatabaseError as e: + print( e.__str__() ) + + while e_thread_started: + t2=py_dt.datetime.now() + d1=t2-t1 + if d1.seconds*1000 + d1.microseconds//1000 > max_wait_encr_thread_finish: + print(f'TIMEOUT EXPIRATION. Mode="{mode}" took {d1.seconds*1000 + d1.microseconds//1000} ms which exceeds limit = {max_wait_encr_thread_finish} ms.') + break + + # Possible output: + # Database [not] encrypted + # Database encrypted, crypt thread not complete + act.isql(switches=['-q'], input = 'show database;', combine_output = True) + if wait_for_state in act.stdout: + if 'not complete' in act.stdout: + pass + else: + e_thread_finished = True + break + act.reset() + + act.expected_stdout = '' + act.stdout = capsys.readouterr().out + assert act.clean_stdout == act.clean_expected_stdout + act.reset() + + assert e_thread_finished + + +#----------------------------------------------------------------------- + +@pytest.mark.es_eds +@pytest.mark.encryption +@pytest.mark.version('>=4.0.5') +@pytest.mark.platform('Windows') +def test_1(act: Action, tmp_sql_file: Path, tmp_log_file: Path, capsys): + + ''' + with act.db.connect() as con: + if act.vars['server-arch'] == 'SuperServer': + pass + else: + pytest.skip("Currently fixed only for SuperServer. Temporary SKIPPED.") + ''' + + # Scan line-by-line through databases.conf, find line starting with REQUIRED_ALIAS and extract name of file that + # must be created in the $(dir_sampleDb)/qa/ folder. This name will be used further as target database (tmp_fdb). + # NOTE: we have to SKIP lines which are commented out, i.e. if they starts with '#': + p_required_alias_ptn = re.compile( '^(?!#)((^|\\s+)' + REQUIRED_ALIAS + ')\\s*=\\s*\\$\\(dir_sampleDb\\)/qa/', re.IGNORECASE ) + fname_in_dbconf = None + + with open(act.home_dir/'databases.conf', 'r') as f: + for line in f: + if p_required_alias_ptn.search(line): + # If databases.conf contains line like this: + # tmp_7598_alias = $(dir_sampleDb)/qa/tmp_gh_7598.fdb + # - then we extract filename: 'tmp_gh_7598.fdb' (see below): + fname_in_dbconf = Path(line.split('=')[1].strip()).name + break + + # if 'fname_in_dbconf' remains undefined here then propably REQUIRED_ALIAS not equals to specified in the databases.conf! + # + assert fname_in_dbconf + + #---------------------------------------------------------------- + + run_encr_decr(act, 'encrypt', MAX_WAITING_ENCR_FINISH, capsys) + + test_script = f""" + set bail on; + create table att_log( + att_prot varchar(15) + ,who_ami varchar(31) default current_user + ,att_id bigint default current_connection + ,trn_id bigint default current_transaction + ,evt_time time default 'now' + ,evt_name varchar(20) + ,swp_interval int + ); + set term ^; + create procedure sp_fill_dblevel_log(a_evt_name type of column att_log.evt_name) as + declare v_swp_interval int; + declare v_protocol type of column att_log.att_prot; + begin + insert into att_log( + att_prot + ,evt_name + ) values ( + rdb$get_context('SYSTEM', 'NETWORK_PROTOCOL') + ,:a_evt_name + ); + + end + ^ + create or alter trigger trg_detach on disconnect as + begin + execute procedure sp_fill_dblevel_log('detach'); + end + ^ + create or alter trigger trg_attach on connect as + begin + execute procedure sp_fill_dblevel_log('attach'); + end + ^ + set term ;^ + commit; + + recreate table test(s varchar(36) unique); + insert into test(s) values('LOCKED_FOR_PAUSE'); + commit; + + set transaction read committed WAIT; + + update test set s = s where s = 'LOCKED_FOR_PAUSE'; + + set term ^; + execute block as + declare n int = {TX_NUMBER_BEFORE_HANG}; + declare v_role varchar(31); + begin + while (n > 0) do + in autonomous transaction do + insert into test(s) values( rpad('', 36, uuid_to_char(gen_uuid()) ) ) + returning :n-1 into n; + + v_role = left(replace( uuid_to_char(gen_uuid()), '-', ''), 31); + + begin + execute statement ('update /* ES/EDS */ test set s = s where s = ?') ('LOCKED_FOR_PAUSE') + on external + 'localhost:' || rdb$get_context('SYSTEM', 'DB_NAME') + as user 'SYSDBA' password 'masterkey' role v_role + with autonomous transaction; + when any do + begin + end + end + + end + ^ + set term ;^ + set heading off; + select '-- shutdown me now --' from rdb$database; + """ + + tmp_sql_file.write_text(test_script) + + #---------------------------------------------------------------- + + # Reduce sweep interval to small value (that must be less than SQL_HANG_AFTER_TX_CNT): + # + act.gfix(switches=['-h', f'{SWP_INTERVAL_TO_CHECK}', act.db.dsn], combine_output = True, io_enc = locale.getpreferredencoding()) + + with open(tmp_log_file, 'w') as f: + # Launch ISQL which will hang because update conflict. + # This ISQl will be 'self-terminated' further because we will change DB state to full shutdown: + # + p_handed_isql = subprocess.Popen([act.vars['isql'], '-nod', '-i', str(tmp_sql_file), + '-user', act.db.user, + '-password', act.db.password, act.db.dsn], + stdout = f, + stderr = subprocess.STDOUT) + + # Let ISQL time to establish connection and fall in hanging state: + time.sleep(3) + + try: + act.gfix(switches=['-shut', 'full', '-force', '0', act.db.dsn], combine_output = True, io_enc = locale.getpreferredencoding()) + finally: + p_handed_isql.terminate() + + p_handed_isql.wait(MAX_WAIT_FOR_ISQL_TERMINATE) + if p_handed_isql.poll() is None: + print(f'Hanged ISQL process WAS NOT terminated in {MAX_WAIT_FOR_ISQL_TERMINATE} second(s).!') + else: + print(f'Hanged ISQL process terminated with retcode = {p_handed_isql.poll()}') + + # Result: log of hanged ISQL must contain now: + # Statement failed, SQLSTATE = 08003 + # connection shutdown + # -Database is shutdown. + + act.gfix(switches=['-online', act.db.dsn], combine_output = True, io_enc = locale.getpreferredencoding()) + assert act.clean_stdout == '' + act.reset() + + # Must show: Attributes encrypted, plugin {ENCRYPTION_PLUGIN} - without 'shutdown'! + act.gstat(switches=['-h']) + print(act.stdout) + + #act.stdout = capsys.readouterr().out + #assert act.clean_stdout == act.clean_expected_stdout + #act.reset() + + + #---------------------------------------------------------------- + + TEST_QUERY = 'select att_prot,who_ami,evt_name from att_log order by trn_id' + final_sql = f""" + set count on; + set list on; + set echo on; + {TEST_QUERY}; + commit; + connect '{act.db.dsn}'; + drop database; + quit; + """ + tmp_sql_file.write_text(final_sql) + + with open(tmp_log_file, 'w') as f: + # Explained by Alex, letter 13-dec-2023 13:18. + # Following ISQL will create attach that provokes AUTO SWEEP (because Next - OST now greater than SWP_INTERVAL_TO_CHECK). + # Problem raised when other attachments were prohibited to use encryption key (and this is default behaviour). + # Before fix, SWEEEP was not allowed to use key from this ISQL-attachment. + # Following message was added in firebird.log: "Automatic sweep error /Missing database encryption key for your attachment" + # But despite problem with establishing connection by SWEEP, its thread already created appropriate lock at that point. + # As result, engine remained in wrong state after this: existied attachments could not be closed. + # Also, FB process could not be normally stopped. + + MAX_WAIT_AUTO_SWEEP_FINISH = 3 + p_chk_sql = subprocess.Popen( [ act.vars['isql'], + '-nod', '-i', str(tmp_sql_file), + '-user', act.db.user, + '-password', act.db.password, + act.db.dsn + ], + stdout = f, + stderr = subprocess.STDOUT, + ) + + # If the process does not terminate after timeout seconds, raise a TimeoutExpired exception. + # It is safe to catch this exception and retry the wait. + try: + p_chk_sql.wait(timeout = MAX_WAIT_AUTO_SWEEP_FINISH) + except subprocess.TimeoutExpired as e: + print(f'Could not obtain result for {MAX_WAIT_AUTO_SWEEP_FINISH} seconds:') + print(e.__str__()) + + p_chk_sql.terminate() + p_chk_sql.wait(MAX_WAIT_FOR_ISQL_TERMINATE) + + # Check if child process has terminated. Set and return returncode attribute. Otherwise, returns None. + if p_chk_sql.poll() is None: + print(f'Final ISQL process WAS NOT terminated in {MAX_WAIT_FOR_ISQL_TERMINATE} second(s).!') + else: + print(f'Final ISQL process terminated') + #print(f'Final ISQL process terminated with retcode = {p_chk_sql.poll()}') + + + # do NOT put here this: + #run_encr_decr(act, 'decrypt', MAX_WAITING_ENCR_FINISH, capsys) + # - otherwise pytest will not return control + + with open(tmp_log_file, 'r') as f: + for line in f: + if line.strip(): + print('final ISQL log:',line) + + act.expected_stdout = f""" + Hanged ISQL process terminated with retcode = 1 + Attributes encrypted, plugin {ENCRYPTION_PLUGIN} + + Final ISQL process terminated + final ISQL log: {TEST_QUERY}; + final ISQL log: ATT_PROT TCP + final ISQL log: WHO_AMI {act.db.user.upper()} + final ISQL log: EVT_NAME attach + + final ISQL log: ATT_PROT TCP + final ISQL log: WHO_AMI {act.db.user.upper()} + final ISQL log: EVT_NAME detach + + final ISQL log: Records affected: 2 + + final ISQL log: commit; + final ISQL log: drop database; + final ISQL log: quit; + """ + + act.stdout = capsys.readouterr().out + assert act.clean_stdout == act.clean_expected_stdout + act.reset() + + # NB! We have to decrypt database now. Otherwise teardown will fail with: + # firebird.driver.types.DatabaseError: Missing database encryption key for your attachment + # -Plugin fbSampleDbCrypt: + # -Crypt key not set + #run_encr_decr(act, 'decrypt', MAX_WAITING_ENCR_FINISH, capsys) diff --git a/tests/bugs/gh_7917_test.py b/tests/bugs/gh_7917_test.py index cfa692b4..7413ab15 100644 --- a/tests/bugs/gh_7917_test.py +++ b/tests/bugs/gh_7917_test.py @@ -166,6 +166,7 @@ def run_encr_decr(act: Action, mode, max_wait_encr_thread_finish, capsys): #----------------------------------------------------------------------- +@pytest.mark.es_eds @pytest.mark.encryption @pytest.mark.version('>=4.0.5') def test_1(act: Action, tmp_sql_file: Path, tmp_log_file: Path, tmp_gstat_log: Path, capsys): diff --git a/tests/bugs/gh_8077_test.py b/tests/bugs/gh_8077_test.py index e9ee4e6d..3ae6a0f5 100644 --- a/tests/bugs/gh_8077_test.py +++ b/tests/bugs/gh_8077_test.py @@ -70,6 +70,7 @@ STOP_RECURSIVE_ES_AFTER_ITER = 101 #-------------------------------------------------------------------- +@pytest.mark.es_eds @pytest.mark.version('>=4.0.5') def test_1(act: Action, tmp_sql: Path, tmp_log: Path, capsys): diff --git a/tests/functional/session/test_ext_conn_pool_01.py b/tests/functional/session/test_ext_conn_pool_01.py index e0f80695..8cadd802 100644 --- a/tests/functional/session/test_ext_conn_pool_01.py +++ b/tests/functional/session/test_ext_conn_pool_01.py @@ -73,6 +73,7 @@ ADD_DELAY_FOR_RARE = 2 # ITER_LOOP_CNT = 3 +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action, tmp_user_freq: User, tmp_user_rare: User, tmp_cleaner_role: Role, capsys): @@ -244,14 +245,6 @@ def test_1(act: Action, tmp_user_freq: User, tmp_user_rare: User, tmp_cleaner_ro commit; ''' - ''' - print(sql_init) - act.expected_stdout = '' - act.stdout = capsys.readouterr().out - assert act.clean_stdout == act.clean_expected_stdout - act.reset() - ''' - act.expected_stdout = '' act.isql(switches = ['-q'], input = sql_init, combine_output = True, io_enc = locale.getpreferredencoding()) assert act.clean_stdout == act.clean_expected_stdout diff --git a/tests/functional/syspriv/test_access_shutdown_database.py b/tests/functional/syspriv/test_access_shutdown_database.py index c2eb1e31..554516b5 100644 --- a/tests/functional/syspriv/test_access_shutdown_database.py +++ b/tests/functional/syspriv/test_access_shutdown_database.py @@ -62,6 +62,7 @@ expected_stdout_isql = """ -At block line """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action, tmp_user: User, tmp_role:Role, capsys): diff --git a/tests/functional/syspriv/test_monitor_any_attachment.py b/tests/functional/syspriv/test_monitor_any_attachment.py index 0f26491c..87d41696 100644 --- a/tests/functional/syspriv/test_monitor_any_attachment.py +++ b/tests/functional/syspriv/test_monitor_any_attachment.py @@ -139,6 +139,7 @@ expected_stdout = """ Records affected: 1 """ +@pytest.mark.es_eds @pytest.mark.version('>=4.0') def test_1(act: Action, test_user, test_role): act.expected_stdout = expected_stdout