From 3bbf0047ba9f645e37bc6ba31dec821f6af4d23a Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Mon, 13 Jan 2025 13:12:28 +0300 Subject: [PATCH] Added/Updated tests\bugs\core_2650_test.py: Separated expected_out for FB 6.x after #b2d03 (More correct plan output for subqueries generated during NOT IN transformation). See notes. --- tests/bugs/core_2650_test.py | 334 ++++++++++++++++++++++++++++++++++- 1 file changed, 327 insertions(+), 7 deletions(-) diff --git a/tests/bugs/core_2650_test.py b/tests/bugs/core_2650_test.py index 2836dea3..2dfd5d87 100644 --- a/tests/bugs/core_2650_test.py +++ b/tests/bugs/core_2650_test.py @@ -17,7 +17,7 @@ DESCRIPTION: JIRA: CORE-2650 FBTEST: bugs.core_2650 NOTES: - [02.09.2024] + [02.09.2024] pzotov 1. Test was fully re-implemented in order to have ability to see both query and its comment in case of mismatch. The 'f'-notation is used in expected output with substitution query text and comments to it, e.g.: {query_map[1000][0]} // output will be compared with: "select txt_short from test a01 order by id" @@ -37,6 +37,13 @@ NOTES: https://github.com/FirebirdSQL/firebird/commit/901b4ced9a3615929e0027d42ebb2392e943b205 Checked on 6.0.0.447-901b4ce, 5.0.2.1487, 4.0.6.3142 + + [13.01.2025] pzotov + 6. Separated expected_out for FB 6.x after b2d03 2025.01.10 ("More correct plan output for subqueries generated during NOT IN transformation") + 7. Parameter OptimizeForFirstRows must have default value fcor this test (i.e. false). To prevent test fail in case of occasional changing of + this parameter, session-level command is used for FB 5.x+: 'set optimize for all rows'. + + Checked 6.0.0.573-c20f37a """ import pytest @@ -212,11 +219,6 @@ query_map = { } -test_script = """ - set plan on; - set explain on; -""" - ############################################################################### fb4x_expected_out = f""" @@ -859,6 +861,316 @@ fb5x_expected_out = f""" ........-> Table "TEST_NS_06" Full Scan """ + +############################################################################### + +fb6x_expected_out = f""" + 1000 + select txt_short from test a01 order by id + Must NOT use refetch because length of non-key column is less than threshold + Select Expression + ....-> Sort (record length: 1036, key length: 8) + ........-> Table "TEST" as "A01" Full Scan + 1010 + select txt_broad from test a02 order by id + MUST use refetch because length of non-key column is greater than threshold + Select Expression + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Table "TEST" as "A02" Full Scan + 1020 + select txt_short from test a03 order by id rows 1 + MUST use refetch regardless on length of column because ROWS presents + Select Expression + ....-> First N Records + ........-> Refetch + ............-> Sort (record length: 28, key length: 8) + ................-> Table "TEST" as "A03" Full Scan + 2000 + select id, computed_ts_dup from test order by id + Must NOT use refetch because computed column is based on txt_short with length < threshold + Select Expression + ....-> Sort (record length: 1036, key length: 8) + ........-> Table "TEST" Full Scan + 2010 + select id, computed_tb_dup from test order by id + MUST use refetch because computed column is based on txt_broad which has length >= threshold + Select Expression + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Table "TEST" Full Scan + 3000 + select id from test a04 where '' in (select txt_short from test x04 where txt_short = '' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Sort (record length: 1036, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X04" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A04" Full Scan + 3010 + select id from test a05 where '' in (select txt_broad from test x05 where txt_broad = '' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Refetch + ............-> Sort (record length: 28, key length: 8) + ................-> Filter + ....................-> Table "TEST" as "X05" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A05" Full Scan + 3020 + select id from test a06 where '' not in (select txt_short from test x06 where txt_short>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Sort (record length: 1036, key length: 8) + ........-> Filter + ............-> Table "TEST" as "X06" Full Scan + Sub-query (invariant) + ....-> Sort (record length: 1036, key length: 8) + ........-> Filter + ............-> Table "TEST" as "X06" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A06" Full Scan + 3030 + select id from test a07 where '' not in (select txt_broad from test x07 where txt_broad>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X07" Full Scan + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X07" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A07" Full Scan + 3040 + select id from test a08 where '' > all (select id from test x08 where txt_short>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Sort (record length: 1036, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X08" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A08" Full Scan + 3050 + select id from test a09 where '' > all (select id from test x09 where txt_broad>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Refetch + ............-> Sort (record length: 28, key length: 8) + ................-> Filter + ....................-> Table "TEST" as "X09" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A09" Full Scan + 3060 + select id from test a10 where '' <> any (select id from test x10 where txt_short>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Sort (record length: 1036, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X10" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A10" Full Scan + 3070 + select id from test a11 where '' <> any (select id from test x11 where txt_broad>'' order by id) + *** not [yet] commented *** + Sub-query (invariant) + ....-> Filter + ........-> Refetch + ............-> Sort (record length: 28, key length: 8) + ................-> Filter + ....................-> Table "TEST" as "X11" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A11" Full Scan + 4000 + select id,txt_short from test a12 where exists(select 1 from test x12 where txt_short>'' order by id) + MUST use refetch: column x12.txt_short not present in order by + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X12" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A12" Full Scan + 4010 + select id,txt_short from test a13 where exists(select 1 from test x13 where computed_id_dup > 0 order by id) + Must NOT use refetch: ORDER BY list contains the single element: ID, and it is base for x13.computed_id_dup column + Sub-query (invariant) + ....-> Sort (record length: 28, key length: 8) + ........-> Filter + ............-> Table "TEST" as "X13" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A13" Full Scan + 4020 + select id,txt_short from test a14 where exists(select 1 from test x14 where computed_id_dup > 0 order by computed_id_dup) + MUST use refetch! See letter from dimitr 28.12.2020 14:49 + Sort procedure will get: + a KEY = result of evaluating 'computed_id_dup'; + a VAL = value of the field 'ID' which is base for computing 'computed_id_dup' + Thus sorter will have a field which not equals to a key, which leads to refetch. + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 36, key length: 12) + ............-> Filter + ................-> Table "TEST" as "X14" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A14" Full Scan + 4030 + select id,txt_short from test a15 where exists(select 1 from test x15 where f02>0 and f01>0 order by f01, f02) + Must NOT use refetch: all persistent columns from WHERE expression (f01, f02) belong to ORDER BY list + Sub-query (invariant) + ....-> Sort (record length: 36, key length: 16) + ........-> Filter + ............-> Table "TEST" as "X15" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A15" Full Scan + 4040 + select id,txt_short from test a16 where exists(select 1 from test x16 where id>0 and f01>0 order by f01, f02) + Must use refetch: one of columns from WHERE expr (id) does not belong to ORDER BY list + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 36, key length: 16) + ............-> Filter + ................-> Table "TEST" as "X16" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A16" Full Scan + 4050 + select id,txt_short from test a17 where exists(select 1 from test x17 where computed_id_dup > 0 order by f01) + Must use refetch: computed column in WHERE expr does not belong to ORDER BY list + Sub-query (invariant) + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "X17" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A17" Full Scan + 4060 + select id,txt_short from test a18 where exists(select 1 from test x18 where computed_guid > '' order by f01) + Must NOT use refetch: computed column x18.computed_guid does is evaluated via GUID and does not refer to any columns + Sub-query (invariant) + ....-> Sort (record length: 28, key length: 8) + ........-> Filter + ............-> Table "TEST" as "X18" Full Scan + Select Expression + ....-> Filter (preliminary) + ........-> Table "TEST" as "A18" Full Scan + 4070 + with recursive + r as ( + select a19.id, a19.txt_short + from test a19 + where not exists(select * from test x where x.txt_short < a19.txt_short order by id) + UNION ALL + select i.id, i.txt_short + from test i + join r on i.id > r.id + and not exists( select * from test x where x.txt_short between r.txt_short and i.txt_short order by id ) + ) + select * from r + MUST use refetch both in anchor and recursive parts + Sub-query + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "R X" Full Scan + Sub-query + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Filter + ................-> Table "TEST" as "R X" Full Scan + Select Expression + ....-> Recursion + ........-> Filter + ............-> Table "TEST" as "R A19" Full Scan + ........-> Filter + ............-> Table "TEST" as "R I" Full Scan + 5000 + select txt_broad from v_unioned v01 order by id + Must NOT use refetch because view DDL includes UNION + Select Expression + ....-> Sort (record length: 4044, key length: 8) + ........-> First N Records + ............-> Union + ................-> Table "TEST" as "V01 TEST" Full Scan + ................-> Table "RDB$DATABASE" as "V01 RDB$DATABASE" Full Scan + 6000 + select left(txt_broad, 50) as txt from test a21 order by id + MUST use refetch because expression is based on column which has length >= threshold + (even if final length of expression result is much less than threshold) + Select Expression + ....-> Refetch + ........-> Sort (record length: 28, key length: 8) + ............-> Table "TEST" as "A21" Full Scan + 6010 + select left( txt_short || txt_short, 2000) as txt from test a22 order by id + Must NOT use refetch because expression is based on column which has length < threshold + (even if final length of expression result is much bigger than threshold) + Select Expression + ....-> Sort (record length: 1036, key length: 8) + ........-> Table "TEST" as "A22" Full Scan + 7000 + select * from test_ns_01 a23 order by id + MUST use refetch + Select Expression + ....-> Refetch + ........-> Sort (record length: 44, key length: 24) + ............-> Table "TEST_NS_01" as "A23" Full Scan + 7010 + select * from test_ns_02 a24 order by id + Must NOT refetch + Select Expression + ....-> Sort (record length: 1052, key length: 24) + ........-> Table "TEST_NS_02" as "A24" Full Scan + 7020 + select * from test_ns_03 order by id + MUST use refetch + Select Expression + ....-> Refetch + ........-> Sort (record length: 36, key length: 12) + ............-> Table "TEST_NS_03" Full Scan + 7030 + select * from test_ns_04 order by id + Must NOT use refetch + Select Expression + ....-> Sort (record length: 1036, key length: 12) + ........-> Table "TEST_NS_04" Full Scan + 7040 + select * from test_ns_05 order by id + MUST use refetch + Select Expression + ....-> Refetch + ........-> Sort (record length: 36, key length: 12) + ............-> Table "TEST_NS_05" Full Scan + 7050 + select * from test_ns_06 order by id + Must NOT use refetch + Select Expression + ....-> Sort (record length: 1036, key length: 12) + ........-> Table "TEST_NS_06" Full Scan +""" + act = python_act('db') #----------------------------------------------------------- @@ -872,6 +1184,14 @@ def replace_leading(source, char="."): @pytest.mark.version('>=4.0') def test_1(act: Action, capsys): with act.db.connect() as con: + + # 13.01.2025: test will FAIL if config parameter OptimizeForFirstRows differs from default value (i.e. is set to true). + # To prevent this, we have to explicitly change appropriate session-level value: + if act.is_version('<5'): + pass + else: + con.execute_immediate('set optimize for all rows') + cur = con.cursor() for q_idx, q_tuple in query_map.items(): test_sql, qry_comment = q_tuple[:2] @@ -882,6 +1202,6 @@ def test_1(act: Action, capsys): print( '\n'.join([replace_leading(s) for s in ps.detailed_plan.split('\n')]) ) ps.free() - act.expected_stdout = fb4x_expected_out if act.is_version('<5') else fb5x_expected_out + act.expected_stdout = fb4x_expected_out if act.is_version('<5') else fb5x_expected_out if act.is_version('<6') else fb6x_expected_out act.stdout = capsys.readouterr().out assert act.clean_stdout == act.clean_expected_stdout