mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Teach optimizer's predtest.c more things about ScalarArrayOpExpr.
In particular, make it possible to prove/refute "x IS NULL" and "x IS NOT NULL" predicates from a clause involving a ScalarArrayOpExpr even when we are unable or unwilling to deconstruct the expression into an AND/OR tree. This avoids a former unexpected degradation of plan quality when the size of an ARRAY[] expression or array constant exceeded the arbitrary MAX_SAOP_ARRAY_SIZE limit. For IS-NULL proofs, we don't really care about the values of the individual array elements; at most, we care whether there are any, and for some common cases we needn't even know that. The main user-visible effect of this is to let the optimizer recognize applicability of partial indexes with "x IS NOT NULL" predicates to queries with "x IN (array)" clauses in some cases where it previously failed to recognize that. The structure of predtest.c is such that a bunch of related proofs will now also succeed, but they're probably much less useful in the wild. James Coleman, reviewed by David Rowley Discussion: https://postgr.es/m/CAAaqYe8yKSvzbyu8w-dThRs9aTFMwrFxn_BkTYeXgjqe3CbNjg@mail.gmail.com
This commit is contained in:
@ -19,6 +19,9 @@ from generate_series(0, 11*11-1) i;
|
||||
-- and a simple strict function that's opaque to the optimizer
|
||||
create function strictf(bool, bool) returns bool
|
||||
language plpgsql as $$begin return $1 and not $2; end$$ strict;
|
||||
-- a simple function to make arrays opaque to the optimizer
|
||||
create function opaque_array(int[]) returns int[]
|
||||
language plpgsql as $$begin return $1; end$$ strict;
|
||||
-- Basic proof rules for single boolean variables
|
||||
select * from test_predtest($$
|
||||
select x, x
|
||||
@ -837,3 +840,257 @@ w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- In these tests, we want to prevent predtest.c from breaking down the
|
||||
-- ScalarArrayOpExpr into an AND/OR tree, so as to exercise the logic
|
||||
-- that handles ScalarArrayOpExpr directly. We use opaque_array() if
|
||||
-- possible, otherwise an array longer than MAX_SAOP_ARRAY_SIZE.
|
||||
-- ScalarArrayOpExpr implies scalar IS NOT NULL
|
||||
select * from test_predtest($$
|
||||
select x is not null, x = any(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | t
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | t
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- but for ALL, we have to be able to prove the array nonempty
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | t
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | t
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | t
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,y
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | t
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | t
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- check empty-array cases
|
||||
select * from test_predtest($$
|
||||
select x is not null, x = any(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | t
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | t
|
||||
w_i_holds | t
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- same thing under a strict function doesn't prove it
|
||||
select * from test_predtest($$
|
||||
select x is not null, strictf(true, x = any(opaque_array(array[]::int[])))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- ScalarArrayOpExpr refutes scalar IS NULL
|
||||
select * from test_predtest($$
|
||||
select x is null, x = any(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | t
|
||||
weak_refuted_by | t
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
-- but for ALL, we have to be able to prove the array nonempty
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | t
|
||||
weak_refuted_by | t
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
-- check empty-array cases
|
||||
select * from test_predtest($$
|
||||
select x is null, x = any(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | t
|
||||
weak_refuted_by | t
|
||||
s_i_holds | t
|
||||
w_i_holds | t
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- same thing under a strict function doesn't prove it
|
||||
select * from test_predtest($$
|
||||
select x is null, strictf(true, x = any(opaque_array(array[]::int[])))
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | f
|
||||
s_r_holds | f
|
||||
w_r_holds | f
|
||||
|
||||
-- Also, nullness of the scalar weakly refutes a SAOP
|
||||
select * from test_predtest($$
|
||||
select x = any(opaque_array(array[1])), x is null
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | t
|
||||
s_i_holds | f
|
||||
w_i_holds | t
|
||||
s_r_holds | f
|
||||
w_r_holds | t
|
||||
|
||||
-- as does nullness of the array
|
||||
select * from test_predtest($$
|
||||
select x = any(opaque_array(array[y])), array[y] is null
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | t
|
||||
s_i_holds | t
|
||||
w_i_holds | t
|
||||
s_r_holds | t
|
||||
w_r_holds | t
|
||||
|
||||
-- ... unless we need to prove array empty
|
||||
select * from test_predtest($$
|
||||
select x = all(opaque_array(array[1])), x is null
|
||||
from integers
|
||||
$$);
|
||||
-[ RECORD 1 ]-----+--
|
||||
strong_implied_by | f
|
||||
weak_implied_by | f
|
||||
strong_refuted_by | f
|
||||
weak_refuted_by | f
|
||||
s_i_holds | f
|
||||
w_i_holds | t
|
||||
s_r_holds | f
|
||||
w_r_holds | t
|
||||
|
||||
|
@ -25,6 +25,10 @@ from generate_series(0, 11*11-1) i;
|
||||
create function strictf(bool, bool) returns bool
|
||||
language plpgsql as $$begin return $1 and not $2; end$$ strict;
|
||||
|
||||
-- a simple function to make arrays opaque to the optimizer
|
||||
create function opaque_array(int[]) returns int[]
|
||||
language plpgsql as $$begin return $1; end$$ strict;
|
||||
|
||||
-- Basic proof rules for single boolean variables
|
||||
|
||||
select * from test_predtest($$
|
||||
@ -325,3 +329,114 @@ select * from test_predtest($$
|
||||
select x <= y, x = any(array[1,3,y])
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- In these tests, we want to prevent predtest.c from breaking down the
|
||||
-- ScalarArrayOpExpr into an AND/OR tree, so as to exercise the logic
|
||||
-- that handles ScalarArrayOpExpr directly. We use opaque_array() if
|
||||
-- possible, otherwise an array longer than MAX_SAOP_ARRAY_SIZE.
|
||||
|
||||
-- ScalarArrayOpExpr implies scalar IS NOT NULL
|
||||
select * from test_predtest($$
|
||||
select x is not null, x = any(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- but for ALL, we have to be able to prove the array nonempty
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,y
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- check empty-array cases
|
||||
select * from test_predtest($$
|
||||
select x is not null, x = any(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is not null, x <> all(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- same thing under a strict function doesn't prove it
|
||||
select * from test_predtest($$
|
||||
select x is not null, strictf(true, x = any(opaque_array(array[]::int[])))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- ScalarArrayOpExpr refutes scalar IS NULL
|
||||
select * from test_predtest($$
|
||||
select x is null, x = any(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- but for ALL, we have to be able to prove the array nonempty
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(opaque_array(array[1]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(array[
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
|
||||
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101
|
||||
])
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- check empty-array cases
|
||||
select * from test_predtest($$
|
||||
select x is null, x = any(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
select * from test_predtest($$
|
||||
select x is null, x <> all(opaque_array(array[]::int[]))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- same thing under a strict function doesn't prove it
|
||||
select * from test_predtest($$
|
||||
select x is null, strictf(true, x = any(opaque_array(array[]::int[])))
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- Also, nullness of the scalar weakly refutes a SAOP
|
||||
select * from test_predtest($$
|
||||
select x = any(opaque_array(array[1])), x is null
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- as does nullness of the array
|
||||
select * from test_predtest($$
|
||||
select x = any(opaque_array(array[y])), array[y] is null
|
||||
from integers
|
||||
$$);
|
||||
|
||||
-- ... unless we need to prove array empty
|
||||
select * from test_predtest($$
|
||||
select x = all(opaque_array(array[1])), x is null
|
||||
from integers
|
||||
$$);
|
||||
|
Reference in New Issue
Block a user