diff --git a/tests/bugs/gh_4314_test.py b/tests/bugs/gh_4314_test.py new file mode 100644 index 00000000..581c3be1 --- /dev/null +++ b/tests/bugs/gh_4314_test.py @@ -0,0 +1,122 @@ +#coding:utf-8 + +""" +ID: issue-4314 +ISSUE: https://github.com/FirebirdSQL/firebird/issues/4314 +TITLE: Sub-optimal predicate checking while selecting from a view [CORE3981] +DESCRIPTION: +NOTES: + [20.08.2024] pzotov + Checked on 6.0.0.438, 5.0.2.1479, 4.0.6.3142, 3.0.12.33784. +""" +import locale +import re + +import pytest +from firebird.qa import * + +init_sql = """ + recreate table rr( + rel_name varchar(63) + ,id int + ,fid int + ); + + recreate table rf( + rel_name varchar(63) + ,fid int + ,fnm varchar(63) + ); + insert into rr + select a.rdb$relation_name, a.rdb$relation_id, a.rdb$field_id + from rdb$relations a + ; + insert into rf select f.rdb$relation_name, f.rdb$field_id, f.rdb$field_name + from rdb$relation_fields f + ; + commit; + + alter table rr add constraint rr_rel_name_unq unique (rel_name); + create index rr_id on rr (id); + + alter table rf add constraint rf_fnm_rel_name_unq unique(fnm, rel_name); + create index rf_rel_name on rf(rel_name); + + recreate view v as + select r.rel_name, abs(r.id) as id + from rr r + left + join rf on r.rel_name = rf.rel_name and r.fid = rf.fid + where r.id < 128; + + set statistics index rr_rel_name_unq; + set statistics index rr_id; + set statistics index rf_fnm_rel_name_unq; + set statistics index rf_rel_name; + commit; +""" + +db = db_factory(init = init_sql) +act = python_act('db') + +#----------------------------------------------------------- + +def replace_leading(source, char="."): + stripped = source.lstrip() + return char * (len(source) - len(stripped)) + stripped + +#----------------------------------------------------------- + +@pytest.mark.version('>=3.0') +def test_1(act: Action, capsys): + + query_from_view = """ + select /* trace_tag: VIEW */ v.rel_name as v_rel_name, v.id as v_id from v + where id = 0 + """ + query_from_dt = """ + select /* trace_tag: DERIVED TABLE */ d.rel_name as d_rel_name, d.id as d_id + from ( + select r.rel_name, abs(r.id) as id + from rr r + left + join rf on r.rel_name = rf.rel_name and r.fid = rf.fid + where r.id < 128 + ) d + where d.id = 0 + """ + + with act.db.connect() as con: + cur = con.cursor() + for test_sql in (query_from_view, query_from_dt): + ps = cur.prepare(test_sql) + print( '\n'.join([replace_leading(s) for s in ps.detailed_plan.split('\n')]) ) + + expected_stdout = """ + Select Expression + ....-> Filter + ........-> Nested Loop Join (outer) + ............-> Filter + ................-> Table "RR" as "V R" Access By ID + ....................-> Bitmap + ........................-> Index "RR_ID" Range Scan (upper bound: 1/1) + ............-> Filter + ................-> Table "RF" as "V RF" Access By ID + ....................-> Bitmap + ........................-> Index "RF_REL_NAME" Range Scan (full match) + + Select Expression + ....-> Filter + ........-> Nested Loop Join (outer) + ............-> Filter + ................-> Table "RR" as "D R" Access By ID + ....................-> Bitmap + ........................-> Index "RR_ID" Range Scan (upper bound: 1/1) + ............-> Filter + ................-> Table "RF" as "D RF" Access By ID + ....................-> Bitmap + ........................-> Index "RF_REL_NAME" Range Scan (full match) + """ + act.expected_stdout = expected_stdout + act.stdout = capsys.readouterr().out + assert act.clean_stdout == act.clean_expected_stdout