From b0d794887df4a15d52be097f0451be958c7c2256 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:31:40 +0300 Subject: [PATCH 01/28] Added/Updated tests\bugs\gh_7904_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_7904_test.py | 39 ++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/bugs/gh_7904_test.py b/tests/bugs/gh_7904_test.py index 77cd52fc..824b8566 100644 --- a/tests/bugs/gh_7904_test.py +++ b/tests/bugs/gh_7904_test.py @@ -10,12 +10,16 @@ Checked on 5.0.0.1303, 6.0.0.180 (for UMOWA_ROWS = 700K number of fetches = 270208, elapsed time = 0.741s) [24.09.2024] pzotov - Changed substitutions: one need to suppress '(keys: N, total key length: M)' in FB 6.x (and ONLY there), - otherwise actual and expected output become differ. - Commit: https://github.com/FirebirdSQL/firebird/commit/c50b0aa652014ce3610a1890017c9dd436388c43 - ("Add key info to the hash join plan output", 23.09.2024 18:26) - Discussed with dimitr. - Checked on 6.0.0.467-cc183f5, 5.0.2.1513 + Changed substitutions: one need to suppress '(keys: N, total key length: M)' in FB 6.x (and ONLY there), + otherwise actual and expected output become differ. + Commit: https://github.com/FirebirdSQL/firebird/commit/c50b0aa652014ce3610a1890017c9dd436388c43 + ("Add key info to the hash join plan output", 23.09.2024 18:26) + Discussed with dimitr. + Checked on 6.0.0.467-cc183f5, 5.0.2.1513 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -241,7 +245,7 @@ def test_1(act: Action, capsys): with cur.prepare(q) as ps: print( '\n'.join([replace_leading(s) for s in ps.detailed_plan .split('\n')]) ) - expected_stdout = """ + expected_stdout_5x = """ Select Expression ....-> Aggregate ........-> Sort (record length: 132, key length: 16) @@ -261,7 +265,26 @@ def test_1(act: Action, capsys): ............................-> Table "DOK_ROZLICZENIOWY" as "Q1_DOKR" Full Scan """ + expected_stdout_6x = """ + Select Expression + ....-> Aggregate + ........-> Sort + ............-> Filter + ................-> Hash Join (inner) + ....................-> Nested Loop Join (inner) + ........................-> Filter + ............................-> Table "PUBLIC"."UMOWA" as "Q1_UMOWA" Access By ID + ................................-> Bitmap + ....................................-> Index "PUBLIC"."FK_UMOWA__RODZAJ_UMOWY" Range Scan (partial match: 1/2) + ........................-> Filter + ............................-> Table "PUBLIC"."ROZLICZENIE" as "Q1_ROZL" Access By ID + ................................-> Bitmap + ....................................-> Index "PUBLIC"."FK_ROZLICZENIE__UMOWA" Range Scan (full match) + ....................-> Record Buffer (record length: 25) + ........................-> Filter + ............................-> Table "PUBLIC"."DOK_ROZLICZENIOWY" as "Q1_DOKR" Full Scan + """ - act.expected_stdout = expected_stdout + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From 49a114ca50ba904e125ee8fd2c3b12b897dff636 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:33:24 +0300 Subject: [PATCH 02/28] Added/Updated tests\bugs\gh_7921_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_7921_test.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/bugs/gh_7921_test.py b/tests/bugs/gh_7921_test.py index aa87a133..838068a6 100644 --- a/tests/bugs/gh_7921_test.py +++ b/tests/bugs/gh_7921_test.py @@ -11,11 +11,14 @@ Checked on 5.0.1.1322 after backporting (commit fef5af38, 23.01.2024). [17.11.2024] pzotov - Query text was replaced after https://github.com/FirebirdSQL/firebird/commit/26e64e9c08f635d55ac7a111469498b3f0c7fe81 - ( Cost-based decision between ORDER and SORT plans (#8316) ): 'OPTIMIZE FOR FIRST ROWS' is used for 6.x - Suggested by dimitr, letter 16.11.2024 15:15 - - Checked on 6.0.0.532; 5.0.2.1567 + Query text was replaced after https://github.com/FirebirdSQL/firebird/commit/26e64e9c08f635d55ac7a111469498b3f0c7fe81 + ( Cost-based decision between ORDER and SORT plans (#8316) ): 'OPTIMIZE FOR FIRST ROWS' is used for 6.x + Suggested by dimitr, letter 16.11.2024 15:15 + Checked on 6.0.0.532; 5.0.2.1567 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -223,7 +226,7 @@ def test_1(act: Action, capsys): with cur.prepare(q) as ps: print( '\n'.join([replace_leading(s) for s in ps.detailed_plan .split('\n')]) ) - expected_stdout = """ + expected_stdout_5x = """ Select Expression ....-> Aggregate ........-> Filter @@ -232,7 +235,16 @@ def test_1(act: Action, capsys): ....................-> Bitmap ........................-> Index "ROZLICZENIE_FK4" Range Scan (full match) """ + expected_stdout_6x = """ + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."ROZLICZENIE" as "Q2_ROZL" Access By ID + ................-> Index "PUBLIC"."FK_ROZLICZENIE__DYREKCJA" Full Scan + ....................-> Bitmap + ........................-> Index "PUBLIC"."ROZLICZENIE_FK4" Range Scan (full match) + """ - act.expected_stdout = expected_stdout + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From a7922ad24f2e1e7d5d02096ccaf24caa158e8305 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:37:38 +0300 Subject: [PATCH 03/28] Added/Updated tests\bugs\gh_7924_1_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_7924_1_test.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/bugs/gh_7924_1_test.py b/tests/bugs/gh_7924_1_test.py index f152edb3..07aaead8 100644 --- a/tests/bugs/gh_7924_1_test.py +++ b/tests/bugs/gh_7924_1_test.py @@ -6,7 +6,10 @@ TITLE: ALTER TABLE ALTER COLUMN can not be changed properly in some cases NOTES: [22.01.2024] pzotov - Checked on 6.0.0.219 (after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09) + Checked on 6.0.0.219 (after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09) + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914. """ import pytest @@ -51,16 +54,19 @@ insert into test(f03) values ('ΔΆ'); """ -act = isql_act('db', test_script) - -expected_stdout = """ - F01 VARCHAR(10) CHARACTER SET WIN1250 COLLATE WIN_CZ_CI_AI Nullable - F02 VARCHAR(10) CHARACTER SET WIN1252 COLLATE WIN_PTBR Nullable - F03 VARCHAR(10) CHARACTER SET WIN1257 COLLATE WIN1257_EE Nullable -""" +substitutions = [('[ \t]+', ' '), (r'Table(:)?\s+\S+.*', '')] +act = isql_act('db', test_script, substitutions = substitutions) @pytest.mark.version('>=6.0') def test_1(act: Action): + + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else 'SYSTEM.' + expected_stdout = f""" + F01 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1250 COLLATE {SQL_SCHEMA_PREFIX}WIN_CZ_CI_AI Nullable + F02 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1252 COLLATE {SQL_SCHEMA_PREFIX}WIN_PTBR Nullable + F03 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1257 COLLATE {SQL_SCHEMA_PREFIX}WIN1257_EE Nullable + """ + act.expected_stdout = expected_stdout act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From e536e573076499e958fad78395a6d497af231497 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:39:26 +0300 Subject: [PATCH 04/28] Added/Updated tests\bugs\gh_7924_2_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_7924_2_test.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/bugs/gh_7924_2_test.py b/tests/bugs/gh_7924_2_test.py index 9d06a790..d169c0be 100644 --- a/tests/bugs/gh_7924_2_test.py +++ b/tests/bugs/gh_7924_2_test.py @@ -6,8 +6,11 @@ TITLE: ALTER TABLE ALTER COLUMN can not be changed properly in some cases NOTES: [22.01.2024] pzotov - Checked on 6.0.0.219 (after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09) - TODO: check ability to insert into fields some data specific to appropriate collation and proper order of selected characters. + Checked on 6.0.0.219 (after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09) + TODO: check ability to insert into fields some data specific to appropriate collation and proper order of selected characters. + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914. """ import pytest @@ -44,16 +47,19 @@ show table test; -------------------- [ 1 ] """ -act = isql_act('db', test_script) +substitutions = [('[ \t]+', ' '), (r'Table(:)?\s+\S+.*', '')] +act = isql_act('db', test_script, substitutions = substitutions) -expected_stdout = """ - F_CURR_1250 VARCHAR(10) CHARACTER SET WIN1250 COLLATE WIN_CZ_CI_AI Nullable - F_CURR_1252 VARCHAR(10) CHARACTER SET WIN1252 COLLATE WIN_PTBR Nullable - F_CURR_1257 VARCHAR(10) CHARACTER SET WIN1257 COLLATE WIN1257_EE Nullable -""" @pytest.mark.version('>=6.0') def test_1(act: Action): + + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else 'SYSTEM.' + expected_stdout = f""" + F_CURR_1250 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1250 COLLATE {SQL_SCHEMA_PREFIX}WIN_CZ_CI_AI Nullable + F_CURR_1252 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1252 COLLATE {SQL_SCHEMA_PREFIX}WIN_PTBR Nullable + F_CURR_1257 VARCHAR(10) CHARACTER SET {SQL_SCHEMA_PREFIX}WIN1257 COLLATE {SQL_SCHEMA_PREFIX}WIN1257_EE Nullable + """ act.expected_stdout = expected_stdout act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From e69b7373e2febaa85c367131803c4714fcbdd5f3 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:41:20 +0300 Subject: [PATCH 05/28] Added/Updated tests\bugs\gh_7962_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_7962_test.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/bugs/gh_7962_test.py b/tests/bugs/gh_7962_test.py index 230d5fa3..58a7a15f 100644 --- a/tests/bugs/gh_7962_test.py +++ b/tests/bugs/gh_7962_test.py @@ -6,8 +6,11 @@ TITLE: System procedure/function inconsistency between ISQL SHOW FUNCTIONS and SHOW PROCEDURES NOTES: [23.01.2024] pzotov - Confirmed on 6.0.0.219 - Checked on 6.0.0.219 after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09 + Confirmed on 6.0.0.219 + Checked on 6.0.0.219 after commit https://github.com/FirebirdSQL/firebird/commit/bcc53d43c8cd0b904d2963173c153056f9465a09 + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914. """ import pytest @@ -34,15 +37,16 @@ act = isql_act('db', test_script) -expected_stdout = """ - STANDALONE_FN - PG_TEST.FN_USER - STANDALONE_SP - PG_TEST.SP_USER -""" - @pytest.mark.version('>=5.0.1') def test_1(act: Action): + + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else 'PUBLIC.' + expected_stdout = f""" + {SQL_SCHEMA_PREFIX}STANDALONE_FN + {SQL_SCHEMA_PREFIX}PG_TEST.FN_USER + {SQL_SCHEMA_PREFIX}STANDALONE_SP + {SQL_SCHEMA_PREFIX}PG_TEST.SP_USER + """ act.expected_stdout = expected_stdout act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From 68c5e42dee51b3c3d08f5eb0f9eb35e33fa6e652 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 12:55:44 +0300 Subject: [PATCH 06/28] Added/Updated tests\bugs\gh_8020_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8020_test.py | 43 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/tests/bugs/gh_8020_test.py b/tests/bugs/gh_8020_test.py index f2b49549..9de5a9b6 100644 --- a/tests/bugs/gh_8020_test.py +++ b/tests/bugs/gh_8020_test.py @@ -7,21 +7,15 @@ DESCRIPTION: NOTES: [12.03.2024] pzotov - 1. Crash occured only when connection is done via TCP protocol. - 2. Another bug currently *remains* in FB 6.x if DROP-statements are in DSQL form, i.e are not 'enclosed' in PSQL and begin/end blocks: - ========== - Statement failed, SQLSTATE = 39000 - unsuccessful metadata update - -DROP TABLE T_FN failed - -invalid request BLR at offset 1 - -function F is not defined - ========== - See https://github.com/FirebirdSQL/firebird/issues/8021 (currently not fixed). - Because of this, it was decided to run DROP statements within PSQL code. - 3. Test checks whether MON$SERVER_PID remains the same after execution of DROP statements. In case of crash this is not so. - - Confirmed bug on 6.0.0.268 - Checked on 6.0.0.269 + 1. Crash occured only when connection is done via TCP protocol. + 2. Another bug currently *remains* in FB 6.x if DROP-statements are in DSQL form, i.e are not 'enclosed' in PSQL and begin/end blocks: + See https://github.com/FirebirdSQL/firebird/issues/8021 (currently not fixed). + Because of this, it was decided to run DROP statements within PSQL code. + 3. Test checks whether MON$SERVER_PID remains the same after execution of DROP statements. In case of crash this is not so. + Confirmed bug on 6.0.0.268. Checked on 6.0.0.269 + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914. """ import pytest @@ -29,15 +23,6 @@ db = db_factory() -expected_stdout = """ - IS_SERVER_PID_THE_SAME - Statement failed, SQLSTATE = 38000 - unsuccessful metadata update - -cannot delete - -Function F - -there are 1 dependencies -""" - act = python_act('db') @pytest.mark.version('>=6.0') @@ -86,6 +71,16 @@ def test_1(act: Action): commit; """ + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + TEST_FUNC_NAME = 'F' if act.is_version('<6') else f'{SQL_SCHEMA_PREFIX}"F"' + expected_stdout = f""" + IS_SERVER_PID_THE_SAME + Statement failed, SQLSTATE = 38000 + unsuccessful metadata update + -cannot delete + -Function {TEST_FUNC_NAME} + -there are 1 dependencies + """ act.expected_stdout = expected_stdout act.isql(switches=['-q'], input = test_script, combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From 2044a5e8449451d3c9bd6bd19d85058da3d7c365 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:21:01 +0300 Subject: [PATCH 07/28] Script 'sample-DB_-_firebird.sql' has been adjusted for applying in FB 6.x: 'ALTER CHARACTER SET SET DEFAULT COLLATION ...' requires explicitly specified `PUBLIC.` prefix. --- files/standard_sample_databases.zip | Bin 19470 -> 19583 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/files/standard_sample_databases.zip b/files/standard_sample_databases.zip index 21a8e44fcf1b75d716e7ddf9e7c1f0201c78320a..b92f3e80b23172fe5a117e7bb4750e3ec141af31 100644 GIT binary patch delta 10802 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 Sldv}ylSNE4269RO00006xYdvV From 953f55ee45acc917f918cb2c4a5288db25c5ffbc Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:21:24 +0300 Subject: [PATCH 08/28] Added/Updated tests\bugs\gh_8061_addi_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8061_addi_test.py | 103 ++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/tests/bugs/gh_8061_addi_test.py b/tests/bugs/gh_8061_addi_test.py index c99b81ea..ded4c632 100644 --- a/tests/bugs/gh_8061_addi_test.py +++ b/tests/bugs/gh_8061_addi_test.py @@ -14,22 +14,30 @@ (paragraph "Validity of Unnesting") 2) https://jonathanlewis.wordpress.com/2007/02/26/subquery-with-or/ NOTES: - 1. One need to change config parameter SubQueryConversion to 'true' when check FB 5.x. - 2. Commits: - 6.x: - 22.03.2025 10:47 - https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c - (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) - 5.x - 31.07.2024 09:46 - https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c - (Added support for semi/anti and outer joins to hash join algorithm ...) - Also: - 14.09.2024 09:24 - https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 - (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) - - Checked on 6.0.0.735, 5.0.3.1647 + 1. One need to change config parameter SubQueryConversion to 'true' when check FB 5.x. + 2. Commits: + 6.x: + 22.03.2025 10:47 + https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c + (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) + 5.x + 31.07.2024 09:46 + https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c + (Added support for semi/anti and outer joins to hash join algorithm ...) + Also: + 14.09.2024 09:24 + https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 + (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) + + Checked on 6.0.0.735, 5.0.3.1647 + [06.07.2025] pzotov + Script 'sample-DB_-_firebird.sql' in filed/standard_sample_databases.zip has been adjusted + for applying in FB 6.x: 'ALTER CHARACTER SET ... SET DEFAULT COLLATION ' + requires explicitly specified `PUBLIC.` prefix. Execute block with if/else is used now there. + + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -163,7 +171,7 @@ def test_1(act: Action, tmp_sql: Path, capsys): print(line) act.reset() - act.expected_stdout = f""" + expected_stdout_5x = f""" 1000 {query_map[1000][0]} {query_map[1000][1]} @@ -221,5 +229,66 @@ def test_1(act: Action, tmp_sql: Path, capsys): ....-> Filter ........-> Table "EMPLOYEE" as "X1" Full Scan """ + + expected_stdout_6x = f""" + 1000 + {query_map[1000][0]} + {query_map[1000][1]} + Sub-query + ....-> Filter + ........-> Table "PUBLIC"."EMPLOYEE" as "X" Full Scan + Sub-query + ....-> Filter (preliminary) + ........-> Filter + ............-> Table "PUBLIC"."SALES" as "S3" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."SALES_CUSTOMER_FK_CUST_NO" Range Scan (full match) + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."CUSTOMER" as "C3" Full Scan + + 2000 + {query_map[2000][0]} + {query_map[2000][1]} + Sub-query + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."SALES" as "S3" Access By ID + ................-> Index "PUBLIC"."SALES_CUSTOMER_FK_CUST_NO" Range Scan (full match) + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."CUSTOMER" as "C3" Full Scan + + 3000 + {query_map[3000][0]} + {query_map[3000][1]} + Sub-query + ....-> Union + ........-> Filter + ............-> Table "PUBLIC"."CUSTOMER" as "C1" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."CUSTOMER_PK" Unique Scan + ........-> Filter + ............-> Table "PUBLIC"."EMPLOYEE" as "X1" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."EMPLOYEE_PK" Unique Scan + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."SALES" as "S1" Full Scan + + 4000 + {query_map[4000][0]} + {query_map[4000][1]} + Sub-query + ....-> Filter + ........-> Table "PUBLIC"."SALES" as "S1" Access By ID + ............-> Bitmap + ................-> Index "PUBLIC"."SALES_EMPLOYEE_FK_SALES_REP" Range Scan (full match) + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."EMPLOYEE" as "X1" Full Scan + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From acc4143a76a8646339a96517acc722256c03c6e0 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:24:59 +0300 Subject: [PATCH 09/28] Added/Updated tests\bugs\gh_8061_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8061_test.py | 123 ++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 29 deletions(-) diff --git a/tests/bugs/gh_8061_test.py b/tests/bugs/gh_8061_test.py index 8b6ee94e..b7de1bd5 100644 --- a/tests/bugs/gh_8061_test.py +++ b/tests/bugs/gh_8061_test.py @@ -12,34 +12,42 @@ Some examples for this test were taken from: https://blogs.oracle.com/optimizer/post/optimizer-transformations-subquery-unnesting-part-1 NOTES: - 1. One need to change config parameter SubQueryConversion to 'true' when check FB 5.x. - 2. Explained plan in FB 5.x has no details about keys and total key length, so we have to apply - substitution in order to ignore these data when make comparison with expected output. - 3. Commits: - 6.x: - 22.03.2025 10:47 - https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c - (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) - 5.x - 31.07.2024 09:46 - https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c - (Added support for semi/anti and outer joins to hash join algorithm ...) - Also: - 14.09.2024 09:24 - https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 - (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) - - 4. Following tests also relate to unnesting but they check only FB 5.x (and not FB 6.x): - bugs/gh_8265_test.py; // additional examples related to ability of subquery unnesting; - bugs/gh_8252_test.py; // example when unnesting must NOT be performed; - bugs/gh_8233_test.py; - bugs/gh_8231_test.py; - bugs/gh_8225_test.py; - bugs/gh_8223_test.py; - All these tests will be reimplemented soon in order to check FB 6.x also. - - Confirmed old execution plan in 6.0.0.680 (19.03.2025), it had no 'hash join (semi)' in any explanied plan. - Checked on 6.0.0.687-730aa8f (22-mar-2025), 5.0.1.1464-d1033cc (01-aug-2024). + 1. One need to change config parameter SubQueryConversion to 'true' when check FB 5.x. + 2. Explained plan in FB 5.x has no details about keys and total key length, so we have to apply + substitution in order to ignore these data when make comparison with expected output. + 3. Commits: + 6.x: + 22.03.2025 10:47 + https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c + (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) + 5.x + 31.07.2024 09:46 + https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c + (Added support for semi/anti and outer joins to hash join algorithm ...) + Also: + 14.09.2024 09:24 + https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 + (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) + + 4. Following tests also relate to unnesting but they check only FB 5.x (and not FB 6.x): + bugs/gh_8265_test.py; // additional examples related to ability of subquery unnesting; + bugs/gh_8252_test.py; // example when unnesting must NOT be performed; + bugs/gh_8233_test.py; + bugs/gh_8231_test.py; + bugs/gh_8225_test.py; + bugs/gh_8223_test.py; + All these tests will be reimplemented soon in order to check FB 6.x also. + + Confirmed old execution plan in 6.0.0.680 (19.03.2025), it had no 'hash join (semi)' in any explanied plan. + Checked on 6.0.0.687-730aa8f (22-mar-2025), 5.0.1.1464-d1033cc (01-aug-2024). + [06.07.2025] pzotov + Script 'sample-DB_-_firebird.sql' in filed/standard_sample_databases.zip has been adjusted + for applying in FB 6.x: 'ALTER CHARACTER SET ... SET DEFAULT COLLATION ' + requires explicitly specified `PUBLIC.` prefix. Execute block with if/else is used now there. + + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -172,7 +180,7 @@ def test_1(act: Action, tmp_sql: Path, capsys): print(line) act.reset() - act.expected_stdout = f""" + expected_stdout_5x = f""" 1000 {query_map[1000][0]} {query_map[1000][1]} @@ -226,7 +234,64 @@ def test_1(act: Action, tmp_sql: Path, capsys): ............-> Record Buffer (record length: 33) ................-> Filter ....................-> Table "SALES" as "S4" Full Scan + """ + + expected_stdout_6x = f""" + 1000 + {query_map[1000][0]} + {query_map[1000][1]} + Select Expression + ....-> Filter + ........-> Hash Join (semi) + ............-> Table "PUBLIC"."CUSTOMER" as "C1" Full Scan + ............-> Record Buffer (record length: NN) + ................-> Filter + ....................-> Table "PUBLIC"."SALES" as "S1" Full Scan + + 2000 + {query_map[2000][0]} + {query_map[2000][1]} + Select Expression + ....-> Filter + ........-> Hash Join (semi) + ............-> Table "PUBLIC"."CUSTOMER" as "C2" Full Scan + ............-> Record Buffer (record length: NN) + ................-> Filter + ....................-> Table "PUBLIC"."SALES" as "S2" Full Scan + + 3000 + {query_map[3000][0]} + {query_map[3000][1]} + Select Expression + ....-> Filter + ........-> Hash Join (semi) + ............-> Table "PUBLIC"."CUSTOMER" as "C3" Full Scan + ............-> Record Buffer (record length: NN) + ................-> Filter + ....................-> Hash Join (semi) + ........................-> Table "PUBLIC"."SALES" as "S3" Full Scan + ........................-> Record Buffer (record length: NN) + ............................-> Filter + ................................-> Table "PUBLIC"."EMPLOYEE" as "X" Full Scan + 4000 + {query_map[4000][0]} + {query_map[4000][1]} + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."EMPLOYEE" as "X" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."EMPLOYEE_PK" Unique Scan + Select Expression + ....-> Filter + ........-> Hash Join (semi) + ............-> Table "PUBLIC"."CUSTOMER" as "C4" Full Scan + ............-> Record Buffer (record length: NN) + ................-> Filter + ....................-> Table "PUBLIC"."SALES" as "S4" Full Scan """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From fba8b96d3a1d7f42310e1904dc14e97d58170b7d Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:28:32 +0300 Subject: [PATCH 10/28] Added/Updated tests\bugs\gh_8077_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8077_test.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/bugs/gh_8077_test.py b/tests/bugs/gh_8077_test.py index 3ae6a0f5..832fc67c 100644 --- a/tests/bugs/gh_8077_test.py +++ b/tests/bugs/gh_8077_test.py @@ -12,15 +12,17 @@ (Error at disconnect: / Execute statement error at attach : / 335544830 : Too many recursion levels of EXECUTE STATEMENT) NOTES: [27.05.2024] pzotov - Time of ISQL execution is limited by MAX_WAIT_FOR_ISQL_TERMINATE seconds. Currently it is ~6s for SS and ~18s for CS. - + Time of ISQL execution is limited by MAX_WAIT_FOR_ISQL_TERMINATE seconds. Currently it is ~6s for SS and ~18s for CS. [08.06.2024] pzotov - Added threshold in order to prevent infinite recursion in case of regression. - Otherwise this test can cause collapse of test machine because of infinite launch of firebird processes (in case of Classic). - See notes in the code below, variable 'STOP_RECURSIVE_ES_AFTER_ITER'. - Checked on snapshot 5.x that was not yet fixed. - - Checked on 6.0.0.362, 5.0.1.1408, 4.0.5.3103 (all SS/CS). + Added threshold in order to prevent infinite recursion in case of regression. + Otherwise this test can cause collapse of test machine because of infinite launch of firebird processes (in case of Classic). + See notes in the code below, variable 'STOP_RECURSIVE_ES_AFTER_ITER'. + Checked on snapshot 5.x that was not yet fixed. + + Checked on 6.0.0.362, 5.0.1.1408, 4.0.5.3103 (all SS/CS). + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668; 4.0.6.3214. """ import re @@ -171,6 +173,8 @@ def test_1(act: Action, tmp_sql: Path, tmp_log: Path, capsys): f.write(line[1:] + '\n') + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + TRG_DETACH_NAME = "'TRG_DETACH'" if act.is_version('<6') else f'{SQL_SCHEMA_PREFIX}"TRG_DETACH"' expected_stdout = f""" rdb_trg_name TRG_ATTACH rdb_trg_type 8192 @@ -181,13 +185,13 @@ def test_1(act: Action, tmp_sql: Path, tmp_log: Path, capsys): Execute statement error at attach : 335544830 : Too many recursion levels of EXECUTE STATEMENT Data source : Firebird:: - At trigger 'TRG_DETACH' + At trigger {TRG_DETACH_NAME} Error at disconnect: Execute statement error at attach : 335544830 : Too many recursion levels of EXECUTE STATEMENT Data source : Firebird:: - At trigger 'TRG_DETACH' + At trigger {TRG_DETACH_NAME} """ with open(tmp_log, 'r') as f: From c270792be560bae8c0a5f06114a59d603c046dd3 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:30:32 +0300 Subject: [PATCH 11/28] Added/Updated tests\bugs\gh_8084_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8084_test.py | 63 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/tests/bugs/gh_8084_test.py b/tests/bugs/gh_8084_test.py index 5c6db971..1d0080cc 100644 --- a/tests/bugs/gh_8084_test.py +++ b/tests/bugs/gh_8084_test.py @@ -7,9 +7,12 @@ DESCRIPTION: NOTES: [19.04.2024] pzotov - Reduced min_version to 5.0.1 after backporting (commit #0e9ef69). - Confirmed bug on 6.0.0.315; confirmed problem noted as second case (see ticket) in 6.0.0.321 #1d96c10. - Checked on 6.0.0.325 #f5930a5, 5.0.1.1383 #0e9ef69 (intermediate snapshot) - all OK. + Reduced min_version to 5.0.1 after backporting (commit #0e9ef69). + Confirmed bug on 6.0.0.315; confirmed problem noted as second case (see ticket) in 6.0.0.321 #1d96c10. + Checked on 6.0.0.325 #f5930a5, 5.0.1.1383 #0e9ef69 (intermediate snapshot) - all OK. + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -75,39 +78,41 @@ act = isql_act('db', test_script, substitutions=[('[ \t]+', ' ')]) -expected_stdout = """ - Statement failed, SQLSTATE = 23000 - attempt to store duplicate value (visible to active transactions) in unique index "TEST1_IDX_A" - -Problematic key value is ("T1_A" = 2) - Statement failed, SQLSTATE = 23000 - attempt to store duplicate value (visible to active transactions) in unique index "TEST1_IDX_A" - -Problematic key value is ("T1_A" = 1) +@pytest.mark.version('>=5.0.1') +def test_1(act: Action): - T1_A 1 - T1_A_CNT 1 + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + expected_stdout = f""" + Statement failed, SQLSTATE = 23000 + attempt to store duplicate value (visible to active transactions) in unique index {SQL_SCHEMA_PREFIX}"TEST1_IDX_A" + -Problematic key value is ("T1_A" = 2) + Statement failed, SQLSTATE = 23000 + attempt to store duplicate value (visible to active transactions) in unique index {SQL_SCHEMA_PREFIX}"TEST1_IDX_A" + -Problematic key value is ("T1_A" = 1) - T1_A 2 - T1_A_CNT 1 + T1_A 1 + T1_A_CNT 1 - T2_ID 1 - T2_A 1 - T2_B 0 + T1_A 2 + T1_A_CNT 1 - T2_ID 2 - T2_A 2 - T2_B 0 + T2_ID 1 + T2_A 1 + T2_B 0 - T2_ID 3 - T2_A 1 - T2_B 0 + T2_ID 2 + T2_A 2 + T2_B 0 - T2_ID 4 - T2_A 2 - T2_B 1 -""" + T2_ID 3 + T2_A 1 + T2_B 0 + + T2_ID 4 + T2_A 2 + T2_B 1 + """ -@pytest.mark.version('>=5.0.1') -def test_1(act: Action): act.expected_stdout = expected_stdout act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From 4ad14156eb482ffb54208d8d7574e91d3cf8197e Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 13:32:06 +0300 Subject: [PATCH 12/28] Added/Updated tests\bugs\gh_8091_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8091_test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/bugs/gh_8091_test.py b/tests/bugs/gh_8091_test.py index 166e48bb..663cd2b9 100644 --- a/tests/bugs/gh_8091_test.py +++ b/tests/bugs/gh_8091_test.py @@ -15,7 +15,10 @@ Result must be the same as for iteration with default dialect = 3. NOTES: [25.10.2024] pzotov - Checked on 6.0.0.508-67d8e39 (intermediate build). + Checked on 6.0.0.508-67d8e39 (intermediate build). + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668. """ import time from io import BytesIO @@ -95,11 +98,13 @@ def test_1(act: Action, capsys): ,8 : 'select count(*) from test where z is not distinct from null and mod(id,2) = 0' ,9 : 'select count(*) from test where x-y is not distinct from null and mod(id,3) <= 1' } - nr_block = """ + + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + nr_block = f""" Select Expression ....-> Aggregate ........-> Filter - ............-> Table "TEST" Full Scan + ............-> Table {SQL_SCHEMA_PREFIX}"TEST" Full Scan """ for iter in range(2): From ff26ec46f1d852694baa1780119f34d1f6726cb4 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 17:25:20 +0300 Subject: [PATCH 13/28] Added/Updated tests\bugs\gh_8109_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8109_test.py | 84 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/tests/bugs/gh_8109_test.py b/tests/bugs/gh_8109_test.py index 8608ea54..fd67ea50 100644 --- a/tests/bugs/gh_8109_test.py +++ b/tests/bugs/gh_8109_test.py @@ -7,8 +7,12 @@ DESCRIPTION: NOTES: [03.02.2025] pzotov - Confirmed problem (regression) on 6.0.0.595-2c5b146, 5.0.2.1601-f094936 - Checked on 6.0.0.601-5dee439, 5.0.2.1606-fd31e52 (intermediate snapshots). + Confirmed problem (regression) on 6.0.0.595-2c5b146, 5.0.2.1601-f094936 + Checked on 6.0.0.601-5dee439, 5.0.2.1606-fd31e52 (intermediate snapshots). + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -78,7 +82,7 @@ def test_1(act: Action, capsys): ps.free() - expected_stdout = f""" + expected_stdout_5x = f""" {queries_map[ 0 ]} Select Expression ....-> Filter @@ -150,6 +154,78 @@ def test_1(act: Action, capsys): ....................-> Index "TEST_Q_DEC" Range Scan (full match) """ - act.expected_stdout = expected_stdout + expected_stdout_6x = f""" + {queries_map[ 0 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_ASC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Y_ASC" Range Scan (full match) + + {queries_map[ 1 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_U_DEC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_V_DEC" Range Scan (full match) + + {queries_map[ 2 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_ASC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_U_DEC" Range Scan (full match) + + {queries_map[ 3 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_V_DEC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Y_ASC" Range Scan (full match) + + {queries_map[ 4 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_C_ASC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_C_DEC" Range Scan (full match) + + {queries_map[ 5 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_P_ASC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Q_ASC" Range Scan (full match) + + {queries_map[ 6 ]} + Select Expression + ....-> Filter + ........-> Table "PUBLIC"."TEST" Access By ID + ............-> Bitmap Or + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_P_DEC" Range Scan (full match) + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Q_DEC" Range Scan (full match) + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From 51e9cde7c82f13ad4c26bd5b124507e0456f267b Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 17:27:39 +0300 Subject: [PATCH 14/28] Added/Updated tests\bugs\gh_8115_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8115_test.py | 44 +++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/bugs/gh_8115_test.py b/tests/bugs/gh_8115_test.py index 9d57fffc..53ba627d 100644 --- a/tests/bugs/gh_8115_test.py +++ b/tests/bugs/gh_8115_test.py @@ -8,28 +8,22 @@ Original title: "FB 5.0.0.1306 - unexpected results using LEFT JOIN with When " NOTES: [16.09.2024] pzotov - Confirmed bug in 5.0.1.1369-8c31082 (17.03.2024) - Bug was fixed in 5.0.1.1369-bbd35ab (20.03.2024) - Commit: - https://github.com/FirebirdSQL/firebird/commit/bbd35ab07c129e9735f081fcd29172a8187aa8ab - Avoid reading/hashing the inner stream(s) if the leader stream is empty - Checked on 6.0.0.457, 5.0.2.1499 - - [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - + Confirmed bug in 5.0.1.1369-8c31082 (17.03.2024) + Bug was fixed in 5.0.1.1369-bbd35ab (20.03.2024) + Commit: + https://github.com/FirebirdSQL/firebird/commit/bbd35ab07c129e9735f081fcd29172a8187aa8ab + Avoid reading/hashing the inner stream(s) if the leader stream is empty + Checked on 6.0.0.457, 5.0.2.1499 [27.03.2025] pzotov - Explained plan for 6.x became the same as for 5.x, see commit: - https://github.com/FirebirdSQL/firebird/commit/6c21404c6ef800ceb7d3bb9c97dc8249431dbc5b - Comparison of actual output must be done with single expected_out in both 5.x and 6.x. - Plan for 5.x (with TWO 'Filter' clauses) must be considered as more effective. - Disussed with dimitr, letter 16.09.2024 17:55. - - Checked on 6.0.0.698-6c21404. + Explained plan for 6.x became the same as for 5.x, see commit: + https://github.com/FirebirdSQL/firebird/commit/6c21404c6ef800ceb7d3bb9c97dc8249431dbc5b + Comparison of actual output must be done with single expected_out in both 5.x and 6.x. + Plan for 5.x (with TWO 'Filter' clauses) must be considered as more effective. + Disussed with dimitr, letter 16.09.2024 17:55. + Checked on 6.0.0.698-6c21404. + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668. """ import zipfile from pathlib import Path @@ -98,13 +92,15 @@ def test_1(act: Action, tmp_fbk: Path, capsys): if ps: ps.free() - expected_stdout = """ + + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + expected_stdout = f""" Select Expression ....-> Nested Loop Join (inner) - ........-> Procedure "SAL_INPERIOADA2" as "AA" Scan + ........-> Procedure {SQL_SCHEMA_PREFIX}"SAL_INPERIOADA2" as "AA" Scan ........-> Filter ............-> Filter - ................-> Procedure "USER_CNP" as "AB" Scan + ................-> Procedure {SQL_SCHEMA_PREFIX}"USER_CNP" as "AB" Scan 000DD4E1-B4D0-4D6E-9D9F-DE9A7D0D6492 E574F734-CECB-4A8F-B9BE-FAF51BC61FAD From b1d54805f559a91af11696860af00eea9374c248 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 17:33:07 +0300 Subject: [PATCH 15/28] Added/Updated tests\bugs\gh_8123_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8123_test.py | 129 ++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/tests/bugs/gh_8123_test.py b/tests/bugs/gh_8123_test.py index ac25d1e8..e4a0fc73 100644 --- a/tests/bugs/gh_8123_test.py +++ b/tests/bugs/gh_8123_test.py @@ -13,8 +13,12 @@ "SQLSTATE = 42000 / ... / -cannot delete ... / -there are 1 dependencies" NOTES: [21.05.2024] pzotov - Confirmed bug on 6.0.0.357-bf6c467 (regular daily snapshot, 18-may-2024). - Checked on intermediate snapshots 6.0.0.357-f94343e, 5.0.1.1404-88bf561. + Confirmed bug on 6.0.0.357-bf6c467 (regular daily snapshot, 18-may-2024). + Checked on intermediate snapshots 6.0.0.357-f94343e, 5.0.1.1404-88bf561. + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -116,48 +120,87 @@ act = isql_act('db', test_script, substitutions=[('[ \t]+', ' ')]) -expected_stdout = """ - RDB$DEPENDED_ON_NAME TB_TEST1 - RDB$FIELD_NAME N1 - RDB$DEPENDENT_TYPE 3 - RDB$DEPENDED_ON_TYPE 0 - Records affected: 1 - - Statement failed, SQLSTATE = 42000 - unsuccessful metadata update - -cannot delete - -COLUMN TB_TEST1.N1 - -there are 1 dependencies - - - RDB$DEPENDED_ON_NAME TB_TEST2 - RDB$FIELD_NAME N1 - RDB$DEPENDENT_TYPE 3 - RDB$DEPENDED_ON_TYPE 0 - Records affected: 1 - - Statement failed, SQLSTATE = 42000 - unsuccessful metadata update - -cannot delete - -COLUMN TB_TEST2.N1 - -there are 1 dependencies - - - RDB$DEPENDED_ON_NAME TB_TEST3 - RDB$FIELD_NAME N1 - RDB$DEPENDENT_TYPE 3 - RDB$DEPENDED_ON_TYPE 0 - Records affected: 1 - - Statement failed, SQLSTATE = 42000 - unsuccessful metadata update - -cannot delete - -COLUMN TB_TEST3.N1 - -there are 1 dependencies -""" - @pytest.mark.version('>=5.0.1') def test_1(act: Action): - act.expected_stdout = expected_stdout + + expected_stdout_5x = """ + RDB$DEPENDED_ON_NAME TB_TEST1 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN TB_TEST1.N1 + -there are 1 dependencies + + + RDB$DEPENDED_ON_NAME TB_TEST2 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN TB_TEST2.N1 + -there are 1 dependencies + + + RDB$DEPENDED_ON_NAME TB_TEST3 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN TB_TEST3.N1 + -there are 1 dependencies + """ + + expected_stdout_6x = """ + RDB$DEPENDED_ON_NAME TB_TEST1 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN "PUBLIC"."TB_TEST1"."N1" + -there are 1 dependencies + + RDB$DEPENDED_ON_NAME TB_TEST2 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN "PUBLIC"."TB_TEST2"."N1" + -there are 1 dependencies + + RDB$DEPENDED_ON_NAME TB_TEST3 + RDB$FIELD_NAME N1 + RDB$DEPENDENT_TYPE 3 + RDB$DEPENDED_ON_TYPE 0 + Records affected: 1 + + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -cannot delete + -COLUMN "PUBLIC"."TB_TEST3"."N1" + -there are 1 dependencies + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From 491362a773448256a84e86cb23fd8a5531b5f490 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 18:48:11 +0300 Subject: [PATCH 16/28] Added/Updated tests\bugs\gh_8223_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8223_test.py | 61 ++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/tests/bugs/gh_8223_test.py b/tests/bugs/gh_8223_test.py index b22b7a8f..bd01d74f 100644 --- a/tests/bugs/gh_8223_test.py +++ b/tests/bugs/gh_8223_test.py @@ -7,26 +7,27 @@ DESCRIPTION: NOTES: [27.08.2024] pzotov - 1. Confirmed bug on 5.0.1.1469-1d792e4 (Release (15.08.2024), got for SubQueryConversion=true: - no current record for fetch operation / gdscode = 335544348. - - 2. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. - Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. - 3. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. - + 1. Confirmed bug on 5.0.1.1469-1d792e4 (Release (15.08.2024), got for SubQueryConversion=true: + no current record for fetch operation / gdscode = 335544348. + 2. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. + Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. + 3. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - - Checked on 5.0.2.1483-0bf2de0 -- all ok. - Thanks to dimitr for the advice on implementing the test. - + Resultset of cursor that executes using instance of selectable PreparedStatement must be stored + in some variable in order to have ability close it EXPLICITLY (before PS will be freed). + Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). + This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 + The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). + + Checked on 5.0.2.1483-0bf2de0 -- all ok. + Thanks to dimitr for the advice on implementing the test. [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -129,7 +130,7 @@ def test_1(act: Action, capsys): ps.free() - act.expected_stdout = f""" + expected_stdout_5x = f""" Select Expression ....-> Filter ........-> Hash Join (semi) @@ -148,5 +149,27 @@ def test_1(act: Action, capsys): ................-> Table "T5" as "E" Full Scan 1 1 1 """ + + expected_stdout_6x = f""" + Select Expression + ....-> Filter + ........-> Hash Join (semi) + ............-> Filter + ................-> Hash Join (inner) + ....................-> Nested Loop Join (outer) + ........................-> Nested Loop Join (outer) + ............................-> Table "PUBLIC"."T1" as "PUBLIC"."V" "A" Full Scan + ............................-> Filter + ................................-> Table "PUBLIC"."T2" as "PUBLIC"."V" "B" Full Scan + ........................-> Filter + ............................-> Table "PUBLIC"."T3" as "PUBLIC"."V" "C" Full Scan + ....................-> Record Buffer (record length: NN) + ........................-> Table "PUBLIC"."T4" as "D" Full Scan + ............-> Record Buffer (record length: NN) + ................-> Table "PUBLIC"."T5" as "E" Full Scan + 1 1 1 + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From a9878572d65efd7cc661d76f67b97b121dc760eb Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 18:56:35 +0300 Subject: [PATCH 17/28] Added/Updated tests\bugs\gh_8225_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8225_test.py | 47 ++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/tests/bugs/gh_8225_test.py b/tests/bugs/gh_8225_test.py index a0074886..3ab7fa0f 100644 --- a/tests/bugs/gh_8225_test.py +++ b/tests/bugs/gh_8225_test.py @@ -7,22 +7,24 @@ DESCRIPTION: NOTES: [03.09.2024] pzotov - Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. - Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. - + Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. + Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - - Confirmed bug on 5.0.2.1479-adfe97a. - Checked on 5.0.2.1482-604555f. - + Resultset of cursor that executes using instance of selectable PreparedStatement must be stored + in some variable in order to have ability close it EXPLICITLY (before PS will be freed). + Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). + This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 + The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). + + Confirmed bug on 5.0.2.1479-adfe97a. + Checked on 5.0.2.1482-604555f. [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -135,7 +137,7 @@ def test_1(act: Action, capsys): con.rollback() - act.expected_stdout = f""" + expected_stdout_5x = f""" Select Expression ....-> First N Records ........-> Filter @@ -147,6 +149,21 @@ def test_1(act: Action, capsys): ....................-> Table "EMPLOYEE" as "E" Full Scan 2 d2 """ + + expected_stdout_6x = f""" + Select Expression + ....-> First N Records + ........-> Filter + ............-> Hash Join (semi) + ................-> Refetch + ....................-> Sort (record length: NN, key length: NN) + ........................-> Table "PUBLIC"."DEPARTMENT" as "D" Full Scan + ................-> Record Buffer (record length: NN) + ....................-> Table "PUBLIC"."EMPLOYEE" as "E" Full Scan + 2 d2 + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From 061921d870090cd81e2960bb8f94251dad40fa2c Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 18:58:47 +0300 Subject: [PATCH 18/28] Added/Updated tests\bugs\gh_8231_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8231_test.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/bugs/gh_8231_test.py b/tests/bugs/gh_8231_test.py index 415c8c16..218a3556 100644 --- a/tests/bugs/gh_8231_test.py +++ b/tests/bugs/gh_8231_test.py @@ -7,24 +7,23 @@ DESCRIPTION: NOTES: [26.08.2024] pzotov - Two tables must be joined by columns which has different charset or collates. - Confirmed bug on 5.0.2.1484-3cdfd38 (25.08.2024), got: - Statement failed, SQLSTATE = HY000 - request size limit exceeded - Checked on 5.0.2.1485-274af35 -- all ok. - + Two tables must be joined by columns which has different charset or collates. + Confirmed bug on 5.0.2.1484-3cdfd38 (25.08.2024), got: SQLSTATE = HY000 / request size limit exceeded + Checked on 5.0.2.1485-274af35 -- all ok. [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - - Thanks to dimitr for the advice on implementing the test. + Resultset of cursor that executes using instance of selectable PreparedStatement must be stored + in some variable in order to have ability close it EXPLICITLY (before PS will be freed). + Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). + This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 + The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). + Thanks to dimitr for the advice on implementing the test. [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -100,12 +99,13 @@ def test_1(act: Action, capsys): con.rollback() + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' act.expected_stdout = f""" Select Expression ....-> Nested Loop Join (semi) - ........-> Table "T1" Full Scan + ........-> Table {SQL_SCHEMA_PREFIX}"T1" Full Scan ........-> Filter - ............-> Table "T2" Full Scan + ............-> Table {SQL_SCHEMA_PREFIX}"T2" Full Scan 1 """ act.stdout = capsys.readouterr().out From d25cf584bd952ef363705b99ba18fbe32b350fbb Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:00:19 +0300 Subject: [PATCH 19/28] Added/Updated tests\bugs\gh_8233_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8233_test.py | 40 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/bugs/gh_8233_test.py b/tests/bugs/gh_8233_test.py index 696227f7..a07f5cd2 100644 --- a/tests/bugs/gh_8233_test.py +++ b/tests/bugs/gh_8233_test.py @@ -7,26 +7,27 @@ DESCRIPTION: NOTES: [27.08.2024] pzotov - 1. Confirmed bug on 5.0.1.1485-274af35 (26.08.2024), got for SubQueryConversion=true: - "multiple rows in singleton select", gdscodes: (335544652, 335544842) - 2. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. - Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. - 3. Table 't1' must have more than one row for bug reproducing. Query must be enclosed in execute block. - 4. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. - + 1. Confirmed bug on 5.0.1.1485-274af35 (26.08.2024), got for SubQueryConversion=true: + "multiple rows in singleton select", gdscodes: (335544652, 335544842) + 2. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. + Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. + 3. Table 't1' must have more than one row for bug reproducing. Query must be enclosed in execute block. + 4. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - - Checked on 5.0.2.1487-6934878 -- all ok. - Thanks to dimitr for the advice on implementing the test. + Resultset of cursor that executes using instance of selectable PreparedStatement must be stored + in some variable in order to have ability close it EXPLICITLY (before PS will be freed). + Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). + This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 + The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). + Checked on 5.0.2.1487-6934878 -- all ok. + Thanks to dimitr for the advice on implementing the test. [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -118,6 +119,7 @@ def test_1(act: Action, capsys): if ps: ps.free() + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' act.expected_stdout = f""" Select Expression (line 5, column 12) ....-> Singularity Check @@ -125,9 +127,9 @@ def test_1(act: Action, capsys): ............-> Filter ................-> Hash Join (semi) ....................-> Sort (record length: 28, key length: 8) - ........................-> Table "T1" Full Scan + ........................-> Table {SQL_SCHEMA_PREFIX}"T1" Full Scan ....................-> Record Buffer (record length: 25) - ........................-> Table "T2" Full Scan + ........................-> Table {SQL_SCHEMA_PREFIX}"T2" Full Scan 3 """ act.stdout = capsys.readouterr().out From 7f4a1777c651e47f7fc26495bb6bf97087f5d0dd Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:03:15 +0300 Subject: [PATCH 20/28] Updated tests\bugs\gh_8249_test.py: adjust expected stdout/stderr to current FB version. --- tests/bugs/gh_8249_test.py | 158 ++++++++++++++++++++++++------------- 1 file changed, 103 insertions(+), 55 deletions(-) diff --git a/tests/bugs/gh_8249_test.py b/tests/bugs/gh_8249_test.py index 30009f1d..04a04b64 100644 --- a/tests/bugs/gh_8249_test.py +++ b/tests/bugs/gh_8249_test.py @@ -66,62 +66,110 @@ act = isql_act('db', test_script, substitutions = [('[-]?At line \\d+.*', '')]) -expected_stdout = """ - Statement failed, SQLSTATE = 22021 - unsuccessful metadata update - -CREATE VIEW V_TEST_1 failed - -Dynamic SQL Error - -SQL error code = -204 - -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined - - Statement failed, SQLSTATE = 22021 - unsuccessful metadata update - -CREATE PROCEDURE SP_TEST_1 failed - -Dynamic SQL Error - -SQL error code = -204 - -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined - - Statement failed, SQLSTATE = 22021 - unsuccessful metadata update - -CREATE PROCEDURE SP_TEST_2 failed - -Dynamic SQL Error - -SQL error code = -204 - -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined - - Statement failed, SQLSTATE = 22021 - unsuccessful metadata update - -CREATE FUNCTION FN_TEST_1 failed - -Dynamic SQL Error - -SQL error code = -204 - -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined - - Statement failed, SQLSTATE = 42S02 - Dynamic SQL Error - -SQL error code = -204 - -Table unknown - -V_TEST_1 - - Statement failed, SQLSTATE = 39000 - Dynamic SQL Error - -SQL error code = -804 - -Function unknown - -FN_TEST_1 - - Statement failed, SQLSTATE = 42S02 - Dynamic SQL Error - -SQL error code = -204 - -Table unknown - -SP_TEST_1 - - Statement failed, SQLSTATE = 42000 - Dynamic SQL Error - -SQL error code = -204 - -Procedure unknown - -SP_TEST_2 -""" - @pytest.mark.version('>=6.0') def test_1(act: Action): - act.expected_stdout = expected_stdout + + expected_stdout_5x = """ + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE VIEW V_TEST_1 failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined + + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE PROCEDURE SP_TEST_1 failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined + + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE PROCEDURE SP_TEST_2 failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined + + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE FUNCTION FN_TEST_1 failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION MISSED_COLL for CHARACTER SET UTF8 is not defined + + Statement failed, SQLSTATE = 42S02 + Dynamic SQL Error + -SQL error code = -204 + -Table unknown + -V_TEST_1 + + Statement failed, SQLSTATE = 39000 + Dynamic SQL Error + -SQL error code = -804 + -Function unknown + -FN_TEST_1 + + Statement failed, SQLSTATE = 42S02 + Dynamic SQL Error + -SQL error code = -204 + -Table unknown + -SP_TEST_1 + + Statement failed, SQLSTATE = 42000 + Dynamic SQL Error + -SQL error code = -204 + -Procedure unknown + -SP_TEST_2 + """ + + expected_stdout_6x = """ + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE VIEW "PUBLIC"."V_TEST_1" failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION "PUBLIC"."MISSED_COLL" for CHARACTER SET "SYSTEM"."UTF8" is not defined + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE PROCEDURE "PUBLIC"."SP_TEST_1" failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION "PUBLIC"."MISSED_COLL" for CHARACTER SET "SYSTEM"."UTF8" is not defined + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE PROCEDURE "PUBLIC"."SP_TEST_2" failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION "PUBLIC"."MISSED_COLL" for CHARACTER SET "SYSTEM"."UTF8" is not defined + Statement failed, SQLSTATE = 22021 + unsuccessful metadata update + -CREATE FUNCTION "PUBLIC"."FN_TEST_1" failed + -Dynamic SQL Error + -SQL error code = -204 + -COLLATION "PUBLIC"."MISSED_COLL" for CHARACTER SET "SYSTEM"."UTF8" is not defined + Statement failed, SQLSTATE = 42S02 + Dynamic SQL Error + -SQL error code = -204 + -Table unknown + -"V_TEST_1" + Statement failed, SQLSTATE = 39000 + Dynamic SQL Error + -SQL error code = -804 + -Function unknown + -"FN_TEST_1" + Statement failed, SQLSTATE = 42S02 + Dynamic SQL Error + -SQL error code = -204 + -Table unknown + -"SP_TEST_1" + Statement failed, SQLSTATE = 42000 + Dynamic SQL Error + -SQL error code = -204 + -Procedure unknown + -"SP_TEST_2" + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.execute(combine_output = True) assert act.clean_stdout == act.clean_expected_stdout From 5ff4eae671d9fc4ae7b4831c1e5deba5b12d7d34 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:05:01 +0300 Subject: [PATCH 21/28] Updated tests\bugs\gh_8250_test.py: adjust expected stdout/stderr to current FB version. --- tests/bugs/gh_8250_test.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/bugs/gh_8250_test.py b/tests/bugs/gh_8250_test.py index 121a3e6d..9b4b4fbb 100644 --- a/tests/bugs/gh_8250_test.py +++ b/tests/bugs/gh_8250_test.py @@ -115,7 +115,7 @@ def test_1(act: Action, capsys): print(max_keys_msg) - act.expected_stdout = f""" + expected_stdout_5x = f""" Select Expression ....-> Filter ........-> Hash Join (inner) (keys, total key length) @@ -127,6 +127,21 @@ def test_1(act: Action, capsys): ................-> Table "TEST2" as "T2" Full Scan {expected_keys} """ + + expected_stdout_6x = f""" + Select Expression + ....-> Filter + ........-> Hash Join (inner) (keys, total key length) + ............-> Hash Join (inner) (keys, total key length) + ................-> Table "PUBLIC"."TEST3" as "T3" Full Scan + ................-> Record Buffer (record length) + ....................-> Table "PUBLIC"."TEST1" as "T1" Full Scan + ............-> Record Buffer (record length) + ................-> Table "PUBLIC"."TEST2" as "T2" Full Scan + {expected_keys} + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout act.reset() From 7755a0f0467c90dfb8ee9bd56b94a71ae26cc0d7 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:07:29 +0300 Subject: [PATCH 22/28] Added/Updated tests\bugs\gh_8252_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8252_test.py | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/tests/bugs/gh_8252_test.py b/tests/bugs/gh_8252_test.py index c0748d4e..6dfe4dd8 100644 --- a/tests/bugs/gh_8252_test.py +++ b/tests/bugs/gh_8252_test.py @@ -7,24 +7,24 @@ DESCRIPTION: NOTES: [14.09.2024] pzotov - 1. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. - Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. - 2. Custom driver config object is created here for using 'SubQueryConversion = true'. - 3. Additional test was made for this issue: tests/functional/tabloid/test_aae2ae32.py - + 1. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. + Because of that, testing version are limited only for 5.0.2. FB 6.x currently is NOT tested. + 2. Custom driver config object is created here for using 'SubQueryConversion = true'. + 3. Additional test was made for this issue: tests/functional/tabloid/test_aae2ae32.py [18.01.2025] pzotov - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). - - Confirmed bug on 5.0.2.1497. - Checked on 5.0.2.1499-5fa4ae6. - + Resultset of cursor that executes using instance of selectable PreparedStatement must be stored + in some variable in order to have ability close it EXPLICITLY (before PS will be freed). + Otherwise access violation raises during Python GC and pytest hangs at final point (does not return control to OS). + This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 + The reason of that was explained by Vlad, 26.10.24 17:42 ("oddities when use instances of selective statements"). + Confirmed bug on 5.0.2.1497. Checked on 5.0.2.1499-5fa4ae6. [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -91,16 +91,17 @@ def test_1(act: Action, capsys): con.rollback() - act.expected_stdout = """ + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' + act.expected_stdout = f""" Sub-query ....-> Filter - ........-> Table "CUSTOMER" as "C" Access By ID + ........-> Table {SQL_SCHEMA_PREFIX}"CUSTOMER" as "C" Access By ID ............-> Bitmap - ................-> Index "RDB$PRIMARY22" Unique Scan + ................-> Index {SQL_SCHEMA_PREFIX}"RDB$PRIMARY22" Unique Scan Select Expression ....-> First N Records ........-> Filter - ............-> Table "SALES" as "S" Full Scan + ............-> Table {SQL_SCHEMA_PREFIX}"SALES" as "S" Full Scan 1 1 1 From cc8f51d3612d848e69dd103df9a6a6f717e1ab8f Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:11:56 +0300 Subject: [PATCH 23/28] Added/Updated tests\bugs\gh_8263_test.py: Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x --- tests/bugs/gh_8263_test.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/bugs/gh_8263_test.py b/tests/bugs/gh_8263_test.py index df739190..e744a556 100644 --- a/tests/bugs/gh_8263_test.py +++ b/tests/bugs/gh_8263_test.py @@ -20,15 +20,16 @@ 'Index "..." Full Scan' line. NOTES: [28.09.2024] pzotov - ::: NB ::: - This test forced to change prototypes of firebird.conf for 5.x and 6.x, see in $QA_HOME/firebird-qa/configs/ - files 'fb50_all.conf' and 'fb60_all.conf': they now contain ParallelWorkers > 1. - This change may affect on entire QA run result! Some other tests may need to be adjusted after this. - - Thanks to Vlad for suggestions about this test implementation. - - Confirmed bug on 6.0.0.471, 5.0.2.1516 - Checked on 6.0.0.474, 5.0.2.1519 -- all Ok. + ::: NB ::: + This test forced to change prototypes of firebird.conf for 5.x and 6.x, see in $QA_HOME/firebird-qa/configs/ + files 'fb50_all.conf' and 'fb60_all.conf': they now contain ParallelWorkers > 1. + This change may affect on entire QA run result! Some other tests may need to be adjusted after this. + Thanks to Vlad for suggestions about this test implementation. + Confirmed bug on 6.0.0.471, 5.0.2.1516 + Checked on 6.0.0.474, 5.0.2.1519 -- all Ok. + [06.07.2025] pzotov + Added 'SQL_SCHEMA_PREFIX' to be substituted in expected_* on FB 6.x + Checked on 6.0.0.914; 5.0.3.1668; 4.0.6.3214. """ import locale from pathlib import Path @@ -135,10 +136,11 @@ def test_1(act: Action, tmp_fbk: Path, tmp_fdb: Path, capsys): # Print explained plan with padding eash line by dots in order to see indentations: print( '\n'.join([replace_leading(s) for s in ps.detailed_plan.split('\n')]) ) + SQL_SCHEMA_PREFIX = '' if act.is_version('<6') else '"PUBLIC".' act.expected_stdout = f""" Select Expression - ....-> Table "TEST" Access By ID - ........-> Index "TEST_ID" Full Scan + ....-> Table {SQL_SCHEMA_PREFIX}"TEST" Access By ID + ........-> Index {SQL_SCHEMA_PREFIX}"TEST_ID" Full Scan """ act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From 99570b680e7bbe8e59bd3dd7826b90c688f488e0 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:20:23 +0300 Subject: [PATCH 24/28] Added/Updated tests\bugs\gh_8265_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8265_test.py | 151 +++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 55 deletions(-) diff --git a/tests/bugs/gh_8265_test.py b/tests/bugs/gh_8265_test.py index 7c122234..29a28c8e 100644 --- a/tests/bugs/gh_8265_test.py +++ b/tests/bugs/gh_8265_test.py @@ -7,61 +7,30 @@ DESCRIPTION: NOTES: [26.09.2024] pzotov - 0. Commits: - 6.x: - 22.03.2025 10:47 - https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c - (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) - 5.x - 31.07.2024 09:46 - https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c - (Added support for semi/anti and outer joins to hash join algorithm ...) - Also: - 14.09.2024 09:24 - https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 - (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) - 1. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. - 2. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. - 3. First example of this test is also used in tests/functional/tabloid/test_aae2ae32.py - - Confirmed problem on 5.0.2.1516-fe6ba50 (23.09.2024). - Checked on 5.0.2.1516-92316F0 (25.09.2024). - - [15.01.2025] pzotov - - ### CRITICAL ISSUE ### PROBABLY MUST BE APPLIED TO ALL TESTS WITH SIMILAR BEHAVOUR ### - - Resultset of cursor that executes using instance of selectable PreparedStatement must be stored - in some variable in order to have ability close it EXPLICITLY (before PS will be freed). - Otherwise access violation raises. - This occurs at least for: Python 3.11.2 / pytest: 7.4.4 / firebird.driver: 1.10.6 / Firebird.Qa: 0.19.3 - - Example of broken pytest log when AV occurs: - ========== - tests/bugs/gh_8265_test.py::test_1 Windows fatal exception: access violation - - Current thread 0x0000385c (most recent call first): - Garbage-collecting - File "C:/Python3x/Lib/site-packages/firebird/driver/interfaces.py", line 831 in free - File "C:/Python3x/Lib/site-packages/firebird/driver/core.py", line 2736 in free - ... - File "C:/Python3x/Scripts/pytest.exe/__main__.py", line 7 in - File "", line 88 in _run_code - File "", line 198 in _run_module_as_main - PASSED [1773/2565] - ========== - The reason of that was explained by Vlad, letter 26.10.24 17:42 - (subject: "oddities when use instances of selective statements"): - * line 'cur1.execute(ps1)' creates a new cursor but looses reference on it; - * but this cursor is linked with instance of ps1 which *has* reference on that cursor; - * call 'ps1.free()' delete this anonimous cursor but Python runtime - (or - maybe - code that makes connection cleanup) does not know about it - and tries to delete this anon cursor AGAIN when code finishes 'with' block. - This attempt causes AV. - + 0. Commits: + 6.x: + 22.03.2025 10:47 + https://github.com/FirebirdSQL/firebird/commit/fc12c0ef392fec9c83d41bc17da3dc233491498c + (Unnest IN/ANY/EXISTS subqueries and optimize them using semi-join algorithm (#8061)) + 5.x + 31.07.2024 09:46 + https://github.com/FirebirdSQL/firebird/commit/4943b3faece209caa93cc9573803677019582f1c + (Added support for semi/anti and outer joins to hash join algorithm ...) + Also: + 14.09.2024 09:24 + https://github.com/FirebirdSQL/firebird/commit/5fa4ae611d18fd4ce9aac1c8dbc79e5fea2bc1f2 + (Fix bug #8252: Incorrect subquery unnesting with complex dependencies) + 1. Parameter 'SubQueryConversion' currently presents only in FB 5.x and _NOT_ in FB 6.x. + 2. Custom driver config objects are created here, one with SubQueryConversion = true and second with false. + 3. First example of this test is also used in tests/functional/tabloid/test_aae2ae32.py + Confirmed problem on 5.0.2.1516-fe6ba50 (23.09.2024). Checked on 5.0.2.1516-92316F0 (25.09.2024). [16.04.2025] pzotov - Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. - Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + Re-implemented in order to check FB 5.x with set 'SubQueryConversion = true' and FB 6.x w/o any changes in its config. + Checked on 6.0.0.687-730aa8f, 5.0.3.1647-8993a57 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -199,7 +168,7 @@ def test_1(act: Action, capsys): if ps: ps.free() - act.expected_stdout = f""" + expected_stdout_5x = f""" 1000 {query_map[1000][0]} {query_map[1000][1]} @@ -268,5 +237,77 @@ def test_1(act: Action, capsys): ............-> Table "TEST1" as "Q4_A" Full Scan 10 """ + + expected_stdout_6x = f""" + 1000 + {query_map[1000][0]} + {query_map[1000][1]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Hash Join (semi) + ................-> Table "PUBLIC"."TEST1" as "Q1_A" Full Scan + ................-> Record Buffer (record length: NN) + ....................-> Filter + ........................-> Hash Join (semi) + ............................-> Table "PUBLIC"."TEST2" as "Q1_B" Full Scan + ............................-> Record Buffer (record length: NN) + ................................-> Filter + ....................................-> Table "PUBLIC"."TEST3" as "Q1_C" Full Scan + 10 + + 2000 + {query_map[2000][0]} + {query_map[2000][1]} + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."TEST3" as "Q2_C" Full Scan + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Hash Join (semi) + ................-> Table "PUBLIC"."TEST1" as "Q2_A" Full Scan + ................-> Record Buffer (record length: NN) + ....................-> Filter + ........................-> Table "PUBLIC"."TEST2" as "Q2_B" Full Scan + 10 + + 3000 + {query_map[3000][0]} + {query_map[3000][1]} + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."TEST3" as "Q3_C" Full Scan + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."TEST2" as "Q3_B" Full Scan + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST1" as "Q3_A" Full Scan + 10 + + 4000 + {query_map[4000][0]} + {query_map[4000][1]} + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."TEST3" as "Q4_C" Full Scan + Sub-query + ....-> Filter + ........-> Filter + ............-> Table "PUBLIC"."TEST2" as "Q4_B" Full Scan + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST1" as "Q4_A" Full Scan + 10 + """ + + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From cc57b0f95838874759cfcc0f4b3b82f955413570 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:24:04 +0300 Subject: [PATCH 25/28] Added/Updated tests\bugs\gh_8290_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8290_test.py | 93 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/tests/bugs/gh_8290_test.py b/tests/bugs/gh_8290_test.py index d0efda58..4ef2293f 100644 --- a/tests/bugs/gh_8290_test.py +++ b/tests/bugs/gh_8290_test.py @@ -9,8 +9,12 @@ For each case we ask engine to show explained plan. Every case must have 'Range Scan (full match)'. NOTES: [25.10.2024] pzotov - Confirmed problem on 6.0.0.485, 5.0.2.1519. - Checked on 6.0.0.502-d2f4cf6, 5.0.2.1542-ab50e20 (intermediate builds). + Confirmed problem on 6.0.0.485, 5.0.2.1519. + Checked on 6.0.0.502-d2f4cf6, 5.0.2.1542-ab50e20 (intermediate builds). + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest @@ -70,7 +74,7 @@ def test_1(act: Action, capsys): ps.free() - expected_out = f""" + expected_stdout_5x = f""" {qry_map[0]} Select Expression ....-> Aggregate @@ -152,7 +156,88 @@ def test_1(act: Action, capsys): ....................-> Index "TEST_X_MINUS_Y_PARTIAL" Range Scan (full match) """ - act.expected_stdout = expected_out + expected_stdout_6x = f""" + {qry_map[0]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_ASC" Range Scan (full match) + + {qry_map[1]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Y_DESC" Range Scan (full match) + + {qry_map[2]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_PLUS_Y" Range Scan (full match) + + {qry_map[3]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Z_PARTIAL" Range Scan (full match) + + {qry_map[4]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_MINUS_Y_PARTIAL" Range Scan (full match) + + {qry_map[5]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_ASC" Range Scan (full match) + + {qry_map[6]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Y_DESC" Range Scan (full match) + + {qry_map[7]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_PLUS_Y" Range Scan (full match) + + {qry_map[8]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_Z_PARTIAL" Range Scan (full match) + + {qry_map[9]} + Select Expression + ....-> Aggregate + ........-> Filter + ............-> Table "PUBLIC"."TEST" Access By ID + ................-> Bitmap + ....................-> Index "PUBLIC"."TEST_X_MINUS_Y_PARTIAL" Range Scan (full match) + """ + act.expected_stdout = expected_stdout_5x if act.is_version('<6') else expected_stdout_6x act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout From 2540bd77541c602f0fac33d73257d596c71ecaca Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 19:27:45 +0300 Subject: [PATCH 26/28] Updated tests\bugs\gh_8434_test.py: adjust expected stdout/stderr to current FB version. --- tests/bugs/gh_8434_test.py | 91 +++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/tests/bugs/gh_8434_test.py b/tests/bugs/gh_8434_test.py index 328f8c56..8c3c9ab3 100644 --- a/tests/bugs/gh_8434_test.py +++ b/tests/bugs/gh_8434_test.py @@ -174,102 +174,95 @@ def test_1(act: Action, capsys): expected_stdout = """ Initial state: - Query: select * from v1_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST1" as "V1_CHK_NR TEST1" Full Scan + ............-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_NR" "PUBLIC"."TEST1" Full Scan 3 lowered "D" normal Query: select * from v1_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_ASC TEST1" Access By ID - ............-> Index "TEST1_ASC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_ASC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_ASC" Range Scan (full match) 3 lowered "D" normal Query: select * from v1_chk_ir_dec Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_DEC TEST1" Access By ID - ............-> Index "TEST1_DEC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_DEC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_DEC" Range Scan (full match) 3 lowered "D" normal Query: select * from v2_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_NR TEST2" Full Scan + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_NR" "PUBLIC"."TEST2" Full Scan 3 lowered "L" normal Query: select * from v2_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST2" as "V2_CHK_IR_ASC TEST2" Access By ID - ............-> Index "TEST2_PARTIAL_ASC" Full Scan + ........-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_ASC" "PUBLIC"."TEST2" Access By ID + ............-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal Query: select * from v2_chk_ir_dec Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_IR_DEC TEST2" Access By ID + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_DEC" "PUBLIC"."TEST2" Access By ID ................-> Bitmap - ....................-> Index "TEST2_PARTIAL_ASC" Full Scan + ....................-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal - - After alter domain dm_utf8 type varchar(1) character set utf8 collate unicode_ci: - Query: select * from v1_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST1" as "V1_CHK_NR TEST1" Full Scan + ............-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_NR" "PUBLIC"."TEST1" Full Scan 3 lowered "D" normal 4 UPPERED "D" normal Query: select * from v1_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_ASC TEST1" Access By ID - ............-> Index "TEST1_ASC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_ASC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_ASC" Range Scan (full match) 3 lowered "D" normal 4 UPPERED "D" normal Query: select * from v1_chk_ir_dec Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_DEC TEST1" Access By ID - ............-> Index "TEST1_DEC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_DEC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_DEC" Range Scan (full match) 4 UPPERED "D" normal 3 lowered "D" normal Query: select * from v2_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_NR TEST2" Full Scan + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_NR" "PUBLIC"."TEST2" Full Scan 3 lowered "L" normal 4 UPPERED "L" normal Query: select * from v2_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST2" as "V2_CHK_IR_ASC TEST2" Access By ID - ............-> Index "TEST2_PARTIAL_ASC" Full Scan + ........-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_ASC" "PUBLIC"."TEST2" Access By ID + ............-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal 4 UPPERED "L" normal Query: select * from v2_chk_ir_dec Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_IR_DEC TEST2" Access By ID + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_DEC" "PUBLIC"."TEST2" Access By ID ................-> Bitmap - ....................-> Index "TEST2_PARTIAL_ASC" Full Scan + ....................-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 4 UPPERED "L" normal 3 lowered "L" normal - - After alter domain dm_utf8 type varchar(1) character set utf8 collate unicode_ci_ai: - Query: select * from v1_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST1" as "V1_CHK_NR TEST1" Full Scan + ............-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_NR" "PUBLIC"."TEST1" Full Scan 1 lowered "D" w/accent 2 UPPERED "D" w/accent 3 lowered "D" normal @@ -277,8 +270,8 @@ def test_1(act: Action, capsys): Query: select * from v1_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_ASC TEST1" Access By ID - ............-> Index "TEST1_ASC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_ASC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_ASC" Range Scan (full match) 3 lowered "D" normal 4 UPPERED "D" normal 1 lowered "D" w/accent @@ -286,8 +279,8 @@ def test_1(act: Action, capsys): Query: select * from v1_chk_ir_dec Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_DEC TEST1" Access By ID - ............-> Index "TEST1_DEC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_DEC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_DEC" Range Scan (full match) 2 UPPERED "D" w/accent 1 lowered "D" w/accent 4 UPPERED "D" normal @@ -296,7 +289,7 @@ def test_1(act: Action, capsys): Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_NR TEST2" Full Scan + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_NR" "PUBLIC"."TEST2" Full Scan 1 lowered "L" w/accent 2 UPPERED "L" w/accent 3 lowered "L" normal @@ -304,8 +297,8 @@ def test_1(act: Action, capsys): Query: select * from v2_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST2" as "V2_CHK_IR_ASC TEST2" Access By ID - ............-> Index "TEST2_PARTIAL_ASC" Full Scan + ........-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_ASC" "PUBLIC"."TEST2" Access By ID + ............-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal 4 UPPERED "L" normal 1 lowered "L" w/accent @@ -314,57 +307,53 @@ def test_1(act: Action, capsys): Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_IR_DEC TEST2" Access By ID + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_DEC" "PUBLIC"."TEST2" Access By ID ................-> Bitmap - ....................-> Index "TEST2_PARTIAL_ASC" Full Scan + ....................-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 2 UPPERED "L" w/accent 1 lowered "L" w/accent 4 UPPERED "L" normal 3 lowered "L" normal - - After alter domain dm_utf8 type varchar(1) character set utf8: - Query: select * from v1_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST1" as "V1_CHK_NR TEST1" Full Scan + ............-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_NR" "PUBLIC"."TEST1" Full Scan 3 lowered "D" normal Query: select * from v1_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_ASC TEST1" Access By ID - ............-> Index "TEST1_ASC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_ASC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_ASC" Range Scan (full match) 3 lowered "D" normal Query: select * from v1_chk_ir_dec Select Expression ....-> Filter - ........-> Table "TEST1" as "V1_CHK_IR_DEC TEST1" Access By ID - ............-> Index "TEST1_DEC" Range Scan (full match) + ........-> Table "PUBLIC"."TEST1" as "PUBLIC"."V1_CHK_IR_DEC" "PUBLIC"."TEST1" Access By ID + ............-> Index "PUBLIC"."TEST1_DEC" Range Scan (full match) 3 lowered "D" normal Query: select * from v2_chk_nr Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_NR TEST2" Full Scan + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_NR" "PUBLIC"."TEST2" Full Scan 3 lowered "L" normal Query: select * from v2_chk_ir_asc Select Expression ....-> Filter - ........-> Table "TEST2" as "V2_CHK_IR_ASC TEST2" Access By ID - ............-> Index "TEST2_PARTIAL_ASC" Full Scan + ........-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_ASC" "PUBLIC"."TEST2" Access By ID + ............-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal Query: select * from v2_chk_ir_dec Select Expression ....-> Sort (record length: NN, key length: MM) ........-> Filter - ............-> Table "TEST2" as "V2_CHK_IR_DEC TEST2" Access By ID + ............-> Table "PUBLIC"."TEST2" as "PUBLIC"."V2_CHK_IR_DEC" "PUBLIC"."TEST2" Access By ID ................-> Bitmap - ....................-> Index "TEST2_PARTIAL_ASC" Full Scan + ....................-> Index "PUBLIC"."TEST2_PARTIAL_ASC" Full Scan 3 lowered "L" normal """ - act.expected_stdout = expected_stdout act.stdout = capsys.readouterr().out From 2e8f362c692653645e44f7f7bf3fa717a2b07999 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sun, 6 Jul 2025 23:58:44 +0300 Subject: [PATCH 27/28] Added/Updated tests\bugs\gh_8203_test.py: Separated expected output for FB major versions prior/since 6.x. --- tests/bugs/gh_8203_test.py | 50 ++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/tests/bugs/gh_8203_test.py b/tests/bugs/gh_8203_test.py index 5604102c..9c40cea6 100644 --- a/tests/bugs/gh_8203_test.py +++ b/tests/bugs/gh_8203_test.py @@ -26,8 +26,12 @@ err.sqlstate='2F000' NOTES: [11.08.2024] pzotov - Confirmed bug on 6.0.0.421, 5.0.1.1469 - Checked on 6.0.0.423, 5.0.2.1477 + Confirmed bug on 6.0.0.421, 5.0.1.1469 + Checked on 6.0.0.423, 5.0.2.1477 + [06.07.2025] pzotov + Separated expected output for FB major versions prior/since 6.x. + No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. + Checked on 6.0.0.914; 5.0.3.1668. """ import pytest from firebird.qa import * @@ -192,22 +196,38 @@ def test_1(act: Action, capsys): for bound_points, range_name in UNICODE_RANGES_MAP.items(): for iter in range(1,REPEAT_CHECKS_FOR_SELECTED_UNICODE_RANGE+1): - table_random_unicode_name = get_random_unicode( random.randint(NAME_MIN_LEN, NAME_MAX_LEN), bound_points ) table_random_unicode_name = ''.join(c for c in table_random_unicode_name if c not in CHARS_TO_SKIP) - - test_sql = f""" - recreate table "{table_random_unicode_name.replace('"','""')}"(id int) - ^ - create or alter procedure sp_chk as - declare id1 int; - begin - select /* {range_name=} {iter=} */ id from "{table_random_unicode_name.replace('"','""')}" where rdb$db_key = make_dbkey('{table_random_unicode_name}', 0) into id1; - end - ^ - """ - # select id from "{table_random_unicode_name.replace('"','""')}" where rdb$db_key = make_dbkey('{table_random_unicode_name.replace("'","''")}', 0) into id1; + table_random_uname_quoted = table_random_unicode_name.replace('"','""') + if act.is_version('<6'): + test_sql = f""" + recreate table "{table_random_uname_quoted}"(id int) + ^ + create or alter procedure sp_chk as + declare id1 int; + begin + select /* {range_name=} {iter=} */ id + from "{table_random_uname_quoted}" + where rdb$db_key = make_dbkey('{table_random_unicode_name}', 0) + into id1; + end + ^ + """ + else: + test_sql = f""" + recreate table "{table_random_uname_quoted}"(id int) + ^ + create or alter procedure sp_chk as + declare id1 int; + begin + select /* {range_name=} {iter=} */ id + from "{table_random_uname_quoted}" + where rdb$db_key = make_dbkey('"{table_random_uname_quoted}"', 0) + into id1; + end + ^ + """ expected_txt = f'{iter=} of {REPEAT_CHECKS_FOR_SELECTED_UNICODE_RANGE=}: SUCCESS' with act.db.connect(charset = 'utf-8') as con: From a365bf597a7a63e38875ef16d5b0abc5ed6ca8cb Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Mon, 7 Jul 2025 00:23:13 +0300 Subject: [PATCH 28/28] Added/Updated tests\bugs\gh_8203_test.py: added comments related to need of quoting of table names in make_dbkey() func. --- tests/bugs/gh_8203_test.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/bugs/gh_8203_test.py b/tests/bugs/gh_8203_test.py index 9c40cea6..cb374d95 100644 --- a/tests/bugs/gh_8203_test.py +++ b/tests/bugs/gh_8203_test.py @@ -29,6 +29,11 @@ Confirmed bug on 6.0.0.421, 5.0.1.1469 Checked on 6.0.0.423, 5.0.2.1477 [06.07.2025] pzotov + ::: NB ::: See doc/sql.extensions/README.schemas.md + When working with object names in ... `MAKE_DBKEY`, the names containing special characters or lowercase + letters must be enclosed in quotes ... In earlier versions, `MAKE_DBKEY` required an exact table name as + its first parameter and did not support the use of double quotes for special characters. + ---------------------------------------------------------------------------------------- Separated expected output for FB major versions prior/since 6.x. No substitutions are used to suppress schema and quotes. Discussed with dimitr, 24.06.2025 12:39. Checked on 6.0.0.914; 5.0.3.1668. @@ -215,6 +220,11 @@ def test_1(act: Action, capsys): ^ """ else: + # ::: NB ::: See doc/sql.extensions/README.schemas.md + # When working with object names in ... `MAKE_DBKEY`, the names containing special characters or lowercase + # letters must be enclosed in quotes ... In earlier versions, `MAKE_DBKEY` required an exact table name as + # its first parameter and did not support the use of double quotes for special characters. + # test_sql = f""" recreate table "{table_random_uname_quoted}"(id int) ^ @@ -224,6 +234,9 @@ def test_1(act: Action, capsys): select /* {range_name=} {iter=} */ id from "{table_random_uname_quoted}" where rdb$db_key = make_dbkey('"{table_random_uname_quoted}"', 0) + -- | | + -- | | + -- +----- required in 6.x ----+ into id1; end ^