mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 13:33:07 +01:00
159 lines
5.5 KiB
Python
159 lines
5.5 KiB
Python
#coding:utf-8
|
|
|
|
"""
|
|
ID: issue-8290
|
|
ISSUE: https://github.com/FirebirdSQL/firebird/issues/8290
|
|
TITLE: "Unique scan" is incorrectly reported in the explained plan for unique index and IS NULL predicate
|
|
DESCRIPTION:
|
|
Test creates a table and checks several cases related to issue: asc/desc, computed-by and partial indices.
|
|
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).
|
|
"""
|
|
|
|
import pytest
|
|
from firebird.qa import *
|
|
|
|
init_sql = """
|
|
recreate table test(id int generated by default as identity, x int, y int, z int);
|
|
insert into test(x, y, z) select null, null, null from rdb$types, rdb$types rows 1000;
|
|
commit;
|
|
create unique index test_x_asc on test(x);
|
|
create unique descending index test_y_desc on test(y);
|
|
create unique index test_x_plus_y on test computed by (x+y);
|
|
|
|
create unique index test_z_partial on test(z) where mod(id,2) = 0;
|
|
create unique index test_x_minus_y_partial on test computed by (x-y) where mod(id,3) <= 1;
|
|
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('>=5.0.2')
|
|
def test_1(act: Action, capsys):
|
|
|
|
qry_map = {
|
|
0 : 'select count(*) from test where x is null'
|
|
,1 : 'select count(*) from test where y is null'
|
|
,2 : 'select count(*) from test where x+y is null'
|
|
,3 : 'select count(*) from test where z is null and mod(id,2) = 0'
|
|
,4 : 'select count(*) from test where x-y is null and mod(id,3) <= 1'
|
|
,5 : 'select count(*) from test where x is not distinct from null'
|
|
,6 : 'select count(*) from test where y is not distinct from null'
|
|
,7 : 'select count(*) from test where x+y is not distinct from null'
|
|
,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'
|
|
}
|
|
|
|
with act.db.connect() as con:
|
|
cur = con.cursor()
|
|
for k,v in qry_map.items():
|
|
ps = cur.prepare(v)
|
|
# Print explained plan with padding eash line by dots in order to see indentations:
|
|
print(v)
|
|
print( '\n'.join([replace_leading(s) for s in ps.detailed_plan.split('\n')]) )
|
|
print('')
|
|
|
|
# 26.10.2024. ::: ACHTUNG :::
|
|
# MANDATORY OTHERWISE PYTEST WILL HANG AT FINAL POINT:
|
|
ps.free()
|
|
|
|
|
|
expected_out = f"""
|
|
{qry_map[0]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_ASC" Range Scan (full match)
|
|
|
|
{qry_map[1]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_Y_DESC" Range Scan (full match)
|
|
|
|
{qry_map[2]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_PLUS_Y" Range Scan (full match)
|
|
|
|
{qry_map[3]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_Z_PARTIAL" Range Scan (full match)
|
|
|
|
{qry_map[4]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_MINUS_Y_PARTIAL" Range Scan (full match)
|
|
|
|
{qry_map[5]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_ASC" Range Scan (full match)
|
|
|
|
{qry_map[6]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_Y_DESC" Range Scan (full match)
|
|
|
|
{qry_map[7]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_PLUS_Y" Range Scan (full match)
|
|
|
|
{qry_map[8]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_Z_PARTIAL" Range Scan (full match)
|
|
|
|
{qry_map[9]}
|
|
Select Expression
|
|
....-> Aggregate
|
|
........-> Filter
|
|
............-> Table "TEST" Access By ID
|
|
................-> Bitmap
|
|
....................-> Index "TEST_X_MINUS_Y_PARTIAL" Range Scan (full match)
|
|
"""
|
|
|
|
act.expected_stdout = expected_out
|
|
|
|
act.stdout = capsys.readouterr().out
|
|
assert act.clean_stdout == act.clean_expected_stdout
|