#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