From 311bcfc6b3acdd6fd152798c7f287ddf74fa2a98 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 16 Apr 2024 21:46:48 +0200 Subject: Adding upstream version 15.4. Signed-off-by: Daniel Baumann --- src/test/modules/test_predtest/.gitignore | 4 + src/test/modules/test_predtest/Makefile | 23 + src/test/modules/test_predtest/README | 28 + .../test_predtest/expected/test_predtest.out | 1096 ++++++++++++++++++++ .../modules/test_predtest/sql/test_predtest.sql | 442 ++++++++ .../modules/test_predtest/test_predtest--1.0.sql | 16 + src/test/modules/test_predtest/test_predtest.c | 218 ++++ .../modules/test_predtest/test_predtest.control | 4 + 8 files changed, 1831 insertions(+) create mode 100644 src/test/modules/test_predtest/.gitignore create mode 100644 src/test/modules/test_predtest/Makefile create mode 100644 src/test/modules/test_predtest/README create mode 100644 src/test/modules/test_predtest/expected/test_predtest.out create mode 100644 src/test/modules/test_predtest/sql/test_predtest.sql create mode 100644 src/test/modules/test_predtest/test_predtest--1.0.sql create mode 100644 src/test/modules/test_predtest/test_predtest.c create mode 100644 src/test/modules/test_predtest/test_predtest.control (limited to 'src/test/modules/test_predtest') diff --git a/src/test/modules/test_predtest/.gitignore b/src/test/modules/test_predtest/.gitignore new file mode 100644 index 0000000..5dcb3ff --- /dev/null +++ b/src/test/modules/test_predtest/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_predtest/Makefile b/src/test/modules/test_predtest/Makefile new file mode 100644 index 0000000..a235e2a --- /dev/null +++ b/src/test/modules/test_predtest/Makefile @@ -0,0 +1,23 @@ +# src/test/modules/test_predtest/Makefile + +MODULE_big = test_predtest +OBJS = \ + $(WIN32RES) \ + test_predtest.o +PGFILEDESC = "test_predtest - test code for optimizer/util/predtest.c" + +EXTENSION = test_predtest +DATA = test_predtest--1.0.sql + +REGRESS = test_predtest + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_predtest +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_predtest/README b/src/test/modules/test_predtest/README new file mode 100644 index 0000000..2c9bec0 --- /dev/null +++ b/src/test/modules/test_predtest/README @@ -0,0 +1,28 @@ +test_predtest is a module for checking the correctness of the optimizer's +predicate-proof logic, in src/backend/optimizer/util/predtest.c. + +The module provides a function that allows direct application of +predtest.c's exposed functions, predicate_implied_by() and +predicate_refuted_by(), to arbitrary boolean expressions, with direct +inspection of the results. This could be done indirectly by checking +planner results, but it can be difficult to construct end-to-end test +cases that prove that the expected results were obtained. + +In general, the use of this function is like + select * from test_predtest('query string') +where the query string must be a SELECT returning two boolean +columns, for example + + select * from test_predtest($$ + select x, not x + from (values (false), (true), (null)) as v(x) + $$); + +The function parses and plans the given query, and then applies the +predtest.c code to the two boolean expressions in the SELECT list, to see +if the first expression can be proven or refuted by the second. It also +executes the query, and checks the resulting rows to see whether any +claimed implication or refutation relationship actually holds. If the +query is designed to exercise the expressions on a full set of possible +input values, as in the example above, then this provides a mechanical +cross-check as to whether the proof code has given a correct answer. diff --git a/src/test/modules/test_predtest/expected/test_predtest.out b/src/test/modules/test_predtest/expected/test_predtest.out new file mode 100644 index 0000000..6d21bcd --- /dev/null +++ b/src/test/modules/test_predtest/expected/test_predtest.out @@ -0,0 +1,1096 @@ +CREATE EXTENSION test_predtest; +-- Make output more legible +\pset expanded on +-- Test data +-- all combinations of four boolean values +create table booleans as +select + case i%3 when 0 then true when 1 then false else null end as x, + case (i/3)%3 when 0 then true when 1 then false else null end as y, + case (i/9)%3 when 0 then true when 1 then false else null end as z, + case (i/27)%3 when 0 then true when 1 then false else null end as w +from generate_series(0, 3*3*3*3-1) i; +-- all combinations of two integers 0..9, plus null +create table integers as +select + case i%11 when 10 then null else i%11 end as x, + case (i/11)%11 when 10 then null else (i/11)%11 end as y +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 +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x, not x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select not x, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select not x, not x +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x is not null, x +from booleans +$$); +-[ 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 is null +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 + +select * from test_predtest($$ +select x is null, x is not null +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 + +select * from test_predtest($$ +select x is not true, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, x is not true +from booleans +$$); +-[ 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 | t + +select * from test_predtest($$ +select x is false, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, x is false +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x is unknown, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, x is unknown +from booleans +$$); +-[ 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 + +-- Assorted not-so-trivial refutation rules +select * from test_predtest($$ +select x is null, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, x is null +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select strictf(x,y), x is null +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select (x is not null) is not true, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select strictf(x,y), (x is not null) is false +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x > y, (y < x) is false +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 + +-- Tests involving AND/OR constructs +select * from test_predtest($$ +select x, x and y +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select not x, x and y +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, not x and y +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x or y, x +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x and y, x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x and y, not x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x and y, y and x +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select not y, y and x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x or y, y or x +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x or y or z, x or z +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x and z, x and y and z +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select z or w, x or y +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select z and w, x or y +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select x, (x and y) or (x and z) +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select (x and y) or z, y and x +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select (not x or not y) and z, y and x +from booleans +$$); +-[ 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 + +select * from test_predtest($$ +select y or x, (x or y) and z +from booleans +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select not x and not y, (x or y) and z +from booleans +$$); +-[ 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 + +-- Tests using btree operator knowledge +select * from test_predtest($$ +select x <= y, x < y +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= y, x > y +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 + +select * from test_predtest($$ +select x <= y, y >= x +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= y, y > x and y < x+2 +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= 5, x <= 7 +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 + +select * from test_predtest($$ +select x <= 5, x > 7 +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 + +select * from test_predtest($$ +select x <= 5, 7 > x +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 + +select * from test_predtest($$ +select 5 >= x, 7 > x +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 + +select * from test_predtest($$ +select 5 >= x, x > 7 +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 + +select * from test_predtest($$ +select 5 = x, x = 7 +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 + +select * from test_predtest($$ +select x is not null, x > 7 +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, int4lt(x,8) +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 null, x > 7 +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 + +select * from test_predtest($$ +select x is null, int4lt(x,8) +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 + +select * from test_predtest($$ +select x is not null, x < 'foo' +from (values + ('aaa'::varchar), ('zzz'::varchar), (null)) as v(x) +$$); +-[ 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 + +-- Cases using ScalarArrayOpExpr +select * from test_predtest($$ +select x <= 5, x in (1,3,5) +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= 5, x in (1,3,5,7) +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 + +select * from test_predtest($$ +select x <= 5, x in (1,3,5,null) +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 in (null,1,3,5,7), x in (1,3,5) +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= 5, x < all(array[1,3,5]) +from integers +$$); +-[ RECORD 1 ]-----+-- +strong_implied_by | t +weak_implied_by | t +strong_refuted_by | f +weak_refuted_by | f +s_i_holds | t +w_i_holds | t +s_r_holds | f +w_r_holds | f + +select * from test_predtest($$ +select x <= y, x = any(array[1,3,y]) +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 + +-- 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 + diff --git a/src/test/modules/test_predtest/sql/test_predtest.sql b/src/test/modules/test_predtest/sql/test_predtest.sql new file mode 100644 index 0000000..072eb5b --- /dev/null +++ b/src/test/modules/test_predtest/sql/test_predtest.sql @@ -0,0 +1,442 @@ +CREATE EXTENSION test_predtest; + +-- Make output more legible +\pset expanded on + +-- Test data + +-- all combinations of four boolean values +create table booleans as +select + case i%3 when 0 then true when 1 then false else null end as x, + case (i/3)%3 when 0 then true when 1 then false else null end as y, + case (i/9)%3 when 0 then true when 1 then false else null end as z, + case (i/27)%3 when 0 then true when 1 then false else null end as w +from generate_series(0, 3*3*3*3-1) i; + +-- all combinations of two integers 0..9, plus null +create table integers as +select + case i%11 when 10 then null else i%11 end as x, + case (i/11)%11 when 10 then null else (i/11)%11 end as y +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 +from booleans +$$); + +select * from test_predtest($$ +select x, not x +from booleans +$$); + +select * from test_predtest($$ +select not x, x +from booleans +$$); + +select * from test_predtest($$ +select not x, not x +from booleans +$$); + +select * from test_predtest($$ +select x is not null, x +from booleans +$$); + +select * from test_predtest($$ +select x is not null, x is null +from integers +$$); + +select * from test_predtest($$ +select x is null, x is not null +from integers +$$); + +select * from test_predtest($$ +select x is not true, x +from booleans +$$); + +select * from test_predtest($$ +select x, x is not true +from booleans +$$); + +select * from test_predtest($$ +select x is false, x +from booleans +$$); + +select * from test_predtest($$ +select x, x is false +from booleans +$$); + +select * from test_predtest($$ +select x is unknown, x +from booleans +$$); + +select * from test_predtest($$ +select x, x is unknown +from booleans +$$); + +-- Assorted not-so-trivial refutation rules + +select * from test_predtest($$ +select x is null, x +from booleans +$$); + +select * from test_predtest($$ +select x, x is null +from booleans +$$); + +select * from test_predtest($$ +select strictf(x,y), x is null +from booleans +$$); + +select * from test_predtest($$ +select (x is not null) is not true, x +from booleans +$$); + +select * from test_predtest($$ +select strictf(x,y), (x is not null) is false +from booleans +$$); + +select * from test_predtest($$ +select x > y, (y < x) is false +from integers +$$); + +-- Tests involving AND/OR constructs + +select * from test_predtest($$ +select x, x and y +from booleans +$$); + +select * from test_predtest($$ +select not x, x and y +from booleans +$$); + +select * from test_predtest($$ +select x, not x and y +from booleans +$$); + +select * from test_predtest($$ +select x or y, x +from booleans +$$); + +select * from test_predtest($$ +select x and y, x +from booleans +$$); + +select * from test_predtest($$ +select x and y, not x +from booleans +$$); + +select * from test_predtest($$ +select x and y, y and x +from booleans +$$); + +select * from test_predtest($$ +select not y, y and x +from booleans +$$); + +select * from test_predtest($$ +select x or y, y or x +from booleans +$$); + +select * from test_predtest($$ +select x or y or z, x or z +from booleans +$$); + +select * from test_predtest($$ +select x and z, x and y and z +from booleans +$$); + +select * from test_predtest($$ +select z or w, x or y +from booleans +$$); + +select * from test_predtest($$ +select z and w, x or y +from booleans +$$); + +select * from test_predtest($$ +select x, (x and y) or (x and z) +from booleans +$$); + +select * from test_predtest($$ +select (x and y) or z, y and x +from booleans +$$); + +select * from test_predtest($$ +select (not x or not y) and z, y and x +from booleans +$$); + +select * from test_predtest($$ +select y or x, (x or y) and z +from booleans +$$); + +select * from test_predtest($$ +select not x and not y, (x or y) and z +from booleans +$$); + +-- Tests using btree operator knowledge + +select * from test_predtest($$ +select x <= y, x < y +from integers +$$); + +select * from test_predtest($$ +select x <= y, x > y +from integers +$$); + +select * from test_predtest($$ +select x <= y, y >= x +from integers +$$); + +select * from test_predtest($$ +select x <= y, y > x and y < x+2 +from integers +$$); + +select * from test_predtest($$ +select x <= 5, x <= 7 +from integers +$$); + +select * from test_predtest($$ +select x <= 5, x > 7 +from integers +$$); + +select * from test_predtest($$ +select x <= 5, 7 > x +from integers +$$); + +select * from test_predtest($$ +select 5 >= x, 7 > x +from integers +$$); + +select * from test_predtest($$ +select 5 >= x, x > 7 +from integers +$$); + +select * from test_predtest($$ +select 5 = x, x = 7 +from integers +$$); + +select * from test_predtest($$ +select x is not null, x > 7 +from integers +$$); + +select * from test_predtest($$ +select x is not null, int4lt(x,8) +from integers +$$); + +select * from test_predtest($$ +select x is null, x > 7 +from integers +$$); + +select * from test_predtest($$ +select x is null, int4lt(x,8) +from integers +$$); + +select * from test_predtest($$ +select x is not null, x < 'foo' +from (values + ('aaa'::varchar), ('zzz'::varchar), (null)) as v(x) +$$); + +-- Cases using ScalarArrayOpExpr + +select * from test_predtest($$ +select x <= 5, x in (1,3,5) +from integers +$$); + +select * from test_predtest($$ +select x <= 5, x in (1,3,5,7) +from integers +$$); + +select * from test_predtest($$ +select x <= 5, x in (1,3,5,null) +from integers +$$); + +select * from test_predtest($$ +select x in (null,1,3,5,7), x in (1,3,5) +from integers +$$); + +select * from test_predtest($$ +select x <= 5, x < all(array[1,3,5]) +from integers +$$); + +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 +$$); diff --git a/src/test/modules/test_predtest/test_predtest--1.0.sql b/src/test/modules/test_predtest/test_predtest--1.0.sql new file mode 100644 index 0000000..11e1444 --- /dev/null +++ b/src/test/modules/test_predtest/test_predtest--1.0.sql @@ -0,0 +1,16 @@ +/* src/test/modules/test_predtest/test_predtest--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_predtest" to load this file. \quit + +CREATE FUNCTION test_predtest(query text, + OUT strong_implied_by bool, + OUT weak_implied_by bool, + OUT strong_refuted_by bool, + OUT weak_refuted_by bool, + OUT s_i_holds bool, + OUT w_i_holds bool, + OUT s_r_holds bool, + OUT w_r_holds bool) +STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/test_predtest/test_predtest.c b/src/test/modules/test_predtest/test_predtest.c new file mode 100644 index 0000000..3b19e0e --- /dev/null +++ b/src/test/modules/test_predtest/test_predtest.c @@ -0,0 +1,218 @@ +/*-------------------------------------------------------------------------- + * + * test_predtest.c + * Test correctness of optimizer's predicate proof logic. + * + * Copyright (c) 2018-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_predtest/test_predtest.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "executor/spi.h" +#include "funcapi.h" +#include "nodes/makefuncs.h" +#include "optimizer/optimizer.h" +#include "utils/builtins.h" + +PG_MODULE_MAGIC; + +/* + * test_predtest(query text) returns record + */ +PG_FUNCTION_INFO_V1(test_predtest); + +Datum +test_predtest(PG_FUNCTION_ARGS) +{ + text *txt = PG_GETARG_TEXT_PP(0); + char *query_string = text_to_cstring(txt); + SPIPlanPtr spiplan; + int spirc; + TupleDesc tupdesc; + bool s_i_holds, + w_i_holds, + s_r_holds, + w_r_holds; + CachedPlan *cplan; + PlannedStmt *stmt; + Plan *plan; + Expr *clause1; + Expr *clause2; + bool strong_implied_by, + weak_implied_by, + strong_refuted_by, + weak_refuted_by; + Datum values[8]; + bool nulls[8]; + int i; + + /* We use SPI to parse, plan, and execute the test query */ + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "SPI_connect failed"); + + /* + * First, plan and execute the query, and inspect the results. To the + * extent that the query fully exercises the two expressions, this + * provides an experimental indication of whether implication or + * refutation holds. + */ + spiplan = SPI_prepare(query_string, 0, NULL); + if (spiplan == NULL) + elog(ERROR, "SPI_prepare failed for \"%s\"", query_string); + + spirc = SPI_execute_plan(spiplan, NULL, NULL, true, 0); + if (spirc != SPI_OK_SELECT) + elog(ERROR, "failed to execute \"%s\"", query_string); + tupdesc = SPI_tuptable->tupdesc; + if (tupdesc->natts != 2 || + TupleDescAttr(tupdesc, 0)->atttypid != BOOLOID || + TupleDescAttr(tupdesc, 1)->atttypid != BOOLOID) + elog(ERROR, "query must yield two boolean columns"); + + s_i_holds = w_i_holds = s_r_holds = w_r_holds = true; + for (i = 0; i < SPI_processed; i++) + { + HeapTuple tup = SPI_tuptable->vals[i]; + Datum dat; + bool isnull; + char c1, + c2; + + /* Extract column values in a 3-way representation */ + dat = SPI_getbinval(tup, tupdesc, 1, &isnull); + if (isnull) + c1 = 'n'; + else if (DatumGetBool(dat)) + c1 = 't'; + else + c1 = 'f'; + + dat = SPI_getbinval(tup, tupdesc, 2, &isnull); + if (isnull) + c2 = 'n'; + else if (DatumGetBool(dat)) + c2 = 't'; + else + c2 = 'f'; + + /* Check for violations of various proof conditions */ + + /* strong implication: truth of c2 implies truth of c1 */ + if (c2 == 't' && c1 != 't') + s_i_holds = false; + /* weak implication: non-falsity of c2 implies non-falsity of c1 */ + if (c2 != 'f' && c1 == 'f') + w_i_holds = false; + /* strong refutation: truth of c2 implies falsity of c1 */ + if (c2 == 't' && c1 != 'f') + s_r_holds = false; + /* weak refutation: truth of c2 implies non-truth of c1 */ + if (c2 == 't' && c1 == 't') + w_r_holds = false; + } + + /* + * Now, dig the clause querytrees out of the plan, and see what predtest.c + * does with them. + */ + cplan = SPI_plan_get_cached_plan(spiplan); + + if (list_length(cplan->stmt_list) != 1) + elog(ERROR, "failed to decipher query plan"); + stmt = linitial_node(PlannedStmt, cplan->stmt_list); + if (stmt->commandType != CMD_SELECT) + elog(ERROR, "failed to decipher query plan"); + plan = stmt->planTree; + Assert(list_length(plan->targetlist) >= 2); + clause1 = linitial_node(TargetEntry, plan->targetlist)->expr; + clause2 = lsecond_node(TargetEntry, plan->targetlist)->expr; + + /* + * Because the clauses are in the SELECT list, preprocess_expression did + * not pass them through canonicalize_qual nor make_ands_implicit. + * + * We can't do canonicalize_qual here, since it's unclear whether the + * expressions ought to be treated as WHERE or CHECK clauses. Fortunately, + * useful test expressions wouldn't be affected by those transformations + * anyway. We should do make_ands_implicit, though. + * + * Another way in which this does not exactly duplicate the normal usage + * of the proof functions is that they are often given qual clauses + * containing RestrictInfo nodes. But since predtest.c just looks through + * those anyway, it seems OK to not worry about that point. + */ + clause1 = (Expr *) make_ands_implicit(clause1); + clause2 = (Expr *) make_ands_implicit(clause2); + + strong_implied_by = predicate_implied_by((List *) clause1, + (List *) clause2, + false); + + weak_implied_by = predicate_implied_by((List *) clause1, + (List *) clause2, + true); + + strong_refuted_by = predicate_refuted_by((List *) clause1, + (List *) clause2, + false); + + weak_refuted_by = predicate_refuted_by((List *) clause1, + (List *) clause2, + true); + + /* + * Issue warning if any proof is demonstrably incorrect. + */ + if (strong_implied_by && !s_i_holds) + elog(WARNING, "strong_implied_by result is incorrect"); + if (weak_implied_by && !w_i_holds) + elog(WARNING, "weak_implied_by result is incorrect"); + if (strong_refuted_by && !s_r_holds) + elog(WARNING, "strong_refuted_by result is incorrect"); + if (weak_refuted_by && !w_r_holds) + elog(WARNING, "weak_refuted_by result is incorrect"); + + /* + * Clean up and return a record of the results. + */ + if (SPI_finish() != SPI_OK_FINISH) + elog(ERROR, "SPI_finish failed"); + + tupdesc = CreateTemplateTupleDesc(8); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "strong_implied_by", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, + "weak_implied_by", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, + "strong_refuted_by", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, + "weak_refuted_by", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, + "s_i_holds", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, + "w_i_holds", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, + "s_r_holds", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, + "w_r_holds", BOOLOID, -1, 0); + tupdesc = BlessTupleDesc(tupdesc); + + MemSet(nulls, 0, sizeof(nulls)); + values[0] = BoolGetDatum(strong_implied_by); + values[1] = BoolGetDatum(weak_implied_by); + values[2] = BoolGetDatum(strong_refuted_by); + values[3] = BoolGetDatum(weak_refuted_by); + values[4] = BoolGetDatum(s_i_holds); + values[5] = BoolGetDatum(w_i_holds); + values[6] = BoolGetDatum(s_r_holds); + values[7] = BoolGetDatum(w_r_holds); + + PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); +} diff --git a/src/test/modules/test_predtest/test_predtest.control b/src/test/modules/test_predtest/test_predtest.control new file mode 100644 index 0000000..a899a9d --- /dev/null +++ b/src/test/modules/test_predtest/test_predtest.control @@ -0,0 +1,4 @@ +comment = 'Test code for optimizer/util/predtest.c' +default_version = '1.0' +module_pathname = '$libdir/test_predtest' +relocatable = true -- cgit v1.2.3