mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 21:43:06 +01:00
156 lines
7.0 KiB
Python
156 lines
7.0 KiB
Python
#coding:utf-8
|
|
|
|
"""
|
|
ID: issue-7398
|
|
ISSUE: https://github.com/FirebirdSQL/firebird/issues/7398
|
|
TITLE: Worst plan sort created to execute an indexed tables
|
|
DESCRIPTION:
|
|
NOTES:
|
|
[29.09.2024] pzotov
|
|
1. Ineffective execution plan was up to 4.0.3.2840.
|
|
Since 4.0.3.2843 plan changed and is the same for all subsequent FB-4.x snapshots.
|
|
Commit: https://github.com/FirebirdSQL/firebird/commit/1b192404d43a15d403b5ff92760bc5df9d3c89c3
|
|
(13.09.2022 19:17, "More complete solution for #3357 and #7118")
|
|
|
|
2. Database provided in the ticket has too big size (~335 Mb).
|
|
Test uses much smaller DB that was created on basis of original one by
|
|
extraction of small portions of data from tables PCP_TIN_REC_MAT and INV_ETQ_MAT.
|
|
These tables in original DB have 114115 and 1351211 rows.
|
|
In DB that is used here these tables have 15000 and 30000 rows corresp.
|
|
NOT all constraints are used in the test DB. Particularly, following DDL were abandoned:
|
|
ALTER TABLE PCP_TIN_REC ADD CONSTRAINT FK_PCP_TIN_REC_EMP FOREIGN KEY (ID_EMP) REFERENCES SYS_EMP (ID_EMP);
|
|
ALTER TABLE PCP_TIN_REC ADD CONSTRAINT FK_PCP_TIN_REC_OP FOREIGN KEY (ID_OP) REFERENCES PCP_OP (ID_OP);
|
|
ALTER TABLE PCP_TIN_REC_MAT ADD CONSTRAINT FK_PCP_TIN_REC_MAT_MAT FOREIGN KEY (ID_MAT) REFERENCES INV_MAT (ID_MAT);
|
|
Test database have been backed up using 4.0.3.2840 and compressed to .zip file.
|
|
3. Because of missed valuable part of source data, I'm not sure that this test verifies exactly ticket issue.
|
|
But in any case, using this test one may see difference in execution plan that is produced in 4.0.3.2840 and 4.0.3.2843.
|
|
And such difference also can be seen on original DB (although plans there differ from those which are in test DB).
|
|
|
|
Checked on 6.0.0.471, 5.0.2.1519, 4.0.6.3157.
|
|
"""
|
|
|
|
import locale
|
|
import re
|
|
import zipfile
|
|
from pathlib import Path
|
|
from firebird.driver import SrvRestoreFlag
|
|
import time
|
|
|
|
import pytest
|
|
from firebird.qa import *
|
|
|
|
db = db_factory()
|
|
|
|
act = python_act('db')
|
|
|
|
check_sql = """
|
|
select r.id_op, r.id_rec, sum(m.q_mat * cus.cus_med)
|
|
from pcp_tin_rec r
|
|
join pcp_tin_rec_mat m on r.id_rec = m.id_rec
|
|
join inv_etq_mat cus on cus.id_mat = m.id_mat and cus.anomes = r.am_bai
|
|
join inv_etq_nat nat on nat.id_nat = cus.id_nat
|
|
where
|
|
nat.cml_stat = 1 and r.id_op = 216262
|
|
group by r.id_op, r.id_rec
|
|
"""
|
|
|
|
fbk_file = temp_file('gh_7398.tmp.fbk')
|
|
|
|
#-----------------------------------------------------------
|
|
|
|
def replace_leading(source, char="."):
|
|
stripped = source.lstrip()
|
|
return char * (len(source) - len(stripped)) + stripped
|
|
|
|
#-----------------------------------------------------------
|
|
|
|
expected_out_4x = """
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Sort (record length: 148, key length: 16)
|
|
............-> Nested Loop Join (inner)
|
|
................-> Filter
|
|
....................-> Table "PCP_TIN_REC" as "R" Full Scan
|
|
................-> Filter
|
|
....................-> Table "PCP_TIN_REC_MAT" as "M" Access By ID
|
|
........................-> Bitmap
|
|
............................-> Index "FK_PCP_TIN_REC_MAT_REC" Range Scan (full match)
|
|
................-> Filter
|
|
....................-> Table "INV_ETQ_MAT" as "CUS" Access By ID
|
|
........................-> Bitmap
|
|
............................-> Index "IDX_INV_ETQ_MAT_ANOMES" Range Scan (full match)
|
|
................-> Filter
|
|
....................-> Table "INV_ETQ_NAT" as "NAT" Access By ID
|
|
........................-> Bitmap
|
|
............................-> Index "PK_INV_ETQ_NAT" Unique Scan
|
|
"""
|
|
expected_out_5x = """
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Sort (record length: 148, key length: 16)
|
|
............-> Filter
|
|
................-> Hash Join (inner)
|
|
....................-> Nested Loop Join (inner)
|
|
........................-> Filter
|
|
............................-> Table "PCP_TIN_REC" as "R" Full Scan
|
|
........................-> Filter
|
|
............................-> Table "PCP_TIN_REC_MAT" as "M" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "FK_PCP_TIN_REC_MAT_REC" Range Scan (full match)
|
|
........................-> Filter
|
|
............................-> Table "INV_ETQ_MAT" as "CUS" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "IDX_INV_ETQ_MAT_ANOMES" Range Scan (full match)
|
|
....................-> Record Buffer (record length: 33)
|
|
........................-> Filter
|
|
............................-> Table "INV_ETQ_NAT" as "NAT" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "IDX_INV_ETQ_NAT_CML_STAT" Range Scan (full match)
|
|
"""
|
|
expected_out_6x = """
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Sort (record length: 148, key length: 16)
|
|
............-> Filter
|
|
................-> Hash Join (inner) (keys: 1, total key length: 4)
|
|
....................-> Nested Loop Join (inner)
|
|
........................-> Filter
|
|
............................-> Table "PCP_TIN_REC" as "R" Full Scan
|
|
........................-> Filter
|
|
............................-> Table "PCP_TIN_REC_MAT" as "M" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "FK_PCP_TIN_REC_MAT_REC" Range Scan (full match)
|
|
........................-> Filter
|
|
............................-> Table "INV_ETQ_MAT" as "CUS" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "IDX_INV_ETQ_MAT_ANOMES" Range Scan (full match)
|
|
....................-> Record Buffer (record length: 33)
|
|
........................-> Filter
|
|
............................-> Table "INV_ETQ_NAT" as "NAT" Access By ID
|
|
................................-> Bitmap
|
|
....................................-> Index "IDX_INV_ETQ_NAT_CML_STAT" Range Scan (full match)
|
|
"""
|
|
|
|
@pytest.mark.version('>=4.0')
|
|
def test_1(act: Action, fbk_file: Path, capsys):
|
|
zipped_fbk_file = zipfile.Path(act.files_dir / 'gh_7398.zip', at = 'gh_7398.fbk')
|
|
fbk_file.write_bytes(zipped_fbk_file.read_bytes())
|
|
|
|
with act.connect_server(encoding=locale.getpreferredencoding()) as srv:
|
|
srv.database.restore(database = act.db.db_path, backup = fbk_file, flags = SrvRestoreFlag.REPLACE)
|
|
restore_log = srv.readlines()
|
|
assert restore_log == []
|
|
|
|
|
|
with act.db.connect() as con:
|
|
chk_sql = 'select 1 from test order by id'
|
|
cur = con.cursor()
|
|
ps = cur.prepare(check_sql)
|
|
# 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')]) )
|
|
|
|
act.expected_stdout = expected_out_4x if act.is_version('<5') else expected_out_5x if act.is_version('<6') else expected_out_6x
|
|
|
|
act.stdout = capsys.readouterr().out
|
|
assert act.clean_stdout == act.clean_expected_stdout
|