OR'ed optimization in 3.0 is ineffective when `WHERE` clause contains two 
expression and they do NOT match order of SP input arguments
--------------------------------------------------------------------------------------------------------------------------------------

                 Key: CORE-4537
                 URL: http://tracker.firebirdsql.org/browse/CORE-4537
             Project: Firebird Core
          Issue Type: Bug
    Affects Versions: 3.0 Alpha 2
            Reporter: Pavel Zotov


DDL:
####

create or alter procedure null_arg_force_idx_usage as begin end;
create or alter procedure null_arg_optimization_test_01 as begin end;
create or alter procedure null_arg_optimization_test_02 as begin end;
recreate table tmp(id int, doc_id int, qty int);
commit;

set term ^;
execute block as
declare i int = 0;
declare n int = 1000000;
begin
while(i<n) do insert into tmp(id, doc_id, qty) values(:i, rand()*100, 1) 
returning :i+1 into i;
end
^ set term ;^
commit;

commit;
create unique index tmp_id_unq on tmp(id);
create index tmp_doc_id on tmp(doc_id);
commit;

set term ^;

create or alter procedure null_arg_force_idx_usage(
a_doc_list_id bigint default null
,a_doc_data_id bigint default null
)
returns(
    dd_first_id int,
    dd_first_qty numeric(12,2)
)
as
begin
        select d.id, d.qty
        from tmp d
        where
            d.id between coalesce( :a_doc_data_id, -2147483648) and coalesce( 
:a_doc_data_id, 2147483647)
            and
            d.doc_id between coalesce( :a_doc_list_id, -2147483648) and 
coalesce( :a_doc_list_id, 2147483647)
        rows 1
        into dd_first_id, dd_first_qty;
        suspend;
end
^

create or alter procedure null_arg_optimization_test_01(
a_doc_list_id bigint default null
,a_doc_data_id bigint default null
)
returns(
    dd_first_id int,
    dd_first_qty numeric(12,2)
)
as
begin
        select d.id, d.qty
        from tmp d
        where -- OR'ed optimization, 3.0 only
            (d.doc_id  = :a_doc_list_id or :a_doc_list_id is null)
            and
            (d.id = :a_doc_data_id or :a_doc_data_id is null)
        rows 1
        into dd_first_id, dd_first_qty;
        suspend;
end
^

create or alter procedure null_arg_optimization_test_02(
a_doc_list_id bigint default null
,a_doc_data_id bigint default null
)
returns(
    dd_first_id int,
    dd_first_qty numeric(12,2)
)
as
begin
        select d.id, d.qty
        from tmp d
        where -- OR'ed optimization, 3.0 only
            (d.id = :a_doc_data_id or :a_doc_data_id is null)
            and
            (d.doc_id  = :a_doc_list_id or :a_doc_list_id is null)
        rows 1
        into dd_first_id, dd_first_qty;
        suspend;
end
^
set term ;^
commit;

So, the table tmp(id int, doc_id int, qty int) contains 10^6 rows and we have 
three procedures which all take two arguments (both nullable) and returns first 
record from TMP which match to one and/or two values of input args (or returns 
first record if no arguments passed).

Procedure `null_arg_force_idx_usage` does not use so called "OR'ed 
optimization" - it only forces FB to use indexes via BETWEEN + pair of literals 
which covers all possible values of input args. It is provided here only for 
comparison.

Procedures `null_arg_optimization_test_01` and `null_arg_optimization_test_02` 
differs only in the order of expressions in WHERE-clause.

The following samples shows statistics of these SPs.

1.1 select * from null_arg_force_idx_usage(null, null) ==> IR = 1, NIR = 0;  
174 ms, 1331 fetch(es)
1.2 select * from null_arg_optimization_test_01(null, null) ==> IR = 0, NIR = 
1; 0 ms, 6 fetch(es) -- ok, but why NATURAL scan here ?
1.3 select * from null_arg_optimization_test_02(null, null) ==> IR = 0, NIR = 
1; 0 ms, 6 fetch(es) -- the same as 1.2

2.1 select * from null_arg_force_idx_usage(1) ==> IR = 1, NIR = 0; 23 ms, 738 
fetch(es)
2.2 select * from null_arg_optimization_test_01(1) ==> IR = 1, NIR = 0; 0 ms, 
11 fetch(es) -- again: why NATURAL scan here ?
2.3 select * from null_arg_optimization_test_02(1) ==> IR = 0, NIR = 145; 0 ms, 
296 fetch(es) -- the same as 2.2

3.1 select * from null_arg_force_idx_usage(-1); ==> no statistics, 21 ms, 730 
fetch(es)
3.2 select * from null_arg_optimization_test_01(-1); ==> no statistics, 0 ms, 3 
fetch(es)
3.3 select * from null_arg_optimization_test_02(-1); ==> IR = 0, NIR = 
1'000'000 (!), 938 ms, 2014194 fetch(es) --  quite ineffective

4.1 select * from null_arg_force_idx_usage(null,1); ==> IR=1; 14 ms, 607 
fetch(es)
4.2 select * from null_arg_optimization_test_01(null,1); ==> IR = 0, NIR=2; 0 
ms, 8 fetch(es) -- why TWO naturals here ?
4.3 select * from null_arg_optimization_test_02(null,1); ==> IR=0, NIR=1

5.1 select * from null_arg_force_idx_usage(null,-1); ==> no statistics; 0 ms, 3 
fetch(es)
5.2 select * from null_arg_optimization_test_01(null,-1); ==> IR=0, 
NIR=1'000'000(!); 938 ms, 2014194 fetch(es) --  quite ineffective
5.3 select * from null_arg_optimization_test_02(null,-1); ==> no statistics; 0 
ms, 3 fetch(es)


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://tracker.firebirdsql.org/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
Firebird-Devel mailing list, web interface at 
https://lists.sourceforge.net/lists/listinfo/firebird-devel

Reply via email to