diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/inherit.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/inherit.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/inherit.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/inherit.out 2021-10-30 22:04:10 -0600 @@ -2548,93 +2548,7 @@ ); ALTER TABLE errtst_child_fastdef ADD COLUMN data int NOT NULL DEFAULT 0; ALTER TABLE errtst_child_fastdef ADD CONSTRAINT errtest_child_fastdef_data_check CHECK (data < 10); -ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_fastdef FOR VALUES FROM (0) TO (10); -ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_plaindef FOR VALUES FROM (10) TO (20); -ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_reorder FOR VALUES FROM (20) TO (30); --- insert without child check constraint error -INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '5'); -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '5'); -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '5'); --- insert with child check constraint error -INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '10'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" -DETAIL: Failing row contains (0, 1, 10). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '10'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" -DETAIL: Failing row contains (10, 1, 10). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '10'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" -DETAIL: Failing row contains (20, 1, 10). --- insert with child not null constraint error -INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint -DETAIL: Failing row contains (0, 1, null). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_plaindef" violates not-null constraint -DETAIL: Failing row contains (10, 1, null). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_reorder" violates not-null constraint -DETAIL: Failing row contains (20, 1, null). --- insert with shared check constraint error -INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '5', '5'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "shdata_small" -DETAIL: Failing row contains (0, 5, 5). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '5', '5'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "shdata_small" -DETAIL: Failing row contains (10, 5, 5). -INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '5', '5'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "shdata_small" -DETAIL: Failing row contains (20, 5, 5). --- within partition update without child check constraint violation -BEGIN; -UPDATE errtst_parent SET data = data + 1 WHERE partid = 0; -UPDATE errtst_parent SET data = data + 1 WHERE partid = 10; -UPDATE errtst_parent SET data = data + 1 WHERE partid = 20; -ROLLBACK; --- within partition update with child check constraint violation -UPDATE errtst_parent SET data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" -DETAIL: Failing row contains (0, 1, 15). -UPDATE errtst_parent SET data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" -DETAIL: Failing row contains (10, 1, 15). -UPDATE errtst_parent SET data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" -DETAIL: Failing row contains (20, 1, 15). --- direct leaf partition update, without partition id violation -BEGIN; -UPDATE errtst_child_fastdef SET partid = 1 WHERE partid = 0; -UPDATE errtst_child_plaindef SET partid = 11 WHERE partid = 10; -UPDATE errtst_child_reorder SET partid = 21 WHERE partid = 20; -ROLLBACK; --- direct leaf partition update, with partition id violation -UPDATE errtst_child_fastdef SET partid = partid + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates partition constraint -DETAIL: Failing row contains (10, 1, 5). -UPDATE errtst_child_plaindef SET partid = partid + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates partition constraint -DETAIL: Failing row contains (20, 1, 5). -UPDATE errtst_child_reorder SET partid = partid + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates partition constraint -DETAIL: Failing row contains (5, 1, 30). --- partition move, without child check constraint violation -BEGIN; -UPDATE errtst_parent SET partid = 10, data = data + 1 WHERE partid = 0; -UPDATE errtst_parent SET partid = 20, data = data + 1 WHERE partid = 10; -UPDATE errtst_parent SET partid = 0, data = data + 1 WHERE partid = 20; -ROLLBACK; --- partition move, with child check constraint violation -UPDATE errtst_parent SET partid = 10, data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" -DETAIL: Failing row contains (10, 1, 15). -UPDATE errtst_parent SET partid = 20, data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" -DETAIL: Failing row contains (20, 1, 15). -UPDATE errtst_parent SET partid = 0, data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" -DETAIL: Failing row contains (0, 1, 15). --- partition move, without target partition -UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20; -ERROR: no partition of relation "errtst_parent" found for row -DETAIL: Partition key of the failing row contains (partid) = (30). -DROP TABLE errtst_parent; +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sanity_check.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sanity_check.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sanity_check.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sanity_check.out 2021-10-30 22:04:11 -0600 @@ -1,248 +1,2 @@ -VACUUM; --- --- sanity check, if we don't have indices the test will take years to --- complete. But skip TOAST relations (since they will have varying --- names depending on the current OID counter) as well as temp tables --- of other backends (to avoid timing-dependent behavior). --- --- temporarily disable fancy output, so catalog changes create less diff noise -\a\t -SELECT relname, relhasindex - FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace - WHERE relkind IN ('r', 'p') AND (nspname ~ '^pg_temp_') IS NOT TRUE - ORDER BY relname; -a|f -a_star|f -aggtest|f -array_index_op_test|t -array_op_test|f -b|f -b_star|f -bit_defaults|f -box_tbl|f -bprime|f -bt_f8_heap|t -bt_i4_heap|t -bt_name_heap|t -bt_txt_heap|t -c|f -c_star|f -char_tbl|f -check2_tbl|f -check_tbl|f -circle_tbl|t -city|f -copy_tbl|f -d|f -d_star|f -date_tbl|f -default_tbl|f -defaultexpr_tbl|f -dept|f -dupindexcols|t -e_star|f -emp|f -equipment_r|f -extra_wide_table|f -f_star|f -fast_emp4000|t -float4_tbl|f -float8_tbl|f -func_index_heap|t -hash_f8_heap|t -hash_i4_heap|t -hash_name_heap|t -hash_txt_heap|t -hobbies_r|f -ihighway|t -inet_tbl|f -insert_tbl|f -int2_tbl|f -int4_tbl|f -int8_tbl|f -interval_tbl|f -invalid_check_con|f -invalid_check_con_child|f -iportaltest|f -kd_point_tbl|t -line_tbl|f -log_table|f -lseg_tbl|f -main_table|f -mlparted|f -mlparted1|f -mlparted11|f -mlparted12|f -mlparted2|f -mlparted3|f -mlparted4|f -mlparted_def|f -mlparted_def1|f -mlparted_def2|f -mlparted_defd|f -money_data|f -num_data|f -num_exp_add|t -num_exp_div|t -num_exp_ln|t -num_exp_log10|t -num_exp_mul|t -num_exp_power_10_ln|t -num_exp_sqrt|t -num_exp_sub|t -num_input_test|f -num_result|f -num_typemod_test|f -nummultirange_test|t -numrange_test|t -onek|t -onek2|t -path_tbl|f -person|f -persons|f -persons2|t -persons3|t -pg_aggregate|t -pg_am|t -pg_amop|t -pg_amproc|t -pg_attrdef|t -pg_attribute|t -pg_auth_members|t -pg_authid|t -pg_cast|t -pg_class|t -pg_collation|t -pg_constraint|t -pg_conversion|t -pg_database|t -pg_db_role_setting|t -pg_default_acl|t -pg_depend|t -pg_description|t -pg_enum|t -pg_event_trigger|t -pg_extension|t -pg_foreign_data_wrapper|t -pg_foreign_server|t -pg_foreign_table|t -pg_index|t -pg_inherits|t -pg_init_privs|t -pg_language|t -pg_largeobject|t -pg_largeobject_metadata|t -pg_namespace|t -pg_opclass|t -pg_operator|t -pg_opfamily|t -pg_partitioned_table|t -pg_policy|t -pg_proc|t -pg_publication|t -pg_publication_rel|t -pg_range|t -pg_replication_origin|t -pg_rewrite|t -pg_seclabel|t -pg_sequence|t -pg_shdepend|t -pg_shdescription|t -pg_shseclabel|t -pg_statistic|t -pg_statistic_ext|t -pg_statistic_ext_data|t -pg_subscription|t -pg_subscription_rel|t -pg_tablespace|t -pg_transform|t -pg_trigger|t -pg_ts_config|t -pg_ts_config_map|t -pg_ts_dict|t -pg_ts_parser|t -pg_ts_template|t -pg_type|t -pg_user_mapping|t -point_tbl|t -polygon_tbl|t -quad_box_tbl|t -quad_box_tbl_ord_seq1|f -quad_box_tbl_ord_seq2|f -quad_point_tbl|t -quad_poly_tbl|t -radix_text_tbl|t -ramp|f -real_city|f -reservations|f -road|t -shighway|t -slow_emp4000|f -sql_features|f -sql_implementation_info|f -sql_parts|f -sql_sizing|f -stud_emp|f -student|f -tableam_parted_a_heap2|f -tableam_parted_b_heap2|f -tableam_parted_c_heap2|f -tableam_parted_d_heap2|f -tableam_parted_heap2|f -tableam_tbl_heap2|f -tableam_tblas_heap2|f -tbl_include_box|t -tbl_include_box_pk|f -tbl_include_pk|t -tbl_include_reg|t -tbl_include_unique1|t -tbl_include_unique2|f -tenk1|t -tenk2|t -test_range_excl|t -test_range_gist|t -test_range_spgist|t -test_tsvector|f -testjsonb|f -text_tbl|f -textrange_test|t -time_tbl|f -timestamp_tbl|f -timestamptz_tbl|f -timetz_tbl|f -tmp|f -trigger_parted|t -trigger_parted_p1|t -trigger_parted_p1_1|t -trigger_parted_p2|t -trigger_parted_p2_2|t -varchar_tbl|f -view_base_table|t --- restore normal output mode -\a\t --- --- another sanity check: every system catalog that has OIDs should have --- a unique index on OID. This ensures that the OIDs will be unique, --- even after the OID counter wraps around. --- We exclude non-system tables from the check by looking at nspname. --- -SELECT relname, nspname - FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace JOIN pg_attribute a ON (attrelid = c.oid AND attname = 'oid') - WHERE relkind = 'r' and c.oid < 16384 - AND ((nspname ~ '^pg_') IS NOT FALSE) - AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid - AND indkey[0] = a.attnum AND indnatts = 1 - AND indisunique AND indimmediate); - relname | nspname ----------+--------- -(0 rows) - --- check that relations without storage don't have relfilenode -SELECT relname, relkind - FROM pg_class - WHERE relkind IN ('v', 'c', 'f', 'p', 'I') - AND relfilenode <> 0; - relname | relkind ----------+--------- -(0 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_into.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_into.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_into.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_into.out 2021-10-30 22:04:12 -0600 @@ -1,222 +1,2 @@ --- --- SELECT_INTO --- -SELECT * - INTO TABLE sitmp1 - FROM onek - WHERE onek.unique1 < 2; -DROP TABLE sitmp1; -SELECT * - INTO TABLE sitmp1 - FROM onek2 - WHERE onek2.unique1 < 2; -DROP TABLE sitmp1; --- --- SELECT INTO and INSERT permission, if owner is not allowed to insert. --- -CREATE SCHEMA selinto_schema; -CREATE USER regress_selinto_user; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user - REVOKE INSERT ON TABLES FROM regress_selinto_user; -GRANT ALL ON SCHEMA selinto_schema TO public; -SET SESSION AUTHORIZATION regress_selinto_user; --- WITH DATA, passes. -CREATE TABLE selinto_schema.tbl_withdata1 (a) - AS SELECT generate_series(1,3) WITH DATA; -INSERT INTO selinto_schema.tbl_withdata1 VALUES (4); -ERROR: permission denied for table tbl_withdata1 -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE selinto_schema.tbl_withdata2 (a) AS - SELECT generate_series(1,3) WITH DATA; - QUERY PLAN --------------------------------------- - ProjectSet (actual rows=3 loops=1) - -> Result (actual rows=1 loops=1) -(2 rows) - --- WITH NO DATA, passes. -CREATE TABLE selinto_schema.tbl_nodata1 (a) AS - SELECT generate_series(1,3) WITH NO DATA; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE selinto_schema.tbl_nodata2 (a) AS - SELECT generate_series(1,3) WITH NO DATA; - QUERY PLAN -------------------------------- - ProjectSet (never executed) - -> Result (never executed) -(2 rows) - --- EXECUTE and WITH DATA, passes. -PREPARE data_sel AS SELECT generate_series(1,3); -CREATE TABLE selinto_schema.tbl_withdata3 (a) AS - EXECUTE data_sel WITH DATA; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE selinto_schema.tbl_withdata4 (a) AS - EXECUTE data_sel WITH DATA; - QUERY PLAN --------------------------------------- - ProjectSet (actual rows=3 loops=1) - -> Result (actual rows=1 loops=1) -(2 rows) - --- EXECUTE and WITH NO DATA, passes. -CREATE TABLE selinto_schema.tbl_nodata3 (a) AS - EXECUTE data_sel WITH NO DATA; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE selinto_schema.tbl_nodata4 (a) AS - EXECUTE data_sel WITH NO DATA; - QUERY PLAN -------------------------------- - ProjectSet (never executed) - -> Result (never executed) -(2 rows) - -RESET SESSION AUTHORIZATION; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user - GRANT INSERT ON TABLES TO regress_selinto_user; -SET SESSION AUTHORIZATION regress_selinto_user; -RESET SESSION AUTHORIZATION; -DEALLOCATE data_sel; -DROP SCHEMA selinto_schema CASCADE; -NOTICE: drop cascades to 8 other objects -DETAIL: drop cascades to table selinto_schema.tbl_withdata1 -drop cascades to table selinto_schema.tbl_withdata2 -drop cascades to table selinto_schema.tbl_nodata1 -drop cascades to table selinto_schema.tbl_nodata2 -drop cascades to table selinto_schema.tbl_withdata3 -drop cascades to table selinto_schema.tbl_withdata4 -drop cascades to table selinto_schema.tbl_nodata3 -drop cascades to table selinto_schema.tbl_nodata4 -DROP USER regress_selinto_user; --- Tests for WITH NO DATA and column name consistency -CREATE TABLE ctas_base (i int, j int); -INSERT INTO ctas_base VALUES (1, 2); -CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base; -- Error -ERROR: too many column names were specified -CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base WITH NO DATA; -- Error -ERROR: too many column names were specified -CREATE TABLE ctas_nodata (ii, jj) AS SELECT i, j FROM ctas_base; -- OK -CREATE TABLE ctas_nodata_2 (ii, jj) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK -CREATE TABLE ctas_nodata_3 (ii) AS SELECT i, j FROM ctas_base; -- OK -CREATE TABLE ctas_nodata_4 (ii) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK -SELECT * FROM ctas_nodata; - ii | jj -----+---- - 1 | 2 -(1 row) - -SELECT * FROM ctas_nodata_2; - ii | jj -----+---- -(0 rows) - -SELECT * FROM ctas_nodata_3; - ii | j -----+--- - 1 | 2 -(1 row) - -SELECT * FROM ctas_nodata_4; - ii | j -----+--- -(0 rows) - -DROP TABLE ctas_base; -DROP TABLE ctas_nodata; -DROP TABLE ctas_nodata_2; -DROP TABLE ctas_nodata_3; -DROP TABLE ctas_nodata_4; --- --- CREATE TABLE AS/SELECT INTO as last command in a SQL function --- have been known to cause problems --- -CREATE FUNCTION make_table() RETURNS VOID -AS $$ - CREATE TABLE created_table AS SELECT * FROM int8_tbl; -$$ LANGUAGE SQL; -SELECT make_table(); - make_table ------------- - -(1 row) - -SELECT * FROM created_table; - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - --- Try EXPLAIN ANALYZE SELECT INTO and EXPLAIN ANALYZE CREATE TABLE AS --- WITH NO DATA, but hide the outputs since they won't be stable. -DO $$ -BEGIN - EXECUTE 'EXPLAIN ANALYZE SELECT * INTO TABLE easi FROM int8_tbl'; - EXECUTE 'EXPLAIN ANALYZE CREATE TABLE easi2 AS SELECT * FROM int8_tbl WITH NO DATA'; -END$$; -DROP TABLE created_table; -DROP TABLE easi, easi2; --- --- Disallowed uses of SELECT ... INTO. All should fail --- -DECLARE foo CURSOR FOR SELECT 1 INTO b; -ERROR: SELECT ... INTO is not allowed here -LINE 1: DECLARE foo CURSOR FOR SELECT 1 INTO b; - ^ -COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blob'; -ERROR: COPY (SELECT INTO) is not supported -SELECT * FROM (SELECT 1 INTO f) bar; -ERROR: SELECT ... INTO is not allowed here -LINE 1: SELECT * FROM (SELECT 1 INTO f) bar; - ^ -CREATE VIEW foo AS SELECT 1 INTO b; -ERROR: views must not contain SELECT INTO -INSERT INTO b SELECT 1 INTO f; -ERROR: SELECT ... INTO is not allowed here -LINE 1: INSERT INTO b SELECT 1 INTO f; - ^ --- Test CREATE TABLE AS ... IF NOT EXISTS -CREATE TABLE ctas_ine_tbl AS SELECT 1; -CREATE TABLE ctas_ine_tbl AS SELECT 1 / 0; -- error -ERROR: relation "ctas_ine_tbl" already exists -CREATE TABLE IF NOT EXISTS ctas_ine_tbl AS SELECT 1 / 0; -- ok -NOTICE: relation "ctas_ine_tbl" already exists, skipping -CREATE TABLE ctas_ine_tbl AS SELECT 1 / 0 WITH NO DATA; -- error -ERROR: relation "ctas_ine_tbl" already exists -CREATE TABLE IF NOT EXISTS ctas_ine_tbl AS SELECT 1 / 0 WITH NO DATA; -- ok -NOTICE: relation "ctas_ine_tbl" already exists, skipping -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE ctas_ine_tbl AS SELECT 1 / 0; -- error -ERROR: relation "ctas_ine_tbl" already exists -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE IF NOT EXISTS ctas_ine_tbl AS SELECT 1 / 0; -- ok -NOTICE: relation "ctas_ine_tbl" already exists, skipping - QUERY PLAN ------------- -(0 rows) - -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE ctas_ine_tbl AS SELECT 1 / 0 WITH NO DATA; -- error -ERROR: relation "ctas_ine_tbl" already exists -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE IF NOT EXISTS ctas_ine_tbl AS SELECT 1 / 0 WITH NO DATA; -- ok -NOTICE: relation "ctas_ine_tbl" already exists, skipping - QUERY PLAN ------------- -(0 rows) - -PREPARE ctas_ine_query AS SELECT 1 / 0; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE ctas_ine_tbl AS EXECUTE ctas_ine_query; -- error -ERROR: relation "ctas_ine_tbl" already exists -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE TABLE IF NOT EXISTS ctas_ine_tbl AS EXECUTE ctas_ine_query; -- ok -NOTICE: relation "ctas_ine_tbl" already exists, skipping - QUERY PLAN ------------- -(0 rows) - -DROP TABLE ctas_ine_tbl; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_distinct.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_distinct.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_distinct.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_distinct.out 2021-10-30 22:04:12 -0600 @@ -1,377 +1,2 @@ --- --- SELECT_DISTINCT --- --- --- awk '{print $3;}' onek.data | sort -n | uniq --- -SELECT DISTINCT two FROM tmp ORDER BY 1; - two ------ - 0 - 1 -(2 rows) - --- --- awk '{print $5;}' onek.data | sort -n | uniq --- -SELECT DISTINCT ten FROM tmp ORDER BY 1; - ten ------ - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -(10 rows) - --- --- awk '{print $16;}' onek.data | sort -d | uniq --- -SELECT DISTINCT string4 FROM tmp ORDER BY 1; - string4 ---------- - AAAAxx - HHHHxx - OOOOxx - VVVVxx -(4 rows) - --- --- awk '{print $3,$16,$5;}' onek.data | sort -d | uniq | --- sort +0n -1 +1d -2 +2n -3 --- -SELECT DISTINCT two, string4, ten - FROM tmp - ORDER BY two using <, string4 using <, ten using <; - two | string4 | ten ------+---------+----- - 0 | AAAAxx | 0 - 0 | AAAAxx | 2 - 0 | AAAAxx | 4 - 0 | AAAAxx | 6 - 0 | AAAAxx | 8 - 0 | HHHHxx | 0 - 0 | HHHHxx | 2 - 0 | HHHHxx | 4 - 0 | HHHHxx | 6 - 0 | HHHHxx | 8 - 0 | OOOOxx | 0 - 0 | OOOOxx | 2 - 0 | OOOOxx | 4 - 0 | OOOOxx | 6 - 0 | OOOOxx | 8 - 0 | VVVVxx | 0 - 0 | VVVVxx | 2 - 0 | VVVVxx | 4 - 0 | VVVVxx | 6 - 0 | VVVVxx | 8 - 1 | AAAAxx | 1 - 1 | AAAAxx | 3 - 1 | AAAAxx | 5 - 1 | AAAAxx | 7 - 1 | AAAAxx | 9 - 1 | HHHHxx | 1 - 1 | HHHHxx | 3 - 1 | HHHHxx | 5 - 1 | HHHHxx | 7 - 1 | HHHHxx | 9 - 1 | OOOOxx | 1 - 1 | OOOOxx | 3 - 1 | OOOOxx | 5 - 1 | OOOOxx | 7 - 1 | OOOOxx | 9 - 1 | VVVVxx | 1 - 1 | VVVVxx | 3 - 1 | VVVVxx | 5 - 1 | VVVVxx | 7 - 1 | VVVVxx | 9 -(40 rows) - --- --- awk '{print $2;}' person.data | --- awk '{if(NF!=1){print $2;}else{print;}}' - emp.data | --- awk '{if(NF!=1){print $2;}else{print;}}' - student.data | --- awk 'BEGIN{FS=" ";}{if(NF!=1){print $5;}else{print;}}' - stud_emp.data | --- sort -n -r | uniq --- -SELECT DISTINCT p.age FROM person* p ORDER BY age using >; - age ------ - 98 - 88 - 78 - 68 - 60 - 58 - 50 - 48 - 40 - 38 - 34 - 30 - 28 - 25 - 24 - 23 - 20 - 19 - 18 - 8 -(20 rows) - --- --- Check mentioning same column more than once --- -EXPLAIN (VERBOSE, COSTS OFF) -SELECT count(*) FROM - (SELECT DISTINCT two, four, two FROM tenk1) ss; - QUERY PLAN --------------------------------------------------------- - Aggregate - Output: count(*) - -> HashAggregate - Output: tenk1.two, tenk1.four, tenk1.two - Group Key: tenk1.two, tenk1.four, tenk1.two - -> Seq Scan on public.tenk1 - Output: tenk1.two, tenk1.four, tenk1.two -(7 rows) - -SELECT count(*) FROM - (SELECT DISTINCT two, four, two FROM tenk1) ss; - count -------- - 4 -(1 row) - --- --- Compare results between plans using sorting and plans using hash --- aggregation. Force spilling in both cases by setting work_mem low. --- -SET work_mem='64kB'; --- Produce results with sorting. -SET enable_hashagg=FALSE; -SET jit_above_cost=0; -EXPLAIN (costs off) -SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; - QUERY PLAN ------------------------------------------------- - Unique - -> Sort - Sort Key: ((g % 1000)) - -> Function Scan on generate_series g -(4 rows) - -CREATE TABLE distinct_group_1 AS -SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; -SET jit_above_cost TO DEFAULT; -CREATE TABLE distinct_group_2 AS -SELECT DISTINCT (g%1000)::text FROM generate_series(0,9999) g; -SET enable_hashagg=TRUE; --- Produce results with hash aggregation. -SET enable_sort=FALSE; -SET jit_above_cost=0; -EXPLAIN (costs off) -SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; - QUERY PLAN ------------------------------------------- - HashAggregate - Group Key: (g % 1000) - -> Function Scan on generate_series g -(3 rows) - -CREATE TABLE distinct_hash_1 AS -SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; -SET jit_above_cost TO DEFAULT; -CREATE TABLE distinct_hash_2 AS -SELECT DISTINCT (g%1000)::text FROM generate_series(0,9999) g; -SET enable_sort=TRUE; -SET work_mem TO DEFAULT; --- Compare results -(SELECT * FROM distinct_hash_1 EXCEPT SELECT * FROM distinct_group_1) - UNION ALL -(SELECT * FROM distinct_group_1 EXCEPT SELECT * FROM distinct_hash_1); - ?column? ----------- -(0 rows) - -(SELECT * FROM distinct_hash_1 EXCEPT SELECT * FROM distinct_group_1) - UNION ALL -(SELECT * FROM distinct_group_1 EXCEPT SELECT * FROM distinct_hash_1); - ?column? ----------- -(0 rows) - -DROP TABLE distinct_hash_1; -DROP TABLE distinct_hash_2; -DROP TABLE distinct_group_1; -DROP TABLE distinct_group_2; --- Test parallel DISTINCT -SET parallel_tuple_cost=0; -SET parallel_setup_cost=0; -SET min_parallel_table_scan_size=0; -SET max_parallel_workers_per_gather=2; --- Ensure we get a parallel plan -EXPLAIN (costs off) -SELECT DISTINCT four FROM tenk1; - QUERY PLAN ----------------------------------------------------- - Unique - -> Sort - Sort Key: four - -> Gather - Workers Planned: 2 - -> HashAggregate - Group Key: four - -> Parallel Seq Scan on tenk1 -(8 rows) - --- Ensure the parallel plan produces the correct results -SELECT DISTINCT four FROM tenk1; - four ------- - 0 - 1 - 2 - 3 -(4 rows) - -CREATE OR REPLACE FUNCTION distinct_func(a INT) RETURNS INT AS $$ - BEGIN - RETURN a; - END; -$$ LANGUAGE plpgsql PARALLEL UNSAFE; --- Ensure we don't do parallel distinct with a parallel unsafe function -EXPLAIN (COSTS OFF) -SELECT DISTINCT distinct_func(1) FROM tenk1; - QUERY PLAN ----------------------------------------------------------- - Unique - -> Sort - Sort Key: (distinct_func(1)) - -> Index Only Scan using tenk1_hundred on tenk1 -(4 rows) - --- make the function parallel safe -CREATE OR REPLACE FUNCTION distinct_func(a INT) RETURNS INT AS $$ - BEGIN - RETURN a; - END; -$$ LANGUAGE plpgsql PARALLEL SAFE; --- Ensure we do parallel distinct now that the function is parallel safe -EXPLAIN (COSTS OFF) -SELECT DISTINCT distinct_func(1) FROM tenk1; - QUERY PLAN ----------------------------------------------- - Unique - -> Sort - Sort Key: (distinct_func(1)) - -> Gather - Workers Planned: 2 - -> Parallel Seq Scan on tenk1 -(6 rows) - -RESET max_parallel_workers_per_gather; -RESET min_parallel_table_scan_size; -RESET parallel_setup_cost; -RESET parallel_tuple_cost; --- --- Also, some tests of IS DISTINCT FROM, which doesn't quite deserve its --- very own regression file. --- -CREATE TEMP TABLE disttable (f1 integer); -INSERT INTO DISTTABLE VALUES(1); -INSERT INTO DISTTABLE VALUES(2); -INSERT INTO DISTTABLE VALUES(3); -INSERT INTO DISTTABLE VALUES(NULL); --- basic cases -SELECT f1, f1 IS DISTINCT FROM 2 as "not 2" FROM disttable; - f1 | not 2 -----+------- - 1 | t - 2 | f - 3 | t - | t -(4 rows) - -SELECT f1, f1 IS DISTINCT FROM NULL as "not null" FROM disttable; - f1 | not null -----+---------- - 1 | t - 2 | t - 3 | t - | f -(4 rows) - -SELECT f1, f1 IS DISTINCT FROM f1 as "false" FROM disttable; - f1 | false -----+------- - 1 | f - 2 | f - 3 | f - | f -(4 rows) - -SELECT f1, f1 IS DISTINCT FROM f1+1 as "not null" FROM disttable; - f1 | not null -----+---------- - 1 | t - 2 | t - 3 | t - | f -(4 rows) - --- check that optimizer constant-folds it properly -SELECT 1 IS DISTINCT FROM 2 as "yes"; - yes ------ - t -(1 row) - -SELECT 2 IS DISTINCT FROM 2 as "no"; - no ----- - f -(1 row) - -SELECT 2 IS DISTINCT FROM null as "yes"; - yes ------ - t -(1 row) - -SELECT null IS DISTINCT FROM null as "no"; - no ----- - f -(1 row) - --- negated form -SELECT 1 IS NOT DISTINCT FROM 2 as "no"; - no ----- - f -(1 row) - -SELECT 2 IS NOT DISTINCT FROM 2 as "yes"; - yes ------ - t -(1 row) - -SELECT 2 IS NOT DISTINCT FROM null as "no"; - no ----- - f -(1 row) - -SELECT null IS NOT DISTINCT FROM null as "yes"; - yes ------ - t -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_distinct_on.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_distinct_on.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_distinct_on.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_distinct_on.out 2021-10-30 22:04:12 -0600 @@ -1,75 +1,2 @@ --- --- SELECT_DISTINCT_ON --- -SELECT DISTINCT ON (string4) string4, two, ten - FROM tmp - ORDER BY string4 using <, two using >, ten using <; - string4 | two | ten ----------+-----+----- - AAAAxx | 1 | 1 - HHHHxx | 1 | 1 - OOOOxx | 1 | 1 - VVVVxx | 1 | 1 -(4 rows) - --- this will fail due to conflict of ordering requirements -SELECT DISTINCT ON (string4, ten) string4, two, ten - FROM tmp - ORDER BY string4 using <, two using <, ten using <; -ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions -LINE 1: SELECT DISTINCT ON (string4, ten) string4, two, ten - ^ -SELECT DISTINCT ON (string4, ten) string4, ten, two - FROM tmp - ORDER BY string4 using <, ten using >, two using <; - string4 | ten | two ----------+-----+----- - AAAAxx | 9 | 1 - AAAAxx | 8 | 0 - AAAAxx | 7 | 1 - AAAAxx | 6 | 0 - AAAAxx | 5 | 1 - AAAAxx | 4 | 0 - AAAAxx | 3 | 1 - AAAAxx | 2 | 0 - AAAAxx | 1 | 1 - AAAAxx | 0 | 0 - HHHHxx | 9 | 1 - HHHHxx | 8 | 0 - HHHHxx | 7 | 1 - HHHHxx | 6 | 0 - HHHHxx | 5 | 1 - HHHHxx | 4 | 0 - HHHHxx | 3 | 1 - HHHHxx | 2 | 0 - HHHHxx | 1 | 1 - HHHHxx | 0 | 0 - OOOOxx | 9 | 1 - OOOOxx | 8 | 0 - OOOOxx | 7 | 1 - OOOOxx | 6 | 0 - OOOOxx | 5 | 1 - OOOOxx | 4 | 0 - OOOOxx | 3 | 1 - OOOOxx | 2 | 0 - OOOOxx | 1 | 1 - OOOOxx | 0 | 0 - VVVVxx | 9 | 1 - VVVVxx | 8 | 0 - VVVVxx | 7 | 1 - VVVVxx | 6 | 0 - VVVVxx | 5 | 1 - VVVVxx | 4 | 0 - VVVVxx | 3 | 1 - VVVVxx | 2 | 0 - VVVVxx | 1 | 1 - VVVVxx | 0 | 0 -(40 rows) - --- bug #5049: early 8.4.x chokes on volatile DISTINCT ON clauses -select distinct on (1) floor(random()) as r, f1 from int4_tbl order by 1,2; - r | f1 ----+------------- - 0 | -2147483647 -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_implicit.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_implicit.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_implicit.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_implicit.out 2021-10-30 22:04:12 -0600 @@ -1,338 +1,2 @@ --- --- SELECT_IMPLICIT --- Test cases for queries with ordering terms missing from the target list. --- This used to be called "junkfilter.sql". --- The parser uses the term "resjunk" to handle these cases. --- - thomas 1998-07-09 --- --- load test data -CREATE TABLE test_missing_target (a int, b int, c char(8), d char); -INSERT INTO test_missing_target VALUES (0, 1, 'XXXX', 'A'); -INSERT INTO test_missing_target VALUES (1, 2, 'ABAB', 'b'); -INSERT INTO test_missing_target VALUES (2, 2, 'ABAB', 'c'); -INSERT INTO test_missing_target VALUES (3, 3, 'BBBB', 'D'); -INSERT INTO test_missing_target VALUES (4, 3, 'BBBB', 'e'); -INSERT INTO test_missing_target VALUES (5, 3, 'bbbb', 'F'); -INSERT INTO test_missing_target VALUES (6, 4, 'cccc', 'g'); -INSERT INTO test_missing_target VALUES (7, 4, 'cccc', 'h'); -INSERT INTO test_missing_target VALUES (8, 4, 'CCCC', 'I'); -INSERT INTO test_missing_target VALUES (9, 4, 'CCCC', 'j'); --- w/ existing GROUP BY target -SELECT c, count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; - c | count -----------+------- - ABAB | 2 - BBBB | 2 - CCCC | 2 - XXXX | 1 - bbbb | 1 - cccc | 2 -(6 rows) - --- w/o existing GROUP BY target using a relation name in GROUP BY clause -SELECT count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; - count -------- - 2 - 2 - 2 - 1 - 1 - 2 -(6 rows) - --- w/o existing GROUP BY target and w/o existing a different ORDER BY target --- failure expected -SELECT count(*) FROM test_missing_target GROUP BY a ORDER BY b; -ERROR: column "test_missing_target.b" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: ...ECT count(*) FROM test_missing_target GROUP BY a ORDER BY b; - ^ --- w/o existing GROUP BY target and w/o existing same ORDER BY target -SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b; - count -------- - 1 - 2 - 3 - 4 -(4 rows) - --- w/ existing GROUP BY target using a relation name in target -SELECT test_missing_target.b, count(*) - FROM test_missing_target GROUP BY b ORDER BY b; - b | count ----+------- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 -(4 rows) - --- w/o existing GROUP BY target -SELECT c FROM test_missing_target ORDER BY a; - c ----------- - XXXX - ABAB - ABAB - BBBB - BBBB - bbbb - cccc - cccc - CCCC - CCCC -(10 rows) - --- w/o existing ORDER BY target -SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b desc; - count -------- - 4 - 3 - 2 - 1 -(4 rows) - --- group using reference number -SELECT count(*) FROM test_missing_target ORDER BY 1 desc; - count -------- - 10 -(1 row) - --- order using reference number -SELECT c, count(*) FROM test_missing_target GROUP BY 1 ORDER BY 1; - c | count -----------+------- - ABAB | 2 - BBBB | 2 - CCCC | 2 - XXXX | 1 - bbbb | 1 - cccc | 2 -(6 rows) - --- group using reference number out of range --- failure expected -SELECT c, count(*) FROM test_missing_target GROUP BY 3; -ERROR: GROUP BY position 3 is not in select list -LINE 1: SELECT c, count(*) FROM test_missing_target GROUP BY 3; - ^ --- group w/o existing GROUP BY and ORDER BY target under ambiguous condition --- failure expected -SELECT count(*) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY b ORDER BY b; -ERROR: column reference "b" is ambiguous -LINE 3: GROUP BY b ORDER BY b; - ^ --- order w/ target under ambiguous condition --- failure NOT expected -SELECT a, a FROM test_missing_target - ORDER BY a; - a | a ----+--- - 0 | 0 - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 -(10 rows) - --- order expression w/ target under ambiguous condition --- failure NOT expected -SELECT a/2, a/2 FROM test_missing_target - ORDER BY a/2; - ?column? | ?column? -----------+---------- - 0 | 0 - 0 | 0 - 1 | 1 - 1 | 1 - 2 | 2 - 2 | 2 - 3 | 3 - 3 | 3 - 4 | 4 - 4 | 4 -(10 rows) - --- group expression w/ target under ambiguous condition --- failure NOT expected -SELECT a/2, a/2 FROM test_missing_target - GROUP BY a/2 ORDER BY a/2; - ?column? | ?column? -----------+---------- - 0 | 0 - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 -(5 rows) - --- group w/ existing GROUP BY target under ambiguous condition -SELECT x.b, count(*) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b ORDER BY x.b; - b | count ----+------- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 -(4 rows) - --- group w/o existing GROUP BY target under ambiguous condition -SELECT count(*) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b ORDER BY x.b; - count -------- - 1 - 2 - 3 - 4 -(4 rows) - --- group w/o existing GROUP BY target under ambiguous condition --- into a table -CREATE TABLE test_missing_target2 AS -SELECT count(*) -FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b ORDER BY x.b; -SELECT * FROM test_missing_target2; - count -------- - 1 - 2 - 3 - 4 -(4 rows) - --- Functions and expressions --- w/ existing GROUP BY target -SELECT a%2, count(b) FROM test_missing_target -GROUP BY test_missing_target.a%2 -ORDER BY test_missing_target.a%2; - ?column? | count -----------+------- - 0 | 5 - 1 | 5 -(2 rows) - --- w/o existing GROUP BY target using a relation name in GROUP BY clause -SELECT count(c) FROM test_missing_target -GROUP BY lower(test_missing_target.c) -ORDER BY lower(test_missing_target.c); - count -------- - 2 - 3 - 4 - 1 -(4 rows) - --- w/o existing GROUP BY target and w/o existing a different ORDER BY target --- failure expected -SELECT count(a) FROM test_missing_target GROUP BY a ORDER BY b; -ERROR: column "test_missing_target.b" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: ...ECT count(a) FROM test_missing_target GROUP BY a ORDER BY b; - ^ --- w/o existing GROUP BY target and w/o existing same ORDER BY target -SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2; - count -------- - 1 - 5 - 4 -(3 rows) - --- w/ existing GROUP BY target using a relation name in target -SELECT lower(test_missing_target.c), count(c) - FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c); - lower | count --------+------- - abab | 2 - bbbb | 3 - cccc | 4 - xxxx | 1 -(4 rows) - --- w/o existing GROUP BY target -SELECT a FROM test_missing_target ORDER BY upper(d); - a ---- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -(10 rows) - --- w/o existing ORDER BY target -SELECT count(b) FROM test_missing_target - GROUP BY (b + 1) / 2 ORDER BY (b + 1) / 2 desc; - count -------- - 7 - 3 -(2 rows) - --- group w/o existing GROUP BY and ORDER BY target under ambiguous condition --- failure expected -SELECT count(x.a) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY b/2 ORDER BY b/2; -ERROR: column reference "b" is ambiguous -LINE 3: GROUP BY b/2 ORDER BY b/2; - ^ --- group w/ existing GROUP BY target under ambiguous condition -SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b/2 ORDER BY x.b/2; - ?column? | count -----------+------- - 0 | 1 - 1 | 5 - 2 | 4 -(3 rows) - --- group w/o existing GROUP BY target under ambiguous condition --- failure expected due to ambiguous b in count(b) -SELECT count(b) FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b/2; -ERROR: column reference "b" is ambiguous -LINE 1: SELECT count(b) FROM test_missing_target x, test_missing_tar... - ^ --- group w/o existing GROUP BY target under ambiguous condition --- into a table -CREATE TABLE test_missing_target3 AS -SELECT count(x.b) -FROM test_missing_target x, test_missing_target y - WHERE x.a = y.a - GROUP BY x.b/2 ORDER BY x.b/2; -SELECT * FROM test_missing_target3; - count -------- - 1 - 5 - 4 -(3 rows) - --- Cleanup -DROP TABLE test_missing_target; -DROP TABLE test_missing_target2; -DROP TABLE test_missing_target3; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_having.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_having.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_having.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_having.out 2021-10-30 22:04:12 -0600 @@ -1,93 +1,2 @@ --- --- SELECT_HAVING --- --- load test data -CREATE TABLE test_having (a int, b int, c char(8), d char); -INSERT INTO test_having VALUES (0, 1, 'XXXX', 'A'); -INSERT INTO test_having VALUES (1, 2, 'AAAA', 'b'); -INSERT INTO test_having VALUES (2, 2, 'AAAA', 'c'); -INSERT INTO test_having VALUES (3, 3, 'BBBB', 'D'); -INSERT INTO test_having VALUES (4, 3, 'BBBB', 'e'); -INSERT INTO test_having VALUES (5, 3, 'bbbb', 'F'); -INSERT INTO test_having VALUES (6, 4, 'cccc', 'g'); -INSERT INTO test_having VALUES (7, 4, 'cccc', 'h'); -INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I'); -INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j'); -SELECT b, c FROM test_having - GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c; - b | c ----+---------- - 1 | XXXX - 3 | bbbb -(2 rows) - --- HAVING is effectively equivalent to WHERE in this case -SELECT b, c FROM test_having - GROUP BY b, c HAVING b = 3 ORDER BY b, c; - b | c ----+---------- - 3 | BBBB - 3 | bbbb -(2 rows) - -SELECT lower(c), count(c) FROM test_having - GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) - ORDER BY lower(c); - lower | count --------+------- - bbbb | 3 - cccc | 4 - xxxx | 1 -(3 rows) - -SELECT c, max(a) FROM test_having - GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) - ORDER BY c; - c | max -----------+----- - XXXX | 0 - bbbb | 5 -(2 rows) - --- test degenerate cases involving HAVING without GROUP BY --- Per SQL spec, these should generate 0 or 1 row, even without aggregates -SELECT min(a), max(a) FROM test_having HAVING min(a) = max(a); - min | max ------+----- -(0 rows) - -SELECT min(a), max(a) FROM test_having HAVING min(a) < max(a); - min | max ------+----- - 0 | 9 -(1 row) - --- errors: ungrouped column references -SELECT a FROM test_having HAVING min(a) < max(a); -ERROR: column "test_having.a" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: SELECT a FROM test_having HAVING min(a) < max(a); - ^ -SELECT 1 AS one FROM test_having HAVING a > 1; -ERROR: column "test_having.a" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: SELECT 1 AS one FROM test_having HAVING a > 1; - ^ --- the really degenerate case: need not scan table at all -SELECT 1 AS one FROM test_having HAVING 1 > 2; - one ------ -(0 rows) - -SELECT 1 AS one FROM test_having HAVING 1 < 2; - one ------ - 1 -(1 row) - --- and just to prove that we aren't scanning the table: -SELECT 1 AS one FROM test_having WHERE 1/a = 1 HAVING 1 < 2; - one ------ - 1 -(1 row) - -DROP TABLE test_having; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/subselect.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/subselect.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/subselect.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/subselect.out 2021-10-30 22:04:12 -0600 @@ -1,1835 +1,2 @@ --- --- SUBSELECT --- -SELECT 1 AS one WHERE 1 IN (SELECT 1); - one ------ - 1 -(1 row) - -SELECT 1 AS zero WHERE 1 NOT IN (SELECT 1); - zero ------- -(0 rows) - -SELECT 1 AS zero WHERE 1 IN (SELECT 2); - zero ------- -(0 rows) - --- Check grammar's handling of extra parens in assorted contexts -SELECT * FROM (SELECT 1 AS x) ss; - x ---- - 1 -(1 row) - -SELECT * FROM ((SELECT 1 AS x)) ss; - x ---- - 1 -(1 row) - -(SELECT 2) UNION SELECT 2; - ?column? ----------- - 2 -(1 row) - -((SELECT 2)) UNION SELECT 2; - ?column? ----------- - 2 -(1 row) - -SELECT ((SELECT 2) UNION SELECT 2); - ?column? ----------- - 2 -(1 row) - -SELECT (((SELECT 2)) UNION SELECT 2); - ?column? ----------- - 2 -(1 row) - -SELECT (SELECT ARRAY[1,2,3])[1]; - array -------- - 1 -(1 row) - -SELECT ((SELECT ARRAY[1,2,3]))[2]; - array -------- - 2 -(1 row) - -SELECT (((SELECT ARRAY[1,2,3])))[3]; - array -------- - 3 -(1 row) - --- Set up some simple test tables -CREATE TABLE SUBSELECT_TBL ( - f1 integer, - f2 integer, - f3 float -); -INSERT INTO SUBSELECT_TBL VALUES (1, 2, 3); -INSERT INTO SUBSELECT_TBL VALUES (2, 3, 4); -INSERT INTO SUBSELECT_TBL VALUES (3, 4, 5); -INSERT INTO SUBSELECT_TBL VALUES (1, 1, 1); -INSERT INTO SUBSELECT_TBL VALUES (2, 2, 2); -INSERT INTO SUBSELECT_TBL VALUES (3, 3, 3); -INSERT INTO SUBSELECT_TBL VALUES (6, 7, 8); -INSERT INTO SUBSELECT_TBL VALUES (8, 9, NULL); -SELECT * FROM SUBSELECT_TBL; - f1 | f2 | f3 -----+----+---- - 1 | 2 | 3 - 2 | 3 | 4 - 3 | 4 | 5 - 1 | 1 | 1 - 2 | 2 | 2 - 3 | 3 | 3 - 6 | 7 | 8 - 8 | 9 | -(8 rows) - --- Uncorrelated subselects -SELECT f1 AS "Constant Select" FROM SUBSELECT_TBL - WHERE f1 IN (SELECT 1); - Constant Select ------------------ - 1 - 1 -(2 rows) - -SELECT f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL - WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL); - Uncorrelated Field --------------------- - 1 - 2 - 3 - 1 - 2 - 3 -(6 rows) - -SELECT f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL - WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE - f2 IN (SELECT f1 FROM SUBSELECT_TBL)); - Uncorrelated Field --------------------- - 1 - 2 - 3 - 1 - 2 - 3 -(6 rows) - -SELECT f1, f2 - FROM SUBSELECT_TBL - WHERE (f1, f2) NOT IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL - WHERE f3 IS NOT NULL); - f1 | f2 -----+---- - 1 | 2 - 6 | 7 - 8 | 9 -(3 rows) - --- Correlated subselects -SELECT f1 AS "Correlated Field", f2 AS "Second Field" - FROM SUBSELECT_TBL upper - WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE f1 = upper.f1); - Correlated Field | Second Field -------------------+-------------- - 1 | 2 - 2 | 3 - 3 | 4 - 1 | 1 - 2 | 2 - 3 | 3 -(6 rows) - -SELECT f1 AS "Correlated Field", f3 AS "Second Field" - FROM SUBSELECT_TBL upper - WHERE f1 IN - (SELECT f2 FROM SUBSELECT_TBL WHERE CAST(upper.f2 AS float) = f3); - Correlated Field | Second Field -------------------+-------------- - 2 | 4 - 3 | 5 - 1 | 1 - 2 | 2 - 3 | 3 -(5 rows) - -SELECT f1 AS "Correlated Field", f3 AS "Second Field" - FROM SUBSELECT_TBL upper - WHERE f3 IN (SELECT upper.f1 + f2 FROM SUBSELECT_TBL - WHERE f2 = CAST(f3 AS integer)); - Correlated Field | Second Field -------------------+-------------- - 1 | 3 - 2 | 4 - 3 | 5 - 6 | 8 -(4 rows) - -SELECT f1 AS "Correlated Field" - FROM SUBSELECT_TBL - WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL - WHERE f3 IS NOT NULL); - Correlated Field ------------------- - 2 - 3 - 1 - 2 - 3 -(5 rows) - --- --- Use some existing tables in the regression test --- -SELECT ss.f1 AS "Correlated Field", ss.f3 AS "Second Field" - FROM SUBSELECT_TBL ss - WHERE f1 NOT IN (SELECT f1+1 FROM INT4_TBL - WHERE f1 != ss.f1 AND f1 < 2147483647); - Correlated Field | Second Field -------------------+-------------- - 2 | 4 - 3 | 5 - 2 | 2 - 3 | 3 - 6 | 8 - 8 | -(6 rows) - -select q1, float8(count(*)) / (select count(*) from int8_tbl) -from int8_tbl group by q1 order by q1; - q1 | ?column? -------------------+---------- - 123 | 0.4 - 4567890123456789 | 0.6 -(2 rows) - --- Unspecified-type literals in output columns should resolve as text -SELECT *, pg_typeof(f1) FROM - (SELECT 'foo' AS f1 FROM generate_series(1,3)) ss ORDER BY 1; - f1 | pg_typeof ------+----------- - foo | text - foo | text - foo | text -(3 rows) - --- ... unless there's context to suggest differently -explain (verbose, costs off) select '42' union all select '43'; - QUERY PLAN ----------------------------- - Append - -> Result - Output: '42'::text - -> Result - Output: '43'::text -(5 rows) - -explain (verbose, costs off) select '42' union all select 43; - QUERY PLAN --------------------- - Append - -> Result - Output: 42 - -> Result - Output: 43 -(5 rows) - --- check materialization of an initplan reference (bug #14524) -explain (verbose, costs off) -select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ - Result - Output: (SubPlan 2) - SubPlan 2 - -> Materialize - Output: ($0) - InitPlan 1 (returns $0) - -> Result - Output: 1 - -> Result - Output: $0 -(10 rows) - -select 1 = all (select (select 1)); - ?column? ----------- - t -(1 row) - --- --- Check EXISTS simplification with LIMIT --- -explain (costs off) -select * from int4_tbl o where exists - (select 1 from int4_tbl i where i.f1=o.f1 limit null); - QUERY PLAN ------------------------------------- - Hash Semi Join - Hash Cond: (o.f1 = i.f1) - -> Seq Scan on int4_tbl o - -> Hash - -> Seq Scan on int4_tbl i -(5 rows) - -explain (costs off) -select * from int4_tbl o where not exists - (select 1 from int4_tbl i where i.f1=o.f1 limit 1); - QUERY PLAN ------------------------------------- - Hash Anti Join - Hash Cond: (o.f1 = i.f1) - -> Seq Scan on int4_tbl o - -> Hash - -> Seq Scan on int4_tbl i -(5 rows) - -explain (costs off) -select * from int4_tbl o where exists - (select 1 from int4_tbl i where i.f1=o.f1 limit 0); - QUERY PLAN --------------------------------------- - Seq Scan on int4_tbl o - Filter: (SubPlan 1) - SubPlan 1 - -> Limit - -> Seq Scan on int4_tbl i - Filter: (f1 = o.f1) -(6 rows) - --- --- Test cases to catch unpleasant interactions between IN-join processing --- and subquery pullup. --- -select count(*) from - (select 1 from tenk1 a - where unique1 IN (select hundred from tenk1 b)) ss; - count -------- - 100 -(1 row) - -select count(distinct ss.ten) from - (select ten from tenk1 a - where unique1 IN (select hundred from tenk1 b)) ss; - count -------- - 10 -(1 row) - -select count(*) from - (select 1 from tenk1 a - where unique1 IN (select distinct hundred from tenk1 b)) ss; - count -------- - 100 -(1 row) - -select count(distinct ss.ten) from - (select ten from tenk1 a - where unique1 IN (select distinct hundred from tenk1 b)) ss; - count -------- - 10 -(1 row) - --- --- Test cases to check for overenthusiastic optimization of --- "IN (SELECT DISTINCT ...)" and related cases. Per example from --- Luca Pireddu and Michael Fuhr. --- -CREATE TEMP TABLE foo (id integer); -CREATE TEMP TABLE bar (id1 integer, id2 integer); -INSERT INTO foo VALUES (1); -INSERT INTO bar VALUES (1, 1); -INSERT INTO bar VALUES (2, 2); -INSERT INTO bar VALUES (3, 1); --- These cases require an extra level of distinct-ing above subquery s -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT DISTINCT id1, id2 FROM bar) AS s); - id ----- - 1 -(1 row) - -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT id1,id2 FROM bar GROUP BY id1,id2) AS s); - id ----- - 1 -(1 row) - -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT id1, id2 FROM bar UNION - SELECT id1, id2 FROM bar) AS s); - id ----- - 1 -(1 row) - --- These cases do not -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT DISTINCT ON (id2) id1, id2 FROM bar) AS s); - id ----- - 1 -(1 row) - -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT id2 FROM bar GROUP BY id2) AS s); - id ----- - 1 -(1 row) - -SELECT * FROM foo WHERE id IN - (SELECT id2 FROM (SELECT id2 FROM bar UNION - SELECT id2 FROM bar) AS s); - id ----- - 1 -(1 row) - --- --- Test case to catch problems with multiply nested sub-SELECTs not getting --- recalculated properly. Per bug report from Didier Moens. --- -CREATE TABLE orderstest ( - approver_ref integer, - po_ref integer, - ordercanceled boolean -); -INSERT INTO orderstest VALUES (1, 1, false); -INSERT INTO orderstest VALUES (66, 5, false); -INSERT INTO orderstest VALUES (66, 6, false); -INSERT INTO orderstest VALUES (66, 7, false); -INSERT INTO orderstest VALUES (66, 1, true); -INSERT INTO orderstest VALUES (66, 8, false); -INSERT INTO orderstest VALUES (66, 1, false); -INSERT INTO orderstest VALUES (77, 1, false); -INSERT INTO orderstest VALUES (1, 1, false); -INSERT INTO orderstest VALUES (66, 1, false); -INSERT INTO orderstest VALUES (1, 1, false); -CREATE VIEW orders_view AS -SELECT *, -(SELECT CASE - WHEN ord.approver_ref=1 THEN '---' ELSE 'Approved' - END) AS "Approved", -(SELECT CASE - WHEN ord.ordercanceled - THEN 'Canceled' - ELSE - (SELECT CASE - WHEN ord.po_ref=1 - THEN - (SELECT CASE - WHEN ord.approver_ref=1 - THEN '---' - ELSE 'Approved' - END) - ELSE 'PO' - END) -END) AS "Status", -(CASE - WHEN ord.ordercanceled - THEN 'Canceled' - ELSE - (CASE - WHEN ord.po_ref=1 - THEN - (CASE - WHEN ord.approver_ref=1 - THEN '---' - ELSE 'Approved' - END) - ELSE 'PO' - END) -END) AS "Status_OK" -FROM orderstest ord; -SELECT * FROM orders_view; - approver_ref | po_ref | ordercanceled | Approved | Status | Status_OK ---------------+--------+---------------+----------+----------+----------- - 1 | 1 | f | --- | --- | --- - 66 | 5 | f | Approved | PO | PO - 66 | 6 | f | Approved | PO | PO - 66 | 7 | f | Approved | PO | PO - 66 | 1 | t | Approved | Canceled | Canceled - 66 | 8 | f | Approved | PO | PO - 66 | 1 | f | Approved | Approved | Approved - 77 | 1 | f | Approved | Approved | Approved - 1 | 1 | f | --- | --- | --- - 66 | 1 | f | Approved | Approved | Approved - 1 | 1 | f | --- | --- | --- -(11 rows) - -DROP TABLE orderstest cascade; -NOTICE: drop cascades to view orders_view --- --- Test cases to catch situations where rule rewriter fails to propagate --- hasSubLinks flag correctly. Per example from Kyle Bateman. --- -create temp table parts ( - partnum text, - cost float8 -); -create temp table shipped ( - ttype char(2), - ordnum int4, - partnum text, - value float8 -); -create temp view shipped_view as - select * from shipped where ttype = 'wt'; -create rule shipped_view_insert as on insert to shipped_view do instead - insert into shipped values('wt', new.ordnum, new.partnum, new.value); -insert into parts (partnum, cost) values (1, 1234.56); -insert into shipped_view (ordnum, partnum, value) - values (0, 1, (select cost from parts where partnum = '1')); -select * from shipped_view; - ttype | ordnum | partnum | value --------+--------+---------+--------- - wt | 0 | 1 | 1234.56 -(1 row) - -create rule shipped_view_update as on update to shipped_view do instead - update shipped set partnum = new.partnum, value = new.value - where ttype = new.ttype and ordnum = new.ordnum; -update shipped_view set value = 11 - from int4_tbl a join int4_tbl b - on (a.f1 = (select f1 from int4_tbl c where c.f1=b.f1)) - where ordnum = a.f1; -select * from shipped_view; - ttype | ordnum | partnum | value --------+--------+---------+------- - wt | 0 | 1 | 11 -(1 row) - -select f1, ss1 as relabel from - (select *, (select sum(f1) from int4_tbl b where f1 >= a.f1) as ss1 - from int4_tbl a) ss; - f1 | relabel --------------+------------ - 0 | 2147607103 - 123456 | 2147607103 - -123456 | 2147483647 - 2147483647 | 2147483647 - -2147483647 | 0 -(5 rows) - --- --- Test cases involving PARAM_EXEC parameters and min/max index optimizations. --- Per bug report from David Sanchez i Gregori. --- -select * from ( - select max(unique1) from tenk1 as a - where exists (select 1 from tenk1 as b where b.thousand = a.unique2) -) ss; - max ------- - 9997 -(1 row) - -select * from ( - select min(unique1) from tenk1 as a - where not exists (select 1 from tenk1 as b where b.unique2 = 10000) -) ss; - min ------ - 0 -(1 row) - --- --- Test that an IN implemented using a UniquePath does unique-ification --- with the right semantics, as per bug #4113. (Unfortunately we have --- no simple way to ensure that this test case actually chooses that type --- of plan, but it does in releases 7.4-8.3. Note that an ordering difference --- here might mean that some other plan type is being used, rendering the test --- pointless.) --- -create temp table numeric_table (num_col numeric); -insert into numeric_table values (1), (1.000000000000000000001), (2), (3); -create temp table float_table (float_col float8); -insert into float_table values (1), (2), (3); -select * from float_table - where float_col in (select num_col from numeric_table); - float_col ------------ - 1 - 2 - 3 -(3 rows) - -select * from numeric_table - where num_col in (select float_col from float_table); - num_col -------------------------- - 1 - 1.000000000000000000001 - 2 - 3 -(4 rows) - --- --- Test case for bug #4290: bogus calculation of subplan param sets --- -create temp table ta (id int primary key, val int); -insert into ta values(1,1); -insert into ta values(2,2); -create temp table tb (id int primary key, aval int); -insert into tb values(1,1); -insert into tb values(2,1); -insert into tb values(3,2); -insert into tb values(4,2); -create temp table tc (id int primary key, aid int); -insert into tc values(1,1); -insert into tc values(2,2); -select - ( select min(tb.id) from tb - where tb.aval = (select ta.val from ta where ta.id = tc.aid) ) as min_tb_id -from tc; - min_tb_id ------------ - 1 - 3 -(2 rows) - --- --- Test case for 8.3 "failed to locate grouping columns" bug --- -create temp table t1 (f1 numeric(14,0), f2 varchar(30)); -select * from - (select distinct f1, f2, (select f2 from t1 x where x.f1 = up.f1) as fs - from t1 up) ss -group by f1,f2,fs; - f1 | f2 | fs -----+----+---- -(0 rows) - --- --- Test case for bug #5514 (mishandling of whole-row Vars in subselects) --- -create temp table table_a(id integer); -insert into table_a values (42); -create temp view view_a as select * from table_a; -select view_a from view_a; - view_a --------- - (42) -(1 row) - -select (select view_a) from view_a; - view_a --------- - (42) -(1 row) - -select (select (select view_a)) from view_a; - view_a --------- - (42) -(1 row) - -select (select (a.*)::text) from view_a a; - a ------- - (42) -(1 row) - --- --- Check that whole-row Vars reading the result of a subselect don't include --- any junk columns therein --- -select q from (select max(f1) from int4_tbl group by f1 order by f1) q; - q ---------------- - (-2147483647) - (-123456) - (0) - (123456) - (2147483647) -(5 rows) - -with q as (select max(f1) from int4_tbl group by f1 order by f1) - select q from q; - q ---------------- - (-2147483647) - (-123456) - (0) - (123456) - (2147483647) -(5 rows) - --- --- Test case for sublinks pulled up into joinaliasvars lists in an --- inherited update/delete query --- -begin; -- this shouldn't delete anything, but be safe -delete from road -where exists ( - select 1 - from - int4_tbl cross join - ( select f1, array(select q1 from int8_tbl) as arr - from text_tbl ) ss - where road.name = ss.f1 ); -rollback; --- --- Test case for sublinks pushed down into subselects via join alias expansion --- -select - (select sq1) as qq1 -from - (select exists(select 1 from int4_tbl where f1 = q2) as sq1, 42 as dummy - from int8_tbl) sq0 - join - int4_tbl i4 on dummy = i4.f1; - qq1 ------ -(0 rows) - --- --- Test case for subselect within UPDATE of INSERT...ON CONFLICT DO UPDATE --- -create temp table upsert(key int4 primary key, val text); -insert into upsert values(1, 'val') on conflict (key) do update set val = 'not seen'; -insert into upsert values(1, 'val') on conflict (key) do update set val = 'seen with subselect ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text; -select * from upsert; - key | val ------+---------------------------- - 1 | seen with subselect 123456 -(1 row) - -with aa as (select 'int4_tbl' u from int4_tbl limit 1) -insert into upsert values (1, 'x'), (999, 'y') -on conflict (key) do update set val = (select u from aa) -returning *; - key | val ------+---------- - 1 | int4_tbl - 999 | y -(2 rows) - --- --- Test case for cross-type partial matching in hashed subplan (bug #7597) --- -create temp table outer_7597 (f1 int4, f2 int4); -insert into outer_7597 values (0, 0); -insert into outer_7597 values (1, 0); -insert into outer_7597 values (0, null); -insert into outer_7597 values (1, null); -create temp table inner_7597(c1 int8, c2 int8); -insert into inner_7597 values(0, null); -select * from outer_7597 where (f1, f2) not in (select * from inner_7597); - f1 | f2 -----+---- - 1 | 0 - 1 | -(2 rows) - --- --- Similar test case using text that verifies that collation --- information is passed through by execTuplesEqual() in nodeSubplan.c --- (otherwise it would error in texteq()) --- -create temp table outer_text (f1 text, f2 text); -insert into outer_text values ('a', 'a'); -insert into outer_text values ('b', 'a'); -insert into outer_text values ('a', null); -insert into outer_text values ('b', null); -create temp table inner_text (c1 text, c2 text); -insert into inner_text values ('a', null); -insert into inner_text values ('123', '456'); -select * from outer_text where (f1, f2) not in (select * from inner_text); - f1 | f2 -----+---- - b | a - b | -(2 rows) - --- --- Another test case for cross-type hashed subplans: comparison of --- inner-side values must be done with appropriate operator --- -explain (verbose, costs off) -select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- - Result - Output: (hashed SubPlan 1) - SubPlan 1 - -> Append - -> Result - Output: 'bar'::name - -> Result - Output: 'bar'::name -(8 rows) - -select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - ?column? ----------- - f -(1 row) - --- --- Test case for premature memory release during hashing of subplan output --- -select '1'::text in (select '1'::name union all select '1'::name); - ?column? ----------- - t -(1 row) - --- --- Test that we don't try to use a hashed subplan if the simplified --- testexpr isn't of the right shape --- --- this fails by default, of course -select * from int8_tbl where q1 in (select c1 from inner_text); -ERROR: operator does not exist: bigint = text -LINE 1: select * from int8_tbl where q1 in (select c1 from inner_tex... - ^ -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -begin; --- make an operator to allow it to succeed -create function bogus_int8_text_eq(int8, text) returns boolean -language sql as 'select $1::text = $2'; -create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); -explain (costs off) -select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- - Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 - -> Seq Scan on inner_text -(4 rows) - -select * from int8_tbl where q1 in (select c1 from inner_text); - q1 | q2 ------+------------------ - 123 | 456 - 123 | 4567890123456789 -(2 rows) - --- inlining of this function results in unusual number of hash clauses, --- which we can still cope with -create or replace function bogus_int8_text_eq(int8, text) returns boolean -language sql as 'select $1::text = $2 and $1::text = $2'; -explain (costs off) -select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- - Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 - -> Seq Scan on inner_text -(4 rows) - -select * from int8_tbl where q1 in (select c1 from inner_text); - q1 | q2 ------+------------------ - 123 | 456 - 123 | 4567890123456789 -(2 rows) - --- inlining of this function causes LHS and RHS to be switched, --- which we can't cope with, so hashing should be abandoned -create or replace function bogus_int8_text_eq(int8, text) returns boolean -language sql as 'select $2 = $1::text'; -explain (costs off) -select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- - Seq Scan on int8_tbl - Filter: (SubPlan 1) - SubPlan 1 - -> Materialize - -> Seq Scan on inner_text -(5 rows) - -select * from int8_tbl where q1 in (select c1 from inner_text); - q1 | q2 ------+------------------ - 123 | 456 - 123 | 4567890123456789 -(2 rows) - -rollback; -- to get rid of the bogus operator --- --- Test resolution of hashed vs non-hashed implementation of EXISTS subplan --- -explain (costs off) -select count(*) from tenk1 t -where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- - Aggregate - -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) - SubPlan 2 - -> Index Only Scan using tenk1_unique1 on tenk1 k -(5 rows) - -select count(*) from tenk1 t -where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - count -------- - 10000 -(1 row) - -explain (costs off) -select count(*) from tenk1 t -where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) - and thousand = 1; - QUERY PLAN --------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on tenk1 t - Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = 1) - SubPlan 1 - -> Index Only Scan using tenk1_unique1 on tenk1 k - Index Cond: (unique1 = t.unique2) -(9 rows) - -select count(*) from tenk1 t -where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) - and thousand = 1; - count -------- - 10 -(1 row) - --- It's possible for the same EXISTS to get resolved both ways -create temp table exists_tbl (c1 int, c2 int, c3 int) partition by list (c1); -create temp table exists_tbl_null partition of exists_tbl for values in (null); -create temp table exists_tbl_def partition of exists_tbl default; -insert into exists_tbl select x, x/2, x+1 from generate_series(0,10) x; -analyze exists_tbl; -explain (costs off) -select * from exists_tbl t1 - where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- - Append - -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) - SubPlan 1 - -> Append - -> Seq Scan on exists_tbl_null t2_1 - Filter: (t1_1.c1 = c2) - -> Seq Scan on exists_tbl_def t2_2 - Filter: (t1_1.c1 = c2) - -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) - SubPlan 2 - -> Append - -> Seq Scan on exists_tbl_null t2_4 - -> Seq Scan on exists_tbl_def t2_5 -(15 rows) - -select * from exists_tbl t1 - where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - c1 | c2 | c3 -----+----+---- - 0 | 0 | 1 - 1 | 0 | 2 - 2 | 1 | 3 - 3 | 1 | 4 - 4 | 2 | 5 - 5 | 2 | 6 -(6 rows) - --- --- Test case for planner bug with nested EXISTS handling --- -select a.thousand from tenk1 a, tenk1 b -where a.thousand = b.thousand - and exists ( select 1 from tenk1 c where b.hundred = c.hundred - and not exists ( select 1 from tenk1 d - where a.thousand = d.thousand ) ); - thousand ----------- -(0 rows) - --- --- Check that nested sub-selects are not pulled up if they contain volatiles --- -explain (verbose, costs off) - select x, x from - (select (select now()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ---------------------------- - Values Scan on "*VALUES*" - Output: $0, $1 - InitPlan 1 (returns $0) - -> Result - Output: now() - InitPlan 2 (returns $1) - -> Result - Output: now() -(8 rows) - -explain (verbose, costs off) - select x, x from - (select (select random()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------- - Subquery Scan on ss - Output: ss.x, ss.x - -> Values Scan on "*VALUES*" - Output: $0 - InitPlan 1 (returns $0) - -> Result - Output: random() -(7 rows) - -explain (verbose, costs off) - select x, x from - (select (select now() where y=y) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------------------------------------------- - Values Scan on "*VALUES*" - Output: (SubPlan 1), (SubPlan 2) - SubPlan 1 - -> Result - Output: now() - One-Time Filter: ("*VALUES*".column1 = "*VALUES*".column1) - SubPlan 2 - -> Result - Output: now() - One-Time Filter: ("*VALUES*".column1 = "*VALUES*".column1) -(10 rows) - -explain (verbose, costs off) - select x, x from - (select (select random() where y=y) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------------------------------------------------- - Subquery Scan on ss - Output: ss.x, ss.x - -> Values Scan on "*VALUES*" - Output: (SubPlan 1) - SubPlan 1 - -> Result - Output: random() - One-Time Filter: ("*VALUES*".column1 = "*VALUES*".column1) -(8 rows) - --- --- Test rescan of a hashed subplan (the use of random() is to prevent the --- sub-select from being pulled up, which would result in not hashing) --- -explain (verbose, costs off) -select sum(ss.tst::int) from - onek o cross join lateral ( - select i.ten in (select f1 from int4_tbl where f1 <= o.hundred) as tst, - random() as r - from onek i where i.unique1 = o.unique1 ) ss -where o.ten = 0; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Aggregate - Output: sum((((hashed SubPlan 1)))::integer) - -> Nested Loop - Output: ((hashed SubPlan 1)) - -> Seq Scan on public.onek o - Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous, o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 - Filter: (o.ten = 0) - -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() - Index Cond: (i.unique1 = o.unique1) - SubPlan 1 - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - Filter: (int4_tbl.f1 <= $0) -(14 rows) - -select sum(ss.tst::int) from - onek o cross join lateral ( - select i.ten in (select f1 from int4_tbl where f1 <= o.hundred) as tst, - random() as r - from onek i where i.unique1 = o.unique1 ) ss -where o.ten = 0; - sum ------ - 100 -(1 row) - --- --- Test rescan of a SetOp node --- -explain (costs off) -select count(*) from - onek o cross join lateral ( - select * from onek i1 where i1.unique1 = o.unique1 - except - select * from onek i2 where i2.unique1 = o.unique2 - ) ss -where o.ten = 1; - QUERY PLAN ------------------------------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on onek o - Filter: (ten = 1) - -> Subquery Scan on ss - -> HashSetOp Except - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Index Scan using onek_unique1 on onek i1 - Index Cond: (unique1 = o.unique1) - -> Subquery Scan on "*SELECT* 2" - -> Index Scan using onek_unique1 on onek i2 - Index Cond: (unique1 = o.unique2) -(13 rows) - -select count(*) from - onek o cross join lateral ( - select * from onek i1 where i1.unique1 = o.unique1 - except - select * from onek i2 where i2.unique1 = o.unique2 - ) ss -where o.ten = 1; - count -------- - 100 -(1 row) - --- --- Test rescan of a RecursiveUnion node --- -explain (costs off) -select sum(o.four), sum(ss.a) from - onek o cross join lateral ( - with recursive x(a) as - (select o.four as a - union - select a + 1 from x - where a < 10) - select * from x - ) ss -where o.ten = 1; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on onek o - Filter: (ten = 1) - -> Memoize - Cache Key: o.four - -> CTE Scan on x - CTE x - -> Recursive Union - -> Result - -> WorkTable Scan on x x_1 - Filter: (a < 10) -(12 rows) - -select sum(o.four), sum(ss.a) from - onek o cross join lateral ( - with recursive x(a) as - (select o.four as a - union - select a + 1 from x - where a < 10) - select * from x - ) ss -where o.ten = 1; - sum | sum -------+------ - 1700 | 5350 -(1 row) - --- --- Check we don't misoptimize a NOT IN where the subquery returns no rows. --- -create temp table notinouter (a int); -create temp table notininner (b int not null); -insert into notinouter values (null), (1); -select * from notinouter where a not in (select b from notininner); - a ---- - - 1 -(2 rows) - --- --- Check we behave sanely in corner case of empty SELECT list (bug #8648) --- -create temp table nocolumns(); -select exists(select * from nocolumns); - exists --------- - f -(1 row) - --- --- Check behavior with a SubPlan in VALUES (bug #14924) --- -select val.x - from generate_series(1,10) as s(i), - lateral ( - values ((select s.i + 1)), (s.i + 101) - ) as val(x) -where s.i < 10 and (select val.x) < 110; - x ------ - 2 - 102 - 3 - 103 - 4 - 104 - 5 - 105 - 6 - 106 - 7 - 107 - 8 - 108 - 9 - 109 - 10 -(17 rows) - --- another variant of that (bug #16213) -explain (verbose, costs off) -select * from -(values - (3 not in (select * from (values (1), (2)) ss1)), - (false) -) ss; - QUERY PLAN ----------------------------------------- - Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - SubPlan 1 - -> Values Scan on "*VALUES*_1" - Output: "*VALUES*_1".column1 -(5 rows) - -select * from -(values - (3 not in (select * from (values (1), (2)) ss1)), - (false) -) ss; - column1 ---------- - t - f -(2 rows) - --- --- Check sane behavior with nested IN SubLinks --- -explain (verbose, costs off) -select * from int4_tbl where - (case when f1 in (select unique1 from tenk1 a) then f1 else null end) in - (select ten from tenk1 b); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop Semi Join - Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - -> Seq Scan on public.tenk1 b - Output: b.unique1, b.unique2, b.two, b.four, b.ten, b.twenty, b.hundred, b.thousand, b.twothousand, b.fivethous, b.tenthous, b.odd, b.even, b.stringu1, b.stringu2, b.string4 - SubPlan 1 - -> Index Only Scan using tenk1_unique1 on public.tenk1 a - Output: a.unique1 -(10 rows) - -select * from int4_tbl where - (case when f1 in (select unique1 from tenk1 a) then f1 else null end) in - (select ten from tenk1 b); - f1 ----- - 0 -(1 row) - --- --- Check for incorrect optimization when IN subquery contains a SRF --- -explain (verbose, costs off) -select * from int4_tbl o where (f1, f1) in - (select f1, generate_series(1,50) / 10 g from int4_tbl i group by f1); - QUERY PLAN -------------------------------------------------------------------- - Nested Loop Semi Join - Output: o.f1 - Join Filter: (o.f1 = "ANY_subquery".f1) - -> Seq Scan on public.int4_tbl o - Output: o.f1 - -> Materialize - Output: "ANY_subquery".f1, "ANY_subquery".g - -> Subquery Scan on "ANY_subquery" - Output: "ANY_subquery".f1, "ANY_subquery".g - Filter: ("ANY_subquery".f1 = "ANY_subquery".g) - -> Result - Output: i.f1, ((generate_series(1, 50)) / 10) - -> ProjectSet - Output: generate_series(1, 50), i.f1 - -> HashAggregate - Output: i.f1 - Group Key: i.f1 - -> Seq Scan on public.int4_tbl i - Output: i.f1 -(19 rows) - -select * from int4_tbl o where (f1, f1) in - (select f1, generate_series(1,50) / 10 g from int4_tbl i group by f1); - f1 ----- - 0 -(1 row) - --- --- check for over-optimization of whole-row Var referencing an Append plan --- -select (select q from - (select 1,2,3 where f1 > 0 - union all - select 4,5,6.0 where f1 <= 0 - ) q ) -from int4_tbl; - q ------------ - (4,5,6.0) - (1,2,3) - (4,5,6.0) - (1,2,3) - (4,5,6.0) -(5 rows) - --- --- Check for sane handling of a lateral reference in a subquery's quals --- (most of the complication here is to prevent the test case from being --- flattened too much) --- -explain (verbose, costs off) -select * from - int4_tbl i4, - lateral ( - select i4.f1 > 1 as b, 1 as id - from (select random() order by 1) as t1 - union all - select true as b, 2 as id - ) as t2 -where b and f1 >= 0; - QUERY PLAN --------------------------------------------- - Nested Loop - Output: i4.f1, ((i4.f1 > 1)), (1) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - Filter: (i4.f1 >= 0) - -> Append - -> Subquery Scan on t1 - Output: (i4.f1 > 1), 1 - Filter: (i4.f1 > 1) - -> Sort - Output: (random()) - Sort Key: (random()) - -> Result - Output: random() - -> Result - Output: true, 2 -(16 rows) - -select * from - int4_tbl i4, - lateral ( - select i4.f1 > 1 as b, 1 as id - from (select random() order by 1) as t1 - union all - select true as b, 2 as id - ) as t2 -where b and f1 >= 0; - f1 | b | id -------------+---+---- - 0 | t | 2 - 123456 | t | 1 - 123456 | t | 2 - 2147483647 | t | 1 - 2147483647 | t | 2 -(5 rows) - --- --- Check that volatile quals aren't pushed down past a DISTINCT: --- nextval() should not be called more than the nominal number of times --- -create temp sequence ts1; -select * from - (select distinct ten from tenk1) ss - where ten < 10 + nextval('ts1') - order by 1; - ten ------ - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -(10 rows) - -select nextval('ts1'); - nextval ---------- - 11 -(1 row) - --- --- Check that volatile quals aren't pushed down past a set-returning function; --- while a nonvolatile qual can be, if it doesn't reference the SRF. --- -create function tattle(x int, y int) returns bool -volatile language plpgsql as $$ -begin - raise notice 'x = %, y = %', x, y; - return x > y; -end$$; -explain (verbose, costs off) -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, 8); - QUERY PLAN ----------------------------------------------------------- - Subquery Scan on ss - Output: ss.x, ss.u - Filter: tattle(ss.x, 8) - -> ProjectSet - Output: 9, unnest('{1,2,3,11,12,13}'::integer[]) - -> Result -(6 rows) - -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, 8); -NOTICE: x = 9, y = 8 -NOTICE: x = 9, y = 8 -NOTICE: x = 9, y = 8 -NOTICE: x = 9, y = 8 -NOTICE: x = 9, y = 8 -NOTICE: x = 9, y = 8 - x | u ----+---- - 9 | 1 - 9 | 2 - 9 | 3 - 9 | 11 - 9 | 12 - 9 | 13 -(6 rows) - --- if we pretend it's stable, we get different results: -alter function tattle(x int, y int) stable; -explain (verbose, costs off) -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, 8); - QUERY PLAN ----------------------------------------------------- - ProjectSet - Output: 9, unnest('{1,2,3,11,12,13}'::integer[]) - -> Result - One-Time Filter: tattle(9, 8) -(4 rows) - -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, 8); -NOTICE: x = 9, y = 8 - x | u ----+---- - 9 | 1 - 9 | 2 - 9 | 3 - 9 | 11 - 9 | 12 - 9 | 13 -(6 rows) - --- although even a stable qual should not be pushed down if it references SRF -explain (verbose, costs off) -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, u); - QUERY PLAN ----------------------------------------------------------- - Subquery Scan on ss - Output: ss.x, ss.u - Filter: tattle(ss.x, ss.u) - -> ProjectSet - Output: 9, unnest('{1,2,3,11,12,13}'::integer[]) - -> Result -(6 rows) - -select * from - (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss - where tattle(x, u); -NOTICE: x = 9, y = 1 -NOTICE: x = 9, y = 2 -NOTICE: x = 9, y = 3 -NOTICE: x = 9, y = 11 -NOTICE: x = 9, y = 12 -NOTICE: x = 9, y = 13 - x | u ----+--- - 9 | 1 - 9 | 2 - 9 | 3 -(3 rows) - -drop function tattle(x int, y int); --- --- Test that LIMIT can be pushed to SORT through a subquery that just projects --- columns. We check for that having happened by looking to see if EXPLAIN --- ANALYZE shows that a top-N sort was used. We must suppress or filter away --- all the non-invariant parts of the EXPLAIN ANALYZE output. --- -create table sq_limit (pk int primary key, c1 int, c2 int); -insert into sq_limit values - (1, 1, 1), - (2, 2, 2), - (3, 3, 3), - (4, 4, 4), - (5, 1, 1), - (6, 2, 2), - (7, 3, 3), - (8, 4, 4); -create function explain_sq_limit() returns setof text language plpgsql as -$$ -declare ln text; -begin - for ln in - explain (analyze, summary off, timing off, costs off) - select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3 - loop - ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); - return next ln; - end loop; -end; -$$; -select * from explain_sq_limit(); - explain_sq_limit ----------------------------------------------------------------- - Limit (actual rows=3 loops=1) - -> Subquery Scan on x (actual rows=3 loops=1) - -> Sort (actual rows=3 loops=1) - Sort Key: sq_limit.c1, sq_limit.pk - Sort Method: top-N heapsort Memory: xxx - -> Seq Scan on sq_limit (actual rows=8 loops=1) -(6 rows) - -select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3; - pk | c2 -----+---- - 1 | 1 - 5 | 1 - 2 | 2 -(3 rows) - -drop function explain_sq_limit(); -drop table sq_limit; --- --- Ensure that backward scan direction isn't propagated into --- expression subqueries (bug #15336) --- -begin; -declare c1 scroll cursor for - select * from generate_series(1,4) i - where i <> all (values (2),(3)); -move forward all in c1; -fetch backward all in c1; - i ---- - 4 - 1 -(2 rows) - -commit; --- --- Tests for CTE inlining behavior --- --- Basic subquery that can be inlined -explain (verbose, costs off) -with x as (select * from (select f1 from subselect_tbl) ss) -select * from x where f1 = 1; - QUERY PLAN ----------------------------------- - Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1 - Filter: (subselect_tbl.f1 = 1) -(3 rows) - --- Explicitly request materialization -explain (verbose, costs off) -with x as materialized (select * from (select f1 from subselect_tbl) ss) -select * from x where f1 = 1; - QUERY PLAN ------------------------------------------- - CTE Scan on x - Output: x.f1 - Filter: (x.f1 = 1) - CTE x - -> Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1 -(6 rows) - --- Stable functions are safe to inline -explain (verbose, costs off) -with x as (select * from (select f1, now() from subselect_tbl) ss) -select * from x where f1 = 1; - QUERY PLAN ------------------------------------ - Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, now() - Filter: (subselect_tbl.f1 = 1) -(3 rows) - --- Volatile functions prevent inlining -explain (verbose, costs off) -with x as (select * from (select f1, random() from subselect_tbl) ss) -select * from x where f1 = 1; - QUERY PLAN ----------------------------------------------- - CTE Scan on x - Output: x.f1, x.random - Filter: (x.f1 = 1) - CTE x - -> Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, random() -(6 rows) - --- SELECT FOR UPDATE cannot be inlined -explain (verbose, costs off) -with x as (select * from (select f1 from subselect_tbl for update) ss) -select * from x where f1 = 1; - QUERY PLAN --------------------------------------------------------------------- - CTE Scan on x - Output: x.f1 - Filter: (x.f1 = 1) - CTE x - -> Subquery Scan on ss - Output: ss.f1 - -> LockRows - Output: subselect_tbl.f1, subselect_tbl.ctid - -> Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, subselect_tbl.ctid -(10 rows) - --- Multiply-referenced CTEs are inlined only when requested -explain (verbose, costs off) -with x as (select * from (select f1, now() as n from subselect_tbl) ss) -select * from x, x x2 where x.n = x2.n; - QUERY PLAN -------------------------------------------- - Merge Join - Output: x.f1, x.n, x2.f1, x2.n - Merge Cond: (x.n = x2.n) - CTE x - -> Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, now() - -> Sort - Output: x.f1, x.n - Sort Key: x.n - -> CTE Scan on x - Output: x.f1, x.n - -> Sort - Output: x2.f1, x2.n - Sort Key: x2.n - -> CTE Scan on x x2 - Output: x2.f1, x2.n -(16 rows) - -explain (verbose, costs off) -with x as not materialized (select * from (select f1, now() as n from subselect_tbl) ss) -select * from x, x x2 where x.n = x2.n; - QUERY PLAN ----------------------------------------------------------------------------- - Result - Output: subselect_tbl.f1, now(), subselect_tbl_1.f1, now() - One-Time Filter: (now() = now()) - -> Nested Loop - Output: subselect_tbl.f1, subselect_tbl_1.f1 - -> Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, subselect_tbl.f2, subselect_tbl.f3 - -> Materialize - Output: subselect_tbl_1.f1 - -> Seq Scan on public.subselect_tbl subselect_tbl_1 - Output: subselect_tbl_1.f1 -(11 rows) - --- Multiply-referenced CTEs can't be inlined if they contain outer self-refs -explain (verbose, costs off) -with recursive x(a) as - ((values ('a'), ('b')) - union all - (with z as not materialized (select * from x) - select z.a || z1.a as a from z cross join z as z1 - where length(z.a || z1.a) < 5)) -select * from x; - QUERY PLAN ----------------------------------------------------------- - CTE Scan on x - Output: x.a - CTE x - -> Recursive Union - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Nested Loop - Output: (z.a || z1.a) - Join Filter: (length((z.a || z1.a)) < 5) - CTE z - -> WorkTable Scan on x x_1 - Output: x_1.a - -> CTE Scan on z - Output: z.a - -> CTE Scan on z z1 - Output: z1.a -(16 rows) - -with recursive x(a) as - ((values ('a'), ('b')) - union all - (with z as not materialized (select * from x) - select z.a || z1.a as a from z cross join z as z1 - where length(z.a || z1.a) < 5)) -select * from x; - a ------- - a - b - aa - ab - ba - bb - aaaa - aaab - aaba - aabb - abaa - abab - abba - abbb - baaa - baab - baba - babb - bbaa - bbab - bbba - bbbb -(22 rows) - -explain (verbose, costs off) -with recursive x(a) as - ((values ('a'), ('b')) - union all - (with z as not materialized (select * from x) - select z.a || z.a as a from z - where length(z.a || z.a) < 5)) -select * from x; - QUERY PLAN --------------------------------------------------------- - CTE Scan on x - Output: x.a - CTE x - -> Recursive Union - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> WorkTable Scan on x x_1 - Output: (x_1.a || x_1.a) - Filter: (length((x_1.a || x_1.a)) < 5) -(9 rows) - -with recursive x(a) as - ((values ('a'), ('b')) - union all - (with z as not materialized (select * from x) - select z.a || z.a as a from z - where length(z.a || z.a) < 5)) -select * from x; - a ------- - a - b - aa - bb - aaaa - bbbb -(6 rows) - --- Check handling of outer references -explain (verbose, costs off) -with x as (select * from int4_tbl) -select * from (with y as (select * from x) select * from y) ss; - QUERY PLAN ------------------------------ - Seq Scan on public.int4_tbl - Output: int4_tbl.f1 -(2 rows) - -explain (verbose, costs off) -with x as materialized (select * from int4_tbl) -select * from (with y as (select * from x) select * from y) ss; - QUERY PLAN -------------------------------------- - CTE Scan on x - Output: x.f1 - CTE x - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 -(5 rows) - --- Ensure that we inline the currect CTE when there are --- multiple CTEs with the same name -explain (verbose, costs off) -with x as (select 1 as y) -select * from (with x as (select 2 as y) select * from x) ss; - QUERY PLAN -------------- - Result - Output: 2 -(2 rows) - --- Row marks are not pushed into CTEs -explain (verbose, costs off) -with x as (select * from subselect_tbl) -select * from x for update; - QUERY PLAN ----------------------------------------------------------------- - Seq Scan on public.subselect_tbl - Output: subselect_tbl.f1, subselect_tbl.f2, subselect_tbl.f3 -(2 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/union.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/union.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/union.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/union.out 2021-10-30 22:04:12 -0600 @@ -1,1434 +1,2 @@ --- --- UNION (also INTERSECT, EXCEPT) --- --- Simple UNION constructs -SELECT 1 AS two UNION SELECT 2 ORDER BY 1; - two ------ - 1 - 2 -(2 rows) - -SELECT 1 AS one UNION SELECT 1 ORDER BY 1; - one ------ - 1 -(1 row) - -SELECT 1 AS two UNION ALL SELECT 2; - two ------ - 1 - 2 -(2 rows) - -SELECT 1 AS two UNION ALL SELECT 1; - two ------ - 1 - 1 -(2 rows) - -SELECT 1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; - three -------- - 1 - 2 - 3 -(3 rows) - -SELECT 1 AS two UNION SELECT 2 UNION SELECT 2 ORDER BY 1; - two ------ - 1 - 2 -(2 rows) - -SELECT 1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; - three -------- - 1 - 2 - 2 -(3 rows) - -SELECT 1.1 AS two UNION SELECT 2.2 ORDER BY 1; - two ------ - 1.1 - 2.2 -(2 rows) - --- Mixed types -SELECT 1.1 AS two UNION SELECT 2 ORDER BY 1; - two ------ - 1.1 - 2 -(2 rows) - -SELECT 1 AS two UNION SELECT 2.2 ORDER BY 1; - two ------ - 1 - 2.2 -(2 rows) - -SELECT 1 AS one UNION SELECT 1.0::float8 ORDER BY 1; - one ------ - 1 -(1 row) - -SELECT 1.1 AS two UNION ALL SELECT 2 ORDER BY 1; - two ------ - 1.1 - 2 -(2 rows) - -SELECT 1.0::float8 AS two UNION ALL SELECT 1 ORDER BY 1; - two ------ - 1 - 1 -(2 rows) - -SELECT 1.1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; - three -------- - 1.1 - 2 - 3 -(3 rows) - -SELECT 1.1::float8 AS two UNION SELECT 2 UNION SELECT 2.0::float8 ORDER BY 1; - two ------ - 1.1 - 2 -(2 rows) - -SELECT 1.1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; - three -------- - 1.1 - 2 - 2 -(3 rows) - -SELECT 1.1 AS two UNION (SELECT 2 UNION ALL SELECT 2) ORDER BY 1; - two ------ - 1.1 - 2 -(2 rows) - --- --- Try testing from tables... --- -SELECT f1 AS five FROM FLOAT8_TBL -UNION -SELECT f1 FROM FLOAT8_TBL -ORDER BY 1; - five ------------------------ - -1.2345678901234e+200 - -1004.3 - -34.84 - -1.2345678901234e-200 - 0 -(5 rows) - -SELECT f1 AS ten FROM FLOAT8_TBL -UNION ALL -SELECT f1 FROM FLOAT8_TBL; - ten ------------------------ - 0 - -34.84 - -1004.3 - -1.2345678901234e+200 - -1.2345678901234e-200 - 0 - -34.84 - -1004.3 - -1.2345678901234e+200 - -1.2345678901234e-200 -(10 rows) - -SELECT f1 AS nine FROM FLOAT8_TBL -UNION -SELECT f1 FROM INT4_TBL -ORDER BY 1; - nine ------------------------ - -1.2345678901234e+200 - -2147483647 - -123456 - -1004.3 - -34.84 - -1.2345678901234e-200 - 0 - 123456 - 2147483647 -(9 rows) - -SELECT f1 AS ten FROM FLOAT8_TBL -UNION ALL -SELECT f1 FROM INT4_TBL; - ten ------------------------ - 0 - -34.84 - -1004.3 - -1.2345678901234e+200 - -1.2345678901234e-200 - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(10 rows) - -SELECT f1 AS five FROM FLOAT8_TBL - WHERE f1 BETWEEN -1e6 AND 1e6 -UNION -SELECT f1 FROM INT4_TBL - WHERE f1 BETWEEN 0 AND 1000000 -ORDER BY 1; - five ------------------------ - -1004.3 - -34.84 - -1.2345678901234e-200 - 0 - 123456 -(5 rows) - -SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL -UNION -SELECT f1 FROM CHAR_TBL -ORDER BY 1; - three -------- - a - ab - abcd -(3 rows) - -SELECT f1 AS three FROM VARCHAR_TBL -UNION -SELECT CAST(f1 AS varchar) FROM CHAR_TBL -ORDER BY 1; - three -------- - a - ab - abcd -(3 rows) - -SELECT f1 AS eight FROM VARCHAR_TBL -UNION ALL -SELECT f1 FROM CHAR_TBL; - eight -------- - a - ab - abcd - abcd - a - ab - abcd - abcd -(8 rows) - -SELECT f1 AS five FROM TEXT_TBL -UNION -SELECT f1 FROM VARCHAR_TBL -UNION -SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL -ORDER BY 1; - five -------------------- - a - ab - abcd - doh! - hi de ho neighbor -(5 rows) - --- --- INTERSECT and EXCEPT --- -SELECT q2 FROM int8_tbl INTERSECT SELECT q1 FROM int8_tbl ORDER BY 1; - q2 ------------------- - 123 - 4567890123456789 -(2 rows) - -SELECT q2 FROM int8_tbl INTERSECT ALL SELECT q1 FROM int8_tbl ORDER BY 1; - q2 ------------------- - 123 - 4567890123456789 - 4567890123456789 -(3 rows) - -SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; - q2 -------------------- - -4567890123456789 - 456 -(2 rows) - -SELECT q2 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl ORDER BY 1; - q2 -------------------- - -4567890123456789 - 456 -(2 rows) - -SELECT q2 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q1 FROM int8_tbl ORDER BY 1; - q2 -------------------- - -4567890123456789 - 456 - 4567890123456789 -(3 rows) - -SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY 1; - q1 ----- -(0 rows) - -SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q2 FROM int8_tbl ORDER BY 1; - q1 ------------------- - 123 - 4567890123456789 -(2 rows) - -SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl ORDER BY 1; - q1 ------------------- - 123 - 4567890123456789 - 4567890123456789 -(3 rows) - -SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl FOR NO KEY UPDATE; -ERROR: FOR NO KEY UPDATE is not allowed with UNION/INTERSECT/EXCEPT --- nested cases -(SELECT 1,2,3 UNION SELECT 4,5,6) INTERSECT SELECT 4,5,6; - ?column? | ?column? | ?column? -----------+----------+---------- - 4 | 5 | 6 -(1 row) - -(SELECT 1,2,3 UNION SELECT 4,5,6 ORDER BY 1,2) INTERSECT SELECT 4,5,6; - ?column? | ?column? | ?column? -----------+----------+---------- - 4 | 5 | 6 -(1 row) - -(SELECT 1,2,3 UNION SELECT 4,5,6) EXCEPT SELECT 4,5,6; - ?column? | ?column? | ?column? -----------+----------+---------- - 1 | 2 | 3 -(1 row) - -(SELECT 1,2,3 UNION SELECT 4,5,6 ORDER BY 1,2) EXCEPT SELECT 4,5,6; - ?column? | ?column? | ?column? -----------+----------+---------- - 1 | 2 | 3 -(1 row) - --- exercise both hashed and sorted implementations of UNION/INTERSECT/EXCEPT -set enable_hashagg to on; -explain (costs off) -select count(*) from - ( select unique1 from tenk1 union select fivethous from tenk1 ) ss; - QUERY PLAN ----------------------------------------------------------------- - Aggregate - -> HashAggregate - Group Key: tenk1.unique1 - -> Append - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Seq Scan on tenk1 tenk1_1 -(6 rows) - -select count(*) from - ( select unique1 from tenk1 union select fivethous from tenk1 ) ss; - count -------- - 10000 -(1 row) - -explain (costs off) -select count(*) from - ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Subquery Scan on ss - -> HashSetOp Intersect - -> Append - -> Subquery Scan on "*SELECT* 2" - -> Seq Scan on tenk1 - -> Subquery Scan on "*SELECT* 1" - -> Index Only Scan using tenk1_unique1 on tenk1 tenk1_1 -(8 rows) - -select count(*) from - ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; - count -------- - 5000 -(1 row) - -explain (costs off) -select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; - QUERY PLAN ------------------------------------------------------------------------- - HashSetOp Except - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Subquery Scan on "*SELECT* 2" - -> Index Only Scan using tenk1_unique2 on tenk1 tenk1_1 - Filter: (unique2 <> 10) -(7 rows) - -select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; - unique1 ---------- - 10 -(1 row) - -set enable_hashagg to off; -explain (costs off) -select count(*) from - ( select unique1 from tenk1 union select fivethous from tenk1 ) ss; - QUERY PLAN ----------------------------------------------------------------------- - Aggregate - -> Unique - -> Sort - Sort Key: tenk1.unique1 - -> Append - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Seq Scan on tenk1 tenk1_1 -(7 rows) - -select count(*) from - ( select unique1 from tenk1 union select fivethous from tenk1 ) ss; - count -------- - 10000 -(1 row) - -explain (costs off) -select count(*) from - ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; - QUERY PLAN ------------------------------------------------------------------------------------------- - Aggregate - -> Subquery Scan on ss - -> SetOp Intersect - -> Sort - Sort Key: "*SELECT* 2".fivethous - -> Append - -> Subquery Scan on "*SELECT* 2" - -> Seq Scan on tenk1 - -> Subquery Scan on "*SELECT* 1" - -> Index Only Scan using tenk1_unique1 on tenk1 tenk1_1 -(10 rows) - -select count(*) from - ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; - count -------- - 5000 -(1 row) - -explain (costs off) -select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; - QUERY PLAN ------------------------------------------------------------------------------- - SetOp Except - -> Sort - Sort Key: "*SELECT* 1".unique1 - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Subquery Scan on "*SELECT* 2" - -> Index Only Scan using tenk1_unique2 on tenk1 tenk1_1 - Filter: (unique2 <> 10) -(9 rows) - -select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; - unique1 ---------- - 10 -(1 row) - -reset enable_hashagg; --- non-hashable type -set enable_hashagg to on; -explain (costs off) -select x from (values (100::money), (200::money)) _(x) union select x from (values (100::money), (300::money)) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -set enable_hashagg to off; -explain (costs off) -select x from (values (100::money), (200::money)) _(x) union select x from (values (100::money), (300::money)) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -reset enable_hashagg; --- arrays -set enable_hashagg to on; -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) union select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------ - HashAggregate - Group Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(5 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) union select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,4} - {1,2} - {1,3} -(3 rows) - -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) intersect select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------------ - HashSetOp Intersect - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) intersect select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,2} -(1 row) - -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) except select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------------ - HashSetOp Except - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) except select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,3} -(1 row) - --- non-hashable type -explain (costs off) -select x from (values (array[100::money]), (array[200::money])) _(x) union select x from (values (array[100::money]), (array[300::money])) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (array[100::money]), (array[200::money])) _(x) union select x from (values (array[100::money]), (array[300::money])) _(x); - x ------------ - {$100.00} - {$200.00} - {$300.00} -(3 rows) - -set enable_hashagg to off; -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) union select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) union select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,2} - {1,3} - {1,4} -(3 rows) - -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) intersect select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Intersect - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) intersect select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,2} -(1 row) - -explain (costs off) -select x from (values (array[1, 2]), (array[1, 3])) _(x) except select x from (values (array[1, 2]), (array[1, 4])) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Except - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (array[1, 2]), (array[1, 3])) _(x) except select x from (values (array[1, 2]), (array[1, 4])) _(x); - x -------- - {1,3} -(1 row) - -reset enable_hashagg; --- records -set enable_hashagg to on; -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) union select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) union select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,2) - (1,3) - (1,4) -(3 rows) - -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) intersect select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Intersect - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) intersect select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,2) -(1 row) - -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) except select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Except - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) except select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,3) -(1 row) - --- non-hashable type --- With an anonymous row type, the typcache does not report that the --- type is hashable. (Otherwise, this would fail at execution time.) -explain (costs off) -select x from (values (row(100::money)), (row(200::money))) _(x) union select x from (values (row(100::money)), (row(300::money))) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (row(100::money)), (row(200::money))) _(x) union select x from (values (row(100::money)), (row(300::money))) _(x); - x ------------ - ($100.00) - ($200.00) - ($300.00) -(3 rows) - --- With a defined row type, the typcache can inspect the type's fields --- for hashability. -create type ct1 as (f1 money); -explain (costs off) -select x from (values (row(100::money)::ct1), (row(200::money)::ct1)) _(x) union select x from (values (row(100::money)::ct1), (row(300::money)::ct1)) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (row(100::money)::ct1), (row(200::money)::ct1)) _(x) union select x from (values (row(100::money)::ct1), (row(300::money)::ct1)) _(x); - x ------------ - ($100.00) - ($200.00) - ($300.00) -(3 rows) - -drop type ct1; -set enable_hashagg to off; -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) union select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------ - Unique - -> Sort - Sort Key: "*VALUES*".column1 - -> Append - -> Values Scan on "*VALUES*" - -> Values Scan on "*VALUES*_1" -(6 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) union select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,2) - (1,3) - (1,4) -(3 rows) - -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) intersect select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Intersect - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) intersect select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,2) -(1 row) - -explain (costs off) -select x from (values (row(1, 2)), (row(1, 3))) _(x) except select x from (values (row(1, 2)), (row(1, 4))) _(x); - QUERY PLAN ------------------------------------------------------ - SetOp Except - -> Sort - Sort Key: "*SELECT* 1".x - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Values Scan on "*VALUES*" - -> Subquery Scan on "*SELECT* 2" - -> Values Scan on "*VALUES*_1" -(8 rows) - -select x from (values (row(1, 2)), (row(1, 3))) _(x) except select x from (values (row(1, 2)), (row(1, 4))) _(x); - x -------- - (1,3) -(1 row) - -reset enable_hashagg; --- --- Mixed types --- -SELECT f1 FROM float8_tbl INTERSECT SELECT f1 FROM int4_tbl ORDER BY 1; - f1 ----- - 0 -(1 row) - -SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1; - f1 ------------------------ - -1.2345678901234e+200 - -1004.3 - -34.84 - -1.2345678901234e-200 -(4 rows) - --- --- Operator precedence and (((((extra))))) parentheses --- -SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl ORDER BY 1; - q1 -------------------- - -4567890123456789 - 123 - 123 - 456 - 4567890123456789 - 4567890123456789 - 4567890123456789 -(7 rows) - -SELECT q1 FROM int8_tbl INTERSECT (((SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) ORDER BY 1; - q1 ------------------- - 123 - 4567890123456789 -(2 rows) - -(((SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl ORDER BY 1))) UNION ALL SELECT q2 FROM int8_tbl; - q1 -------------------- - 123 - 4567890123456789 - 456 - 4567890123456789 - 123 - 4567890123456789 - -4567890123456789 -(7 rows) - -SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; - q1 -------------------- - -4567890123456789 - 456 -(2 rows) - -SELECT q1 FROM int8_tbl UNION ALL (((SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1))); - q1 -------------------- - 123 - 123 - 4567890123456789 - 4567890123456789 - 4567890123456789 - -4567890123456789 - 456 -(7 rows) - -(((SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; - q1 -------------------- - -4567890123456789 - 456 -(2 rows) - --- --- Subqueries with ORDER BY & LIMIT clauses --- --- In this syntax, ORDER BY/LIMIT apply to the result of the EXCEPT -SELECT q1,q2 FROM int8_tbl EXCEPT SELECT q2,q1 FROM int8_tbl -ORDER BY q2,q1; - q1 | q2 -------------------+------------------- - 4567890123456789 | -4567890123456789 - 123 | 456 -(2 rows) - --- This should fail, because q2 isn't a name of an EXCEPT output column -SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1; -ERROR: column "q2" does not exist -LINE 1: ... int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1... - ^ -HINT: There is a column named "q2" in table "*SELECT* 2", but it cannot be referenced from this part of the query. --- But this should work: -SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1))) ORDER BY 1; - q1 ------------------- - 123 - 4567890123456789 -(2 rows) - --- --- New syntaxes (7.1) permit new tests --- -(((((select * from int8_tbl))))); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - --- --- Check behavior with empty select list (allowed since 9.4) --- -select union select; --- -(1 row) - -select intersect select; --- -(1 row) - -select except select; --- -(0 rows) - --- check hashed implementation -set enable_hashagg = true; -set enable_sort = false; -explain (costs off) -select from generate_series(1,5) union select from generate_series(1,3); - QUERY PLAN ----------------------------------------------------------------- - HashAggregate - -> Append - -> Function Scan on generate_series - -> Function Scan on generate_series generate_series_1 -(4 rows) - -explain (costs off) -select from generate_series(1,5) intersect select from generate_series(1,3); - QUERY PLAN ----------------------------------------------------------------------- - HashSetOp Intersect - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Function Scan on generate_series - -> Subquery Scan on "*SELECT* 2" - -> Function Scan on generate_series generate_series_1 -(6 rows) - -select from generate_series(1,5) union select from generate_series(1,3); --- -(1 row) - -select from generate_series(1,5) union all select from generate_series(1,3); --- -(8 rows) - -select from generate_series(1,5) intersect select from generate_series(1,3); --- -(1 row) - -select from generate_series(1,5) intersect all select from generate_series(1,3); --- -(3 rows) - -select from generate_series(1,5) except select from generate_series(1,3); --- -(0 rows) - -select from generate_series(1,5) except all select from generate_series(1,3); --- -(2 rows) - --- check sorted implementation -set enable_hashagg = false; -set enable_sort = true; -explain (costs off) -select from generate_series(1,5) union select from generate_series(1,3); - QUERY PLAN ----------------------------------------------------------------- - Unique - -> Append - -> Function Scan on generate_series - -> Function Scan on generate_series generate_series_1 -(4 rows) - -explain (costs off) -select from generate_series(1,5) intersect select from generate_series(1,3); - QUERY PLAN ----------------------------------------------------------------------- - SetOp Intersect - -> Append - -> Subquery Scan on "*SELECT* 1" - -> Function Scan on generate_series - -> Subquery Scan on "*SELECT* 2" - -> Function Scan on generate_series generate_series_1 -(6 rows) - -select from generate_series(1,5) union select from generate_series(1,3); --- -(1 row) - -select from generate_series(1,5) union all select from generate_series(1,3); --- -(8 rows) - -select from generate_series(1,5) intersect select from generate_series(1,3); --- -(1 row) - -select from generate_series(1,5) intersect all select from generate_series(1,3); --- -(3 rows) - -select from generate_series(1,5) except select from generate_series(1,3); --- -(0 rows) - -select from generate_series(1,5) except all select from generate_series(1,3); --- -(2 rows) - -reset enable_hashagg; -reset enable_sort; --- --- Check handling of a case with unknown constants. We don't guarantee --- an undecorated constant will work in all cases, but historically this --- usage has worked, so test we don't break it. --- -SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a -UNION -SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b -ORDER BY 1; - f1 ------- - a - ab - abcd - test -(4 rows) - --- This should fail, but it should produce an error cursor -SELECT '3.4'::numeric UNION SELECT 'foo'; -ERROR: invalid input syntax for type numeric: "foo" -LINE 1: SELECT '3.4'::numeric UNION SELECT 'foo'; - ^ --- --- Test that expression-index constraints can be pushed down through --- UNION or UNION ALL --- -CREATE TEMP TABLE t1 (a text, b text); -CREATE INDEX t1_ab_idx on t1 ((a || b)); -CREATE TEMP TABLE t2 (ab text primary key); -INSERT INTO t1 VALUES ('a', 'b'), ('x', 'y'); -INSERT INTO t2 VALUES ('ab'), ('xy'); -set enable_seqscan = off; -set enable_indexscan = on; -set enable_bitmapscan = off; -explain (costs off) - SELECT * FROM - (SELECT a || b AS ab FROM t1 - UNION ALL - SELECT * FROM t2) t - WHERE ab = 'ab'; - QUERY PLAN ---------------------------------------------- - Append - -> Index Scan using t1_ab_idx on t1 - Index Cond: ((a || b) = 'ab'::text) - -> Index Only Scan using t2_pkey on t2 - Index Cond: (ab = 'ab'::text) -(5 rows) - -explain (costs off) - SELECT * FROM - (SELECT a || b AS ab FROM t1 - UNION - SELECT * FROM t2) t - WHERE ab = 'ab'; - QUERY PLAN ---------------------------------------------------- - HashAggregate - Group Key: ((t1.a || t1.b)) - -> Append - -> Index Scan using t1_ab_idx on t1 - Index Cond: ((a || b) = 'ab'::text) - -> Index Only Scan using t2_pkey on t2 - Index Cond: (ab = 'ab'::text) -(7 rows) - --- --- Test that ORDER BY for UNION ALL can be pushed down to inheritance --- children. --- -CREATE TEMP TABLE t1c (b text, a text); -ALTER TABLE t1c INHERIT t1; -CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2); -INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f'); -INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef'); -CREATE INDEX t1c_ab_idx on t1c ((a || b)); -set enable_seqscan = on; -set enable_indexonlyscan = off; -explain (costs off) - SELECT * FROM - (SELECT a || b AS ab FROM t1 - UNION ALL - SELECT ab FROM t2) t - ORDER BY 1 LIMIT 8; - QUERY PLAN ------------------------------------------------------ - Limit - -> Merge Append - Sort Key: ((t1.a || t1.b)) - -> Index Scan using t1_ab_idx on t1 - -> Index Scan using t1c_ab_idx on t1c t1_1 - -> Index Scan using t2_pkey on t2 - -> Index Scan using t2c_pkey on t2c t2_1 -(7 rows) - - SELECT * FROM - (SELECT a || b AS ab FROM t1 - UNION ALL - SELECT ab FROM t2) t - ORDER BY 1 LIMIT 8; - ab ----- - ab - ab - cd - dc - ef - fe - mn - nm -(8 rows) - -reset enable_seqscan; -reset enable_indexscan; -reset enable_bitmapscan; --- This simpler variant of the above test has been observed to fail differently -create table events (event_id int primary key); -create table other_events (event_id int primary key); -create table events_child () inherits (events); -explain (costs off) -select event_id - from (select event_id from events - union all - select event_id from other_events) ss - order by event_id; - QUERY PLAN ----------------------------------------------------------- - Merge Append - Sort Key: events.event_id - -> Index Scan using events_pkey on events - -> Sort - Sort Key: events_1.event_id - -> Seq Scan on events_child events_1 - -> Index Scan using other_events_pkey on other_events -(7 rows) - -drop table events_child, events, other_events; -reset enable_indexonlyscan; --- Test constraint exclusion of UNION ALL subqueries -explain (costs off) - SELECT * FROM - (SELECT 1 AS t, * FROM tenk1 a - UNION ALL - SELECT 2 AS t, * FROM tenk1 b) c - WHERE t = 2; - QUERY PLAN ---------------------- - Seq Scan on tenk1 b -(1 row) - --- Test that we push quals into UNION sub-selects only when it's safe -explain (costs off) -SELECT * FROM - (SELECT 1 AS t, 2 AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x < 4 -ORDER BY x; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: (2) - -> Unique - -> Sort - Sort Key: (1), (2) - -> Append - -> Result - -> Result - One-Time Filter: false -(9 rows) - -SELECT * FROM - (SELECT 1 AS t, 2 AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x < 4 -ORDER BY x; - t | x ----+--- - 1 | 2 -(1 row) - -explain (costs off) -SELECT * FROM - (SELECT 1 AS t, generate_series(1,10) AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x < 4 -ORDER BY x; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: ss.x - -> Subquery Scan on ss - Filter: (ss.x < 4) - -> HashAggregate - Group Key: (1), (generate_series(1, 10)) - -> Append - -> ProjectSet - -> Result - -> Result -(10 rows) - -SELECT * FROM - (SELECT 1 AS t, generate_series(1,10) AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x < 4 -ORDER BY x; - t | x ----+--- - 1 | 1 - 1 | 2 - 1 | 3 -(3 rows) - -explain (costs off) -SELECT * FROM - (SELECT 1 AS t, (random()*3)::int AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x > 3 -ORDER BY x; - QUERY PLAN ------------------------------------------------------------------------------------- - Sort - Sort Key: ss.x - -> Subquery Scan on ss - Filter: (ss.x > 3) - -> Unique - -> Sort - Sort Key: (1), (((random() * '3'::double precision))::integer) - -> Append - -> Result - -> Result -(10 rows) - -SELECT * FROM - (SELECT 1 AS t, (random()*3)::int AS x - UNION - SELECT 2 AS t, 4 AS x) ss -WHERE x > 3 -ORDER BY x; - t | x ----+--- - 2 | 4 -(1 row) - --- Test cases where the native ordering of a sub-select has more pathkeys --- than the outer query cares about -explain (costs off) -select distinct q1 from - (select distinct * from int8_tbl i81 - union all - select distinct * from int8_tbl i82) ss -where q2 = q2; - QUERY PLAN ----------------------------------------------------------- - Unique - -> Merge Append - Sort Key: "*SELECT* 1".q1 - -> Subquery Scan on "*SELECT* 1" - -> Unique - -> Sort - Sort Key: i81.q1, i81.q2 - -> Seq Scan on int8_tbl i81 - Filter: (q2 IS NOT NULL) - -> Subquery Scan on "*SELECT* 2" - -> Unique - -> Sort - Sort Key: i82.q1, i82.q2 - -> Seq Scan on int8_tbl i82 - Filter: (q2 IS NOT NULL) -(15 rows) - -select distinct q1 from - (select distinct * from int8_tbl i81 - union all - select distinct * from int8_tbl i82) ss -where q2 = q2; - q1 ------------------- - 123 - 4567890123456789 -(2 rows) - -explain (costs off) -select distinct q1 from - (select distinct * from int8_tbl i81 - union all - select distinct * from int8_tbl i82) ss -where -q1 = q2; - QUERY PLAN --------------------------------------------------------- - Unique - -> Merge Append - Sort Key: "*SELECT* 1".q1 - -> Subquery Scan on "*SELECT* 1" - -> Unique - -> Sort - Sort Key: i81.q1, i81.q2 - -> Seq Scan on int8_tbl i81 - Filter: ((- q1) = q2) - -> Subquery Scan on "*SELECT* 2" - -> Unique - -> Sort - Sort Key: i82.q1, i82.q2 - -> Seq Scan on int8_tbl i82 - Filter: ((- q1) = q2) -(15 rows) - -select distinct q1 from - (select distinct * from int8_tbl i81 - union all - select distinct * from int8_tbl i82) ss -where -q1 = q2; - q1 ------------------- - 4567890123456789 -(1 row) - --- Test proper handling of parameterized appendrel paths when the --- potential join qual is expensive -create function expensivefunc(int) returns int -language plpgsql immutable strict cost 10000 -as $$begin return $1; end$$; -create temp table t3 as select generate_series(-1000,1000) as x; -create index t3i on t3 (expensivefunc(x)); -analyze t3; -explain (costs off) -select * from - (select * from t3 a union all select * from t3 b) ss - join int4_tbl on f1 = expensivefunc(x); - QUERY PLAN ------------------------------------------------------------- - Nested Loop - -> Seq Scan on int4_tbl - -> Append - -> Index Scan using t3i on t3 a - Index Cond: (expensivefunc(x) = int4_tbl.f1) - -> Index Scan using t3i on t3 b - Index Cond: (expensivefunc(x) = int4_tbl.f1) -(7 rows) - -select * from - (select * from t3 a union all select * from t3 b) ss - join int4_tbl on f1 = expensivefunc(x); - x | f1 ----+---- - 0 | 0 - 0 | 0 -(2 rows) - -drop table t3; -drop function expensivefunc(int); --- Test handling of appendrel quals that const-simplify into an AND -explain (costs off) -select * from - (select *, 0 as x from int8_tbl a - union all - select *, 1 as x from int8_tbl b) ss -where (x = 0) or (q1 >= q2 and q1 <= q2); - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on int8_tbl a - -> Seq Scan on int8_tbl b - Filter: ((q1 >= q2) AND (q1 <= q2)) -(4 rows) - -select * from - (select *, 0 as x from int8_tbl a - union all - select *, 1 as x from int8_tbl b) ss -where (x = 0) or (q1 >= q2 and q1 <= q2); - q1 | q2 | x -------------------+-------------------+--- - 123 | 456 | 0 - 123 | 4567890123456789 | 0 - 4567890123456789 | 123 | 0 - 4567890123456789 | 4567890123456789 | 0 - 4567890123456789 | -4567890123456789 | 0 - 4567890123456789 | 4567890123456789 | 1 -(6 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/case.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/case.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/case.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/case.out 2021-10-30 22:04:12 -0600 @@ -1,419 +1,2 @@ --- --- CASE --- Test the case statement --- -CREATE TABLE CASE_TBL ( - i integer, - f double precision -); -CREATE TABLE CASE2_TBL ( - i integer, - j integer -); -INSERT INTO CASE_TBL VALUES (1, 10.1); -INSERT INTO CASE_TBL VALUES (2, 20.2); -INSERT INTO CASE_TBL VALUES (3, -30.3); -INSERT INTO CASE_TBL VALUES (4, NULL); -INSERT INTO CASE2_TBL VALUES (1, -1); -INSERT INTO CASE2_TBL VALUES (2, -2); -INSERT INTO CASE2_TBL VALUES (3, -3); -INSERT INTO CASE2_TBL VALUES (2, -4); -INSERT INTO CASE2_TBL VALUES (1, NULL); -INSERT INTO CASE2_TBL VALUES (NULL, -6); --- --- Simplest examples without tables --- -SELECT '3' AS "One", - CASE - WHEN 1 < 2 THEN 3 - END AS "Simple WHEN"; - One | Simple WHEN ------+------------- - 3 | 3 -(1 row) - -SELECT '' AS "One", - CASE - WHEN 1 > 2 THEN 3 - END AS "Simple default"; - One | Simple default ---------+---------------- - | -(1 row) - -SELECT '3' AS "One", - CASE - WHEN 1 < 2 THEN 3 - ELSE 4 - END AS "Simple ELSE"; - One | Simple ELSE ------+------------- - 3 | 3 -(1 row) - -SELECT '4' AS "One", - CASE - WHEN 1 > 2 THEN 3 - ELSE 4 - END AS "ELSE default"; - One | ELSE default ------+-------------- - 4 | 4 -(1 row) - -SELECT '6' AS "One", - CASE - WHEN 1 > 2 THEN 3 - WHEN 4 < 5 THEN 6 - ELSE 7 - END AS "Two WHEN with default"; - One | Two WHEN with default ------+----------------------- - 6 | 6 -(1 row) - -SELECT '7' AS "None", - CASE WHEN random() < 0 THEN 1 - END AS "NULL on no matches"; - None | NULL on no matches -------+-------------------- - 7 | -(1 row) - --- Constant-expression folding shouldn't evaluate unreachable subexpressions -SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END; - case ------- - 1 -(1 row) - -SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END; - case ------- - 1 -(1 row) - --- However we do not currently suppress folding of potentially --- reachable subexpressions -SELECT CASE WHEN i > 100 THEN 1/0 ELSE 0 END FROM case_tbl; -ERROR: division by zero --- Test for cases involving untyped literals in test expression -SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END; - case ------- - 1 -(1 row) - --- --- Examples of targets involving tables --- -SELECT - CASE - WHEN i >= 3 THEN i - END AS ">= 3 or Null" - FROM CASE_TBL; - >= 3 or Null --------------- - - - 3 - 4 -(4 rows) - -SELECT - CASE WHEN i >= 3 THEN (i + i) - ELSE i - END AS "Simplest Math" - FROM CASE_TBL; - Simplest Math ---------------- - 1 - 2 - 6 - 8 -(4 rows) - -SELECT i AS "Value", - CASE WHEN (i < 0) THEN 'small' - WHEN (i = 0) THEN 'zero' - WHEN (i = 1) THEN 'one' - WHEN (i = 2) THEN 'two' - ELSE 'big' - END AS "Category" - FROM CASE_TBL; - Value | Category --------+---------- - 1 | one - 2 | two - 3 | big - 4 | big -(4 rows) - -SELECT - CASE WHEN ((i < 0) or (i < 0)) THEN 'small' - WHEN ((i = 0) or (i = 0)) THEN 'zero' - WHEN ((i = 1) or (i = 1)) THEN 'one' - WHEN ((i = 2) or (i = 2)) THEN 'two' - ELSE 'big' - END AS "Category" - FROM CASE_TBL; - Category ----------- - one - two - big - big -(4 rows) - --- --- Examples of qualifications involving tables --- --- --- NULLIF() and COALESCE() --- Shorthand forms for typical CASE constructs --- defined in the SQL standard. --- -SELECT * FROM CASE_TBL WHERE COALESCE(f,i) = 4; - i | f ----+--- - 4 | -(1 row) - -SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2; - i | f ----+--- -(0 rows) - -SELECT COALESCE(a.f, b.i, b.j) - FROM CASE_TBL a, CASE2_TBL b; - coalesce ----------- - 10.1 - 20.2 - -30.3 - 1 - 10.1 - 20.2 - -30.3 - 2 - 10.1 - 20.2 - -30.3 - 3 - 10.1 - 20.2 - -30.3 - 2 - 10.1 - 20.2 - -30.3 - 1 - 10.1 - 20.2 - -30.3 - -6 -(24 rows) - -SELECT * - FROM CASE_TBL a, CASE2_TBL b - WHERE COALESCE(a.f, b.i, b.j) = 2; - i | f | i | j ----+---+---+---- - 4 | | 2 | -2 - 4 | | 2 | -4 -(2 rows) - -SELECT NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)", - NULLIF(b.i, 4) AS "NULLIF(b.i,4)" - FROM CASE_TBL a, CASE2_TBL b; - NULLIF(a.i,b.i) | NULLIF(b.i,4) ------------------+--------------- - | 1 - 2 | 1 - 3 | 1 - 4 | 1 - 1 | 2 - | 2 - 3 | 2 - 4 | 2 - 1 | 3 - 2 | 3 - | 3 - 4 | 3 - 1 | 2 - | 2 - 3 | 2 - 4 | 2 - | 1 - 2 | 1 - 3 | 1 - 4 | 1 - 1 | - 2 | - 3 | - 4 | -(24 rows) - -SELECT * - FROM CASE_TBL a, CASE2_TBL b - WHERE COALESCE(f,b.i) = 2; - i | f | i | j ----+---+---+---- - 4 | | 2 | -2 - 4 | | 2 | -4 -(2 rows) - --- Tests for constant subexpression simplification -explain (costs off) -SELECT * FROM CASE_TBL WHERE NULLIF(1, 2) = 2; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) -SELECT * FROM CASE_TBL WHERE NULLIF(1, 1) IS NOT NULL; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) -SELECT * FROM CASE_TBL WHERE NULLIF(1, null) = 2; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - --- --- Examples of updates involving tables --- -UPDATE CASE_TBL - SET i = CASE WHEN i >= 3 THEN (- i) - ELSE (2 * i) END; -SELECT * FROM CASE_TBL; - i | f -----+------- - 2 | 10.1 - 4 | 20.2 - -3 | -30.3 - -4 | -(4 rows) - -UPDATE CASE_TBL - SET i = CASE WHEN i >= 2 THEN (2 * i) - ELSE (3 * i) END; -SELECT * FROM CASE_TBL; - i | f ------+------- - 4 | 10.1 - 8 | 20.2 - -9 | -30.3 - -12 | -(4 rows) - -UPDATE CASE_TBL - SET i = CASE WHEN b.i >= 2 THEN (2 * j) - ELSE (3 * j) END - FROM CASE2_TBL b - WHERE j = -CASE_TBL.i; -SELECT * FROM CASE_TBL; - i | f ------+------- - 8 | 20.2 - -9 | -30.3 - -12 | - -8 | 10.1 -(4 rows) - --- --- Nested CASE expressions --- --- This test exercises a bug caused by aliasing econtext->caseValue_isNull --- with the isNull argument of the inner CASE's CaseExpr evaluation. After --- evaluating the vol(null) expression in the inner CASE's second WHEN-clause, --- the isNull flag for the case test value incorrectly became true, causing --- the third WHEN-clause not to match. The volatile function calls are needed --- to prevent constant-folding in the planner, which would hide the bug. --- Wrap this in a single transaction so the transient '=' operator doesn't --- cause problems in concurrent sessions -BEGIN; -CREATE FUNCTION vol(text) returns text as - 'begin return $1; end' language plpgsql volatile; -SELECT CASE - (CASE vol('bar') - WHEN 'foo' THEN 'it was foo!' - WHEN vol(null) THEN 'null input' - WHEN 'bar' THEN 'it was bar!' END - ) - WHEN 'it was foo!' THEN 'foo recognized' - WHEN 'it was bar!' THEN 'bar recognized' - ELSE 'unrecognized' END; - case ----------------- - bar recognized -(1 row) - --- In this case, we can't inline the SQL function without confusing things. -CREATE DOMAIN foodomain AS text; -CREATE FUNCTION volfoo(text) returns foodomain as - 'begin return $1::foodomain; end' language plpgsql volatile; -CREATE FUNCTION inline_eq(foodomain, foodomain) returns boolean as - 'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' language sql; -CREATE OPERATOR = (procedure = inline_eq, - leftarg = foodomain, rightarg = foodomain); -SELECT CASE volfoo('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END; - case ------------- - is not foo -(1 row) - -ROLLBACK; --- Test multiple evaluation of a CASE arg that is a read/write object (#14472) --- Wrap this in a single transaction so the transient '=' operator doesn't --- cause problems in concurrent sessions -BEGIN; -CREATE DOMAIN arrdomain AS int[]; -CREATE FUNCTION make_ad(int,int) returns arrdomain as - 'declare x arrdomain; - begin - x := array[$1,$2]; - return x; - end' language plpgsql volatile; -CREATE FUNCTION ad_eq(arrdomain, arrdomain) returns boolean as - 'begin return array_eq($1, $2); end' language plpgsql; -CREATE OPERATOR = (procedure = ad_eq, - leftarg = arrdomain, rightarg = arrdomain); -SELECT CASE make_ad(1,2) - WHEN array[2,4]::arrdomain THEN 'wrong' - WHEN array[2,5]::arrdomain THEN 'still wrong' - WHEN array[1,2]::arrdomain THEN 'right' - END; - case -------- - right -(1 row) - -ROLLBACK; --- Test interaction of CASE with ArrayCoerceExpr (bug #15471) -BEGIN; -CREATE TYPE casetestenum AS ENUM ('e', 'f', 'g'); -SELECT - CASE 'foo'::text - WHEN 'foo' THEN ARRAY['a', 'b', 'c', 'd'] || enum_range(NULL::casetestenum)::text[] - ELSE ARRAY['x', 'y'] - END; - array ------------------ - {a,b,c,d,e,f,g} -(1 row) - -ROLLBACK; --- --- Clean up --- -DROP TABLE CASE_TBL; -DROP TABLE CASE2_TBL; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/join.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/join.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/join.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/join.out 2021-10-30 22:04:12 -0600 @@ -1,6588 +1,2 @@ --- --- JOIN --- Test JOIN clauses --- -CREATE TABLE J1_TBL ( - i integer, - j integer, - t text -); -CREATE TABLE J2_TBL ( - i integer, - k integer -); -INSERT INTO J1_TBL VALUES (1, 4, 'one'); -INSERT INTO J1_TBL VALUES (2, 3, 'two'); -INSERT INTO J1_TBL VALUES (3, 2, 'three'); -INSERT INTO J1_TBL VALUES (4, 1, 'four'); -INSERT INTO J1_TBL VALUES (5, 0, 'five'); -INSERT INTO J1_TBL VALUES (6, 6, 'six'); -INSERT INTO J1_TBL VALUES (7, 7, 'seven'); -INSERT INTO J1_TBL VALUES (8, 8, 'eight'); -INSERT INTO J1_TBL VALUES (0, NULL, 'zero'); -INSERT INTO J1_TBL VALUES (NULL, NULL, 'null'); -INSERT INTO J1_TBL VALUES (NULL, 0, 'zero'); -INSERT INTO J2_TBL VALUES (1, -1); -INSERT INTO J2_TBL VALUES (2, 2); -INSERT INTO J2_TBL VALUES (3, -3); -INSERT INTO J2_TBL VALUES (2, 4); -INSERT INTO J2_TBL VALUES (5, -5); -INSERT INTO J2_TBL VALUES (5, -5); -INSERT INTO J2_TBL VALUES (0, NULL); -INSERT INTO J2_TBL VALUES (NULL, NULL); -INSERT INTO J2_TBL VALUES (NULL, 0); --- useful in some tests below -create temp table onerow(); -insert into onerow default values; -analyze onerow; --- --- CORRELATION NAMES --- Make sure that table/column aliases are supported --- before diving into more complex join syntax. --- -SELECT * - FROM J1_TBL AS tx; - i | j | t ----+---+------- - 1 | 4 | one - 2 | 3 | two - 3 | 2 | three - 4 | 1 | four - 5 | 0 | five - 6 | 6 | six - 7 | 7 | seven - 8 | 8 | eight - 0 | | zero - | | null - | 0 | zero -(11 rows) - -SELECT * - FROM J1_TBL tx; - i | j | t ----+---+------- - 1 | 4 | one - 2 | 3 | two - 3 | 2 | three - 4 | 1 | four - 5 | 0 | five - 6 | 6 | six - 7 | 7 | seven - 8 | 8 | eight - 0 | | zero - | | null - | 0 | zero -(11 rows) - -SELECT * - FROM J1_TBL AS t1 (a, b, c); - a | b | c ----+---+------- - 1 | 4 | one - 2 | 3 | two - 3 | 2 | three - 4 | 1 | four - 5 | 0 | five - 6 | 6 | six - 7 | 7 | seven - 8 | 8 | eight - 0 | | zero - | | null - | 0 | zero -(11 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c); - a | b | c ----+---+------- - 1 | 4 | one - 2 | 3 | two - 3 | 2 | three - 4 | 1 | four - 5 | 0 | five - 6 | 6 | six - 7 | 7 | seven - 8 | 8 | eight - 0 | | zero - | | null - | 0 | zero -(11 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e); - a | b | c | d | e ----+---+-------+---+---- - 1 | 4 | one | 1 | -1 - 2 | 3 | two | 1 | -1 - 3 | 2 | three | 1 | -1 - 4 | 1 | four | 1 | -1 - 5 | 0 | five | 1 | -1 - 6 | 6 | six | 1 | -1 - 7 | 7 | seven | 1 | -1 - 8 | 8 | eight | 1 | -1 - 0 | | zero | 1 | -1 - | | null | 1 | -1 - | 0 | zero | 1 | -1 - 1 | 4 | one | 2 | 2 - 2 | 3 | two | 2 | 2 - 3 | 2 | three | 2 | 2 - 4 | 1 | four | 2 | 2 - 5 | 0 | five | 2 | 2 - 6 | 6 | six | 2 | 2 - 7 | 7 | seven | 2 | 2 - 8 | 8 | eight | 2 | 2 - 0 | | zero | 2 | 2 - | | null | 2 | 2 - | 0 | zero | 2 | 2 - 1 | 4 | one | 3 | -3 - 2 | 3 | two | 3 | -3 - 3 | 2 | three | 3 | -3 - 4 | 1 | four | 3 | -3 - 5 | 0 | five | 3 | -3 - 6 | 6 | six | 3 | -3 - 7 | 7 | seven | 3 | -3 - 8 | 8 | eight | 3 | -3 - 0 | | zero | 3 | -3 - | | null | 3 | -3 - | 0 | zero | 3 | -3 - 1 | 4 | one | 2 | 4 - 2 | 3 | two | 2 | 4 - 3 | 2 | three | 2 | 4 - 4 | 1 | four | 2 | 4 - 5 | 0 | five | 2 | 4 - 6 | 6 | six | 2 | 4 - 7 | 7 | seven | 2 | 4 - 8 | 8 | eight | 2 | 4 - 0 | | zero | 2 | 4 - | | null | 2 | 4 - | 0 | zero | 2 | 4 - 1 | 4 | one | 5 | -5 - 2 | 3 | two | 5 | -5 - 3 | 2 | three | 5 | -5 - 4 | 1 | four | 5 | -5 - 5 | 0 | five | 5 | -5 - 6 | 6 | six | 5 | -5 - 7 | 7 | seven | 5 | -5 - 8 | 8 | eight | 5 | -5 - 0 | | zero | 5 | -5 - | | null | 5 | -5 - | 0 | zero | 5 | -5 - 1 | 4 | one | 5 | -5 - 2 | 3 | two | 5 | -5 - 3 | 2 | three | 5 | -5 - 4 | 1 | four | 5 | -5 - 5 | 0 | five | 5 | -5 - 6 | 6 | six | 5 | -5 - 7 | 7 | seven | 5 | -5 - 8 | 8 | eight | 5 | -5 - 0 | | zero | 5 | -5 - | | null | 5 | -5 - | 0 | zero | 5 | -5 - 1 | 4 | one | 0 | - 2 | 3 | two | 0 | - 3 | 2 | three | 0 | - 4 | 1 | four | 0 | - 5 | 0 | five | 0 | - 6 | 6 | six | 0 | - 7 | 7 | seven | 0 | - 8 | 8 | eight | 0 | - 0 | | zero | 0 | - | | null | 0 | - | 0 | zero | 0 | - 1 | 4 | one | | - 2 | 3 | two | | - 3 | 2 | three | | - 4 | 1 | four | | - 5 | 0 | five | | - 6 | 6 | six | | - 7 | 7 | seven | | - 8 | 8 | eight | | - 0 | | zero | | - | | null | | - | 0 | zero | | - 1 | 4 | one | | 0 - 2 | 3 | two | | 0 - 3 | 2 | three | | 0 - 4 | 1 | four | | 0 - 5 | 0 | five | | 0 - 6 | 6 | six | | 0 - 7 | 7 | seven | | 0 - 8 | 8 | eight | | 0 - 0 | | zero | | 0 - | | null | | 0 - | 0 | zero | | 0 -(99 rows) - -SELECT t1.a, t2.e - FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e) - WHERE t1.a = t2.d; - a | e ----+---- - 0 | - 1 | -1 - 2 | 2 - 2 | 4 - 3 | -3 - 5 | -5 - 5 | -5 -(7 rows) - --- --- CROSS JOIN --- Qualifications are not allowed on cross joins, --- which degenerate into a standard unqualified inner join. --- -SELECT * - FROM J1_TBL CROSS JOIN J2_TBL; - i | j | t | i | k ----+---+-------+---+---- - 1 | 4 | one | 1 | -1 - 2 | 3 | two | 1 | -1 - 3 | 2 | three | 1 | -1 - 4 | 1 | four | 1 | -1 - 5 | 0 | five | 1 | -1 - 6 | 6 | six | 1 | -1 - 7 | 7 | seven | 1 | -1 - 8 | 8 | eight | 1 | -1 - 0 | | zero | 1 | -1 - | | null | 1 | -1 - | 0 | zero | 1 | -1 - 1 | 4 | one | 2 | 2 - 2 | 3 | two | 2 | 2 - 3 | 2 | three | 2 | 2 - 4 | 1 | four | 2 | 2 - 5 | 0 | five | 2 | 2 - 6 | 6 | six | 2 | 2 - 7 | 7 | seven | 2 | 2 - 8 | 8 | eight | 2 | 2 - 0 | | zero | 2 | 2 - | | null | 2 | 2 - | 0 | zero | 2 | 2 - 1 | 4 | one | 3 | -3 - 2 | 3 | two | 3 | -3 - 3 | 2 | three | 3 | -3 - 4 | 1 | four | 3 | -3 - 5 | 0 | five | 3 | -3 - 6 | 6 | six | 3 | -3 - 7 | 7 | seven | 3 | -3 - 8 | 8 | eight | 3 | -3 - 0 | | zero | 3 | -3 - | | null | 3 | -3 - | 0 | zero | 3 | -3 - 1 | 4 | one | 2 | 4 - 2 | 3 | two | 2 | 4 - 3 | 2 | three | 2 | 4 - 4 | 1 | four | 2 | 4 - 5 | 0 | five | 2 | 4 - 6 | 6 | six | 2 | 4 - 7 | 7 | seven | 2 | 4 - 8 | 8 | eight | 2 | 4 - 0 | | zero | 2 | 4 - | | null | 2 | 4 - | 0 | zero | 2 | 4 - 1 | 4 | one | 5 | -5 - 2 | 3 | two | 5 | -5 - 3 | 2 | three | 5 | -5 - 4 | 1 | four | 5 | -5 - 5 | 0 | five | 5 | -5 - 6 | 6 | six | 5 | -5 - 7 | 7 | seven | 5 | -5 - 8 | 8 | eight | 5 | -5 - 0 | | zero | 5 | -5 - | | null | 5 | -5 - | 0 | zero | 5 | -5 - 1 | 4 | one | 5 | -5 - 2 | 3 | two | 5 | -5 - 3 | 2 | three | 5 | -5 - 4 | 1 | four | 5 | -5 - 5 | 0 | five | 5 | -5 - 6 | 6 | six | 5 | -5 - 7 | 7 | seven | 5 | -5 - 8 | 8 | eight | 5 | -5 - 0 | | zero | 5 | -5 - | | null | 5 | -5 - | 0 | zero | 5 | -5 - 1 | 4 | one | 0 | - 2 | 3 | two | 0 | - 3 | 2 | three | 0 | - 4 | 1 | four | 0 | - 5 | 0 | five | 0 | - 6 | 6 | six | 0 | - 7 | 7 | seven | 0 | - 8 | 8 | eight | 0 | - 0 | | zero | 0 | - | | null | 0 | - | 0 | zero | 0 | - 1 | 4 | one | | - 2 | 3 | two | | - 3 | 2 | three | | - 4 | 1 | four | | - 5 | 0 | five | | - 6 | 6 | six | | - 7 | 7 | seven | | - 8 | 8 | eight | | - 0 | | zero | | - | | null | | - | 0 | zero | | - 1 | 4 | one | | 0 - 2 | 3 | two | | 0 - 3 | 2 | three | | 0 - 4 | 1 | four | | 0 - 5 | 0 | five | | 0 - 6 | 6 | six | | 0 - 7 | 7 | seven | | 0 - 8 | 8 | eight | | 0 - 0 | | zero | | 0 - | | null | | 0 - | 0 | zero | | 0 -(99 rows) - --- ambiguous column -SELECT i, k, t - FROM J1_TBL CROSS JOIN J2_TBL; -ERROR: column reference "i" is ambiguous -LINE 1: SELECT i, k, t - ^ --- resolve previous ambiguity by specifying the table name -SELECT t1.i, k, t - FROM J1_TBL t1 CROSS JOIN J2_TBL t2; - i | k | t ----+----+------- - 1 | -1 | one - 2 | -1 | two - 3 | -1 | three - 4 | -1 | four - 5 | -1 | five - 6 | -1 | six - 7 | -1 | seven - 8 | -1 | eight - 0 | -1 | zero - | -1 | null - | -1 | zero - 1 | 2 | one - 2 | 2 | two - 3 | 2 | three - 4 | 2 | four - 5 | 2 | five - 6 | 2 | six - 7 | 2 | seven - 8 | 2 | eight - 0 | 2 | zero - | 2 | null - | 2 | zero - 1 | -3 | one - 2 | -3 | two - 3 | -3 | three - 4 | -3 | four - 5 | -3 | five - 6 | -3 | six - 7 | -3 | seven - 8 | -3 | eight - 0 | -3 | zero - | -3 | null - | -3 | zero - 1 | 4 | one - 2 | 4 | two - 3 | 4 | three - 4 | 4 | four - 5 | 4 | five - 6 | 4 | six - 7 | 4 | seven - 8 | 4 | eight - 0 | 4 | zero - | 4 | null - | 4 | zero - 1 | -5 | one - 2 | -5 | two - 3 | -5 | three - 4 | -5 | four - 5 | -5 | five - 6 | -5 | six - 7 | -5 | seven - 8 | -5 | eight - 0 | -5 | zero - | -5 | null - | -5 | zero - 1 | -5 | one - 2 | -5 | two - 3 | -5 | three - 4 | -5 | four - 5 | -5 | five - 6 | -5 | six - 7 | -5 | seven - 8 | -5 | eight - 0 | -5 | zero - | -5 | null - | -5 | zero - 1 | | one - 2 | | two - 3 | | three - 4 | | four - 5 | | five - 6 | | six - 7 | | seven - 8 | | eight - 0 | | zero - | | null - | | zero - 1 | | one - 2 | | two - 3 | | three - 4 | | four - 5 | | five - 6 | | six - 7 | | seven - 8 | | eight - 0 | | zero - | | null - | | zero - 1 | 0 | one - 2 | 0 | two - 3 | 0 | three - 4 | 0 | four - 5 | 0 | five - 6 | 0 | six - 7 | 0 | seven - 8 | 0 | eight - 0 | 0 | zero - | 0 | null - | 0 | zero -(99 rows) - -SELECT ii, tt, kk - FROM (J1_TBL CROSS JOIN J2_TBL) - AS tx (ii, jj, tt, ii2, kk); - ii | tt | kk -----+-------+---- - 1 | one | -1 - 2 | two | -1 - 3 | three | -1 - 4 | four | -1 - 5 | five | -1 - 6 | six | -1 - 7 | seven | -1 - 8 | eight | -1 - 0 | zero | -1 - | null | -1 - | zero | -1 - 1 | one | 2 - 2 | two | 2 - 3 | three | 2 - 4 | four | 2 - 5 | five | 2 - 6 | six | 2 - 7 | seven | 2 - 8 | eight | 2 - 0 | zero | 2 - | null | 2 - | zero | 2 - 1 | one | -3 - 2 | two | -3 - 3 | three | -3 - 4 | four | -3 - 5 | five | -3 - 6 | six | -3 - 7 | seven | -3 - 8 | eight | -3 - 0 | zero | -3 - | null | -3 - | zero | -3 - 1 | one | 4 - 2 | two | 4 - 3 | three | 4 - 4 | four | 4 - 5 | five | 4 - 6 | six | 4 - 7 | seven | 4 - 8 | eight | 4 - 0 | zero | 4 - | null | 4 - | zero | 4 - 1 | one | -5 - 2 | two | -5 - 3 | three | -5 - 4 | four | -5 - 5 | five | -5 - 6 | six | -5 - 7 | seven | -5 - 8 | eight | -5 - 0 | zero | -5 - | null | -5 - | zero | -5 - 1 | one | -5 - 2 | two | -5 - 3 | three | -5 - 4 | four | -5 - 5 | five | -5 - 6 | six | -5 - 7 | seven | -5 - 8 | eight | -5 - 0 | zero | -5 - | null | -5 - | zero | -5 - 1 | one | - 2 | two | - 3 | three | - 4 | four | - 5 | five | - 6 | six | - 7 | seven | - 8 | eight | - 0 | zero | - | null | - | zero | - 1 | one | - 2 | two | - 3 | three | - 4 | four | - 5 | five | - 6 | six | - 7 | seven | - 8 | eight | - 0 | zero | - | null | - | zero | - 1 | one | 0 - 2 | two | 0 - 3 | three | 0 - 4 | four | 0 - 5 | five | 0 - 6 | six | 0 - 7 | seven | 0 - 8 | eight | 0 - 0 | zero | 0 - | null | 0 - | zero | 0 -(99 rows) - -SELECT tx.ii, tx.jj, tx.kk - FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e)) - AS tx (ii, jj, tt, ii2, kk); - ii | jj | kk -----+----+---- - 1 | 4 | -1 - 2 | 3 | -1 - 3 | 2 | -1 - 4 | 1 | -1 - 5 | 0 | -1 - 6 | 6 | -1 - 7 | 7 | -1 - 8 | 8 | -1 - 0 | | -1 - | | -1 - | 0 | -1 - 1 | 4 | 2 - 2 | 3 | 2 - 3 | 2 | 2 - 4 | 1 | 2 - 5 | 0 | 2 - 6 | 6 | 2 - 7 | 7 | 2 - 8 | 8 | 2 - 0 | | 2 - | | 2 - | 0 | 2 - 1 | 4 | -3 - 2 | 3 | -3 - 3 | 2 | -3 - 4 | 1 | -3 - 5 | 0 | -3 - 6 | 6 | -3 - 7 | 7 | -3 - 8 | 8 | -3 - 0 | | -3 - | | -3 - | 0 | -3 - 1 | 4 | 4 - 2 | 3 | 4 - 3 | 2 | 4 - 4 | 1 | 4 - 5 | 0 | 4 - 6 | 6 | 4 - 7 | 7 | 4 - 8 | 8 | 4 - 0 | | 4 - | | 4 - | 0 | 4 - 1 | 4 | -5 - 2 | 3 | -5 - 3 | 2 | -5 - 4 | 1 | -5 - 5 | 0 | -5 - 6 | 6 | -5 - 7 | 7 | -5 - 8 | 8 | -5 - 0 | | -5 - | | -5 - | 0 | -5 - 1 | 4 | -5 - 2 | 3 | -5 - 3 | 2 | -5 - 4 | 1 | -5 - 5 | 0 | -5 - 6 | 6 | -5 - 7 | 7 | -5 - 8 | 8 | -5 - 0 | | -5 - | | -5 - | 0 | -5 - 1 | 4 | - 2 | 3 | - 3 | 2 | - 4 | 1 | - 5 | 0 | - 6 | 6 | - 7 | 7 | - 8 | 8 | - 0 | | - | | - | 0 | - 1 | 4 | - 2 | 3 | - 3 | 2 | - 4 | 1 | - 5 | 0 | - 6 | 6 | - 7 | 7 | - 8 | 8 | - 0 | | - | | - | 0 | - 1 | 4 | 0 - 2 | 3 | 0 - 3 | 2 | 0 - 4 | 1 | 0 - 5 | 0 | 0 - 6 | 6 | 0 - 7 | 7 | 0 - 8 | 8 | 0 - 0 | | 0 - | | 0 - | 0 | 0 -(99 rows) - -SELECT * - FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b; - i | j | t | i | k | i | k ----+---+-------+---+----+---+---- - 1 | 4 | one | 1 | -1 | 1 | -1 - 1 | 4 | one | 1 | -1 | 2 | 2 - 1 | 4 | one | 1 | -1 | 3 | -3 - 1 | 4 | one | 1 | -1 | 2 | 4 - 1 | 4 | one | 1 | -1 | 5 | -5 - 1 | 4 | one | 1 | -1 | 5 | -5 - 1 | 4 | one | 1 | -1 | 0 | - 1 | 4 | one | 1 | -1 | | - 1 | 4 | one | 1 | -1 | | 0 - 2 | 3 | two | 1 | -1 | 1 | -1 - 2 | 3 | two | 1 | -1 | 2 | 2 - 2 | 3 | two | 1 | -1 | 3 | -3 - 2 | 3 | two | 1 | -1 | 2 | 4 - 2 | 3 | two | 1 | -1 | 5 | -5 - 2 | 3 | two | 1 | -1 | 5 | -5 - 2 | 3 | two | 1 | -1 | 0 | - 2 | 3 | two | 1 | -1 | | - 2 | 3 | two | 1 | -1 | | 0 - 3 | 2 | three | 1 | -1 | 1 | -1 - 3 | 2 | three | 1 | -1 | 2 | 2 - 3 | 2 | three | 1 | -1 | 3 | -3 - 3 | 2 | three | 1 | -1 | 2 | 4 - 3 | 2 | three | 1 | -1 | 5 | -5 - 3 | 2 | three | 1 | -1 | 5 | -5 - 3 | 2 | three | 1 | -1 | 0 | - 3 | 2 | three | 1 | -1 | | - 3 | 2 | three | 1 | -1 | | 0 - 4 | 1 | four | 1 | -1 | 1 | -1 - 4 | 1 | four | 1 | -1 | 2 | 2 - 4 | 1 | four | 1 | -1 | 3 | -3 - 4 | 1 | four | 1 | -1 | 2 | 4 - 4 | 1 | four | 1 | -1 | 5 | -5 - 4 | 1 | four | 1 | -1 | 5 | -5 - 4 | 1 | four | 1 | -1 | 0 | - 4 | 1 | four | 1 | -1 | | - 4 | 1 | four | 1 | -1 | | 0 - 5 | 0 | five | 1 | -1 | 1 | -1 - 5 | 0 | five | 1 | -1 | 2 | 2 - 5 | 0 | five | 1 | -1 | 3 | -3 - 5 | 0 | five | 1 | -1 | 2 | 4 - 5 | 0 | five | 1 | -1 | 5 | -5 - 5 | 0 | five | 1 | -1 | 5 | -5 - 5 | 0 | five | 1 | -1 | 0 | - 5 | 0 | five | 1 | -1 | | - 5 | 0 | five | 1 | -1 | | 0 - 6 | 6 | six | 1 | -1 | 1 | -1 - 6 | 6 | six | 1 | -1 | 2 | 2 - 6 | 6 | six | 1 | -1 | 3 | -3 - 6 | 6 | six | 1 | -1 | 2 | 4 - 6 | 6 | six | 1 | -1 | 5 | -5 - 6 | 6 | six | 1 | -1 | 5 | -5 - 6 | 6 | six | 1 | -1 | 0 | - 6 | 6 | six | 1 | -1 | | - 6 | 6 | six | 1 | -1 | | 0 - 7 | 7 | seven | 1 | -1 | 1 | -1 - 7 | 7 | seven | 1 | -1 | 2 | 2 - 7 | 7 | seven | 1 | -1 | 3 | -3 - 7 | 7 | seven | 1 | -1 | 2 | 4 - 7 | 7 | seven | 1 | -1 | 5 | -5 - 7 | 7 | seven | 1 | -1 | 5 | -5 - 7 | 7 | seven | 1 | -1 | 0 | - 7 | 7 | seven | 1 | -1 | | - 7 | 7 | seven | 1 | -1 | | 0 - 8 | 8 | eight | 1 | -1 | 1 | -1 - 8 | 8 | eight | 1 | -1 | 2 | 2 - 8 | 8 | eight | 1 | -1 | 3 | -3 - 8 | 8 | eight | 1 | -1 | 2 | 4 - 8 | 8 | eight | 1 | -1 | 5 | -5 - 8 | 8 | eight | 1 | -1 | 5 | -5 - 8 | 8 | eight | 1 | -1 | 0 | - 8 | 8 | eight | 1 | -1 | | - 8 | 8 | eight | 1 | -1 | | 0 - 0 | | zero | 1 | -1 | 1 | -1 - 0 | | zero | 1 | -1 | 2 | 2 - 0 | | zero | 1 | -1 | 3 | -3 - 0 | | zero | 1 | -1 | 2 | 4 - 0 | | zero | 1 | -1 | 5 | -5 - 0 | | zero | 1 | -1 | 5 | -5 - 0 | | zero | 1 | -1 | 0 | - 0 | | zero | 1 | -1 | | - 0 | | zero | 1 | -1 | | 0 - | | null | 1 | -1 | 1 | -1 - | | null | 1 | -1 | 2 | 2 - | | null | 1 | -1 | 3 | -3 - | | null | 1 | -1 | 2 | 4 - | | null | 1 | -1 | 5 | -5 - | | null | 1 | -1 | 5 | -5 - | | null | 1 | -1 | 0 | - | | null | 1 | -1 | | - | | null | 1 | -1 | | 0 - | 0 | zero | 1 | -1 | 1 | -1 - | 0 | zero | 1 | -1 | 2 | 2 - | 0 | zero | 1 | -1 | 3 | -3 - | 0 | zero | 1 | -1 | 2 | 4 - | 0 | zero | 1 | -1 | 5 | -5 - | 0 | zero | 1 | -1 | 5 | -5 - | 0 | zero | 1 | -1 | 0 | - | 0 | zero | 1 | -1 | | - | 0 | zero | 1 | -1 | | 0 - 1 | 4 | one | 2 | 2 | 1 | -1 - 1 | 4 | one | 2 | 2 | 2 | 2 - 1 | 4 | one | 2 | 2 | 3 | -3 - 1 | 4 | one | 2 | 2 | 2 | 4 - 1 | 4 | one | 2 | 2 | 5 | -5 - 1 | 4 | one | 2 | 2 | 5 | -5 - 1 | 4 | one | 2 | 2 | 0 | - 1 | 4 | one | 2 | 2 | | - 1 | 4 | one | 2 | 2 | | 0 - 2 | 3 | two | 2 | 2 | 1 | -1 - 2 | 3 | two | 2 | 2 | 2 | 2 - 2 | 3 | two | 2 | 2 | 3 | -3 - 2 | 3 | two | 2 | 2 | 2 | 4 - 2 | 3 | two | 2 | 2 | 5 | -5 - 2 | 3 | two | 2 | 2 | 5 | -5 - 2 | 3 | two | 2 | 2 | 0 | - 2 | 3 | two | 2 | 2 | | - 2 | 3 | two | 2 | 2 | | 0 - 3 | 2 | three | 2 | 2 | 1 | -1 - 3 | 2 | three | 2 | 2 | 2 | 2 - 3 | 2 | three | 2 | 2 | 3 | -3 - 3 | 2 | three | 2 | 2 | 2 | 4 - 3 | 2 | three | 2 | 2 | 5 | -5 - 3 | 2 | three | 2 | 2 | 5 | -5 - 3 | 2 | three | 2 | 2 | 0 | - 3 | 2 | three | 2 | 2 | | - 3 | 2 | three | 2 | 2 | | 0 - 4 | 1 | four | 2 | 2 | 1 | -1 - 4 | 1 | four | 2 | 2 | 2 | 2 - 4 | 1 | four | 2 | 2 | 3 | -3 - 4 | 1 | four | 2 | 2 | 2 | 4 - 4 | 1 | four | 2 | 2 | 5 | -5 - 4 | 1 | four | 2 | 2 | 5 | -5 - 4 | 1 | four | 2 | 2 | 0 | - 4 | 1 | four | 2 | 2 | | - 4 | 1 | four | 2 | 2 | | 0 - 5 | 0 | five | 2 | 2 | 1 | -1 - 5 | 0 | five | 2 | 2 | 2 | 2 - 5 | 0 | five | 2 | 2 | 3 | -3 - 5 | 0 | five | 2 | 2 | 2 | 4 - 5 | 0 | five | 2 | 2 | 5 | -5 - 5 | 0 | five | 2 | 2 | 5 | -5 - 5 | 0 | five | 2 | 2 | 0 | - 5 | 0 | five | 2 | 2 | | - 5 | 0 | five | 2 | 2 | | 0 - 6 | 6 | six | 2 | 2 | 1 | -1 - 6 | 6 | six | 2 | 2 | 2 | 2 - 6 | 6 | six | 2 | 2 | 3 | -3 - 6 | 6 | six | 2 | 2 | 2 | 4 - 6 | 6 | six | 2 | 2 | 5 | -5 - 6 | 6 | six | 2 | 2 | 5 | -5 - 6 | 6 | six | 2 | 2 | 0 | - 6 | 6 | six | 2 | 2 | | - 6 | 6 | six | 2 | 2 | | 0 - 7 | 7 | seven | 2 | 2 | 1 | -1 - 7 | 7 | seven | 2 | 2 | 2 | 2 - 7 | 7 | seven | 2 | 2 | 3 | -3 - 7 | 7 | seven | 2 | 2 | 2 | 4 - 7 | 7 | seven | 2 | 2 | 5 | -5 - 7 | 7 | seven | 2 | 2 | 5 | -5 - 7 | 7 | seven | 2 | 2 | 0 | - 7 | 7 | seven | 2 | 2 | | - 7 | 7 | seven | 2 | 2 | | 0 - 8 | 8 | eight | 2 | 2 | 1 | -1 - 8 | 8 | eight | 2 | 2 | 2 | 2 - 8 | 8 | eight | 2 | 2 | 3 | -3 - 8 | 8 | eight | 2 | 2 | 2 | 4 - 8 | 8 | eight | 2 | 2 | 5 | -5 - 8 | 8 | eight | 2 | 2 | 5 | -5 - 8 | 8 | eight | 2 | 2 | 0 | - 8 | 8 | eight | 2 | 2 | | - 8 | 8 | eight | 2 | 2 | | 0 - 0 | | zero | 2 | 2 | 1 | -1 - 0 | | zero | 2 | 2 | 2 | 2 - 0 | | zero | 2 | 2 | 3 | -3 - 0 | | zero | 2 | 2 | 2 | 4 - 0 | | zero | 2 | 2 | 5 | -5 - 0 | | zero | 2 | 2 | 5 | -5 - 0 | | zero | 2 | 2 | 0 | - 0 | | zero | 2 | 2 | | - 0 | | zero | 2 | 2 | | 0 - | | null | 2 | 2 | 1 | -1 - | | null | 2 | 2 | 2 | 2 - | | null | 2 | 2 | 3 | -3 - | | null | 2 | 2 | 2 | 4 - | | null | 2 | 2 | 5 | -5 - | | null | 2 | 2 | 5 | -5 - | | null | 2 | 2 | 0 | - | | null | 2 | 2 | | - | | null | 2 | 2 | | 0 - | 0 | zero | 2 | 2 | 1 | -1 - | 0 | zero | 2 | 2 | 2 | 2 - | 0 | zero | 2 | 2 | 3 | -3 - | 0 | zero | 2 | 2 | 2 | 4 - | 0 | zero | 2 | 2 | 5 | -5 - | 0 | zero | 2 | 2 | 5 | -5 - | 0 | zero | 2 | 2 | 0 | - | 0 | zero | 2 | 2 | | - | 0 | zero | 2 | 2 | | 0 - 1 | 4 | one | 3 | -3 | 1 | -1 - 1 | 4 | one | 3 | -3 | 2 | 2 - 1 | 4 | one | 3 | -3 | 3 | -3 - 1 | 4 | one | 3 | -3 | 2 | 4 - 1 | 4 | one | 3 | -3 | 5 | -5 - 1 | 4 | one | 3 | -3 | 5 | -5 - 1 | 4 | one | 3 | -3 | 0 | - 1 | 4 | one | 3 | -3 | | - 1 | 4 | one | 3 | -3 | | 0 - 2 | 3 | two | 3 | -3 | 1 | -1 - 2 | 3 | two | 3 | -3 | 2 | 2 - 2 | 3 | two | 3 | -3 | 3 | -3 - 2 | 3 | two | 3 | -3 | 2 | 4 - 2 | 3 | two | 3 | -3 | 5 | -5 - 2 | 3 | two | 3 | -3 | 5 | -5 - 2 | 3 | two | 3 | -3 | 0 | - 2 | 3 | two | 3 | -3 | | - 2 | 3 | two | 3 | -3 | | 0 - 3 | 2 | three | 3 | -3 | 1 | -1 - 3 | 2 | three | 3 | -3 | 2 | 2 - 3 | 2 | three | 3 | -3 | 3 | -3 - 3 | 2 | three | 3 | -3 | 2 | 4 - 3 | 2 | three | 3 | -3 | 5 | -5 - 3 | 2 | three | 3 | -3 | 5 | -5 - 3 | 2 | three | 3 | -3 | 0 | - 3 | 2 | three | 3 | -3 | | - 3 | 2 | three | 3 | -3 | | 0 - 4 | 1 | four | 3 | -3 | 1 | -1 - 4 | 1 | four | 3 | -3 | 2 | 2 - 4 | 1 | four | 3 | -3 | 3 | -3 - 4 | 1 | four | 3 | -3 | 2 | 4 - 4 | 1 | four | 3 | -3 | 5 | -5 - 4 | 1 | four | 3 | -3 | 5 | -5 - 4 | 1 | four | 3 | -3 | 0 | - 4 | 1 | four | 3 | -3 | | - 4 | 1 | four | 3 | -3 | | 0 - 5 | 0 | five | 3 | -3 | 1 | -1 - 5 | 0 | five | 3 | -3 | 2 | 2 - 5 | 0 | five | 3 | -3 | 3 | -3 - 5 | 0 | five | 3 | -3 | 2 | 4 - 5 | 0 | five | 3 | -3 | 5 | -5 - 5 | 0 | five | 3 | -3 | 5 | -5 - 5 | 0 | five | 3 | -3 | 0 | - 5 | 0 | five | 3 | -3 | | - 5 | 0 | five | 3 | -3 | | 0 - 6 | 6 | six | 3 | -3 | 1 | -1 - 6 | 6 | six | 3 | -3 | 2 | 2 - 6 | 6 | six | 3 | -3 | 3 | -3 - 6 | 6 | six | 3 | -3 | 2 | 4 - 6 | 6 | six | 3 | -3 | 5 | -5 - 6 | 6 | six | 3 | -3 | 5 | -5 - 6 | 6 | six | 3 | -3 | 0 | - 6 | 6 | six | 3 | -3 | | - 6 | 6 | six | 3 | -3 | | 0 - 7 | 7 | seven | 3 | -3 | 1 | -1 - 7 | 7 | seven | 3 | -3 | 2 | 2 - 7 | 7 | seven | 3 | -3 | 3 | -3 - 7 | 7 | seven | 3 | -3 | 2 | 4 - 7 | 7 | seven | 3 | -3 | 5 | -5 - 7 | 7 | seven | 3 | -3 | 5 | -5 - 7 | 7 | seven | 3 | -3 | 0 | - 7 | 7 | seven | 3 | -3 | | - 7 | 7 | seven | 3 | -3 | | 0 - 8 | 8 | eight | 3 | -3 | 1 | -1 - 8 | 8 | eight | 3 | -3 | 2 | 2 - 8 | 8 | eight | 3 | -3 | 3 | -3 - 8 | 8 | eight | 3 | -3 | 2 | 4 - 8 | 8 | eight | 3 | -3 | 5 | -5 - 8 | 8 | eight | 3 | -3 | 5 | -5 - 8 | 8 | eight | 3 | -3 | 0 | - 8 | 8 | eight | 3 | -3 | | - 8 | 8 | eight | 3 | -3 | | 0 - 0 | | zero | 3 | -3 | 1 | -1 - 0 | | zero | 3 | -3 | 2 | 2 - 0 | | zero | 3 | -3 | 3 | -3 - 0 | | zero | 3 | -3 | 2 | 4 - 0 | | zero | 3 | -3 | 5 | -5 - 0 | | zero | 3 | -3 | 5 | -5 - 0 | | zero | 3 | -3 | 0 | - 0 | | zero | 3 | -3 | | - 0 | | zero | 3 | -3 | | 0 - | | null | 3 | -3 | 1 | -1 - | | null | 3 | -3 | 2 | 2 - | | null | 3 | -3 | 3 | -3 - | | null | 3 | -3 | 2 | 4 - | | null | 3 | -3 | 5 | -5 - | | null | 3 | -3 | 5 | -5 - | | null | 3 | -3 | 0 | - | | null | 3 | -3 | | - | | null | 3 | -3 | | 0 - | 0 | zero | 3 | -3 | 1 | -1 - | 0 | zero | 3 | -3 | 2 | 2 - | 0 | zero | 3 | -3 | 3 | -3 - | 0 | zero | 3 | -3 | 2 | 4 - | 0 | zero | 3 | -3 | 5 | -5 - | 0 | zero | 3 | -3 | 5 | -5 - | 0 | zero | 3 | -3 | 0 | - | 0 | zero | 3 | -3 | | - | 0 | zero | 3 | -3 | | 0 - 1 | 4 | one | 2 | 4 | 1 | -1 - 1 | 4 | one | 2 | 4 | 2 | 2 - 1 | 4 | one | 2 | 4 | 3 | -3 - 1 | 4 | one | 2 | 4 | 2 | 4 - 1 | 4 | one | 2 | 4 | 5 | -5 - 1 | 4 | one | 2 | 4 | 5 | -5 - 1 | 4 | one | 2 | 4 | 0 | - 1 | 4 | one | 2 | 4 | | - 1 | 4 | one | 2 | 4 | | 0 - 2 | 3 | two | 2 | 4 | 1 | -1 - 2 | 3 | two | 2 | 4 | 2 | 2 - 2 | 3 | two | 2 | 4 | 3 | -3 - 2 | 3 | two | 2 | 4 | 2 | 4 - 2 | 3 | two | 2 | 4 | 5 | -5 - 2 | 3 | two | 2 | 4 | 5 | -5 - 2 | 3 | two | 2 | 4 | 0 | - 2 | 3 | two | 2 | 4 | | - 2 | 3 | two | 2 | 4 | | 0 - 3 | 2 | three | 2 | 4 | 1 | -1 - 3 | 2 | three | 2 | 4 | 2 | 2 - 3 | 2 | three | 2 | 4 | 3 | -3 - 3 | 2 | three | 2 | 4 | 2 | 4 - 3 | 2 | three | 2 | 4 | 5 | -5 - 3 | 2 | three | 2 | 4 | 5 | -5 - 3 | 2 | three | 2 | 4 | 0 | - 3 | 2 | three | 2 | 4 | | - 3 | 2 | three | 2 | 4 | | 0 - 4 | 1 | four | 2 | 4 | 1 | -1 - 4 | 1 | four | 2 | 4 | 2 | 2 - 4 | 1 | four | 2 | 4 | 3 | -3 - 4 | 1 | four | 2 | 4 | 2 | 4 - 4 | 1 | four | 2 | 4 | 5 | -5 - 4 | 1 | four | 2 | 4 | 5 | -5 - 4 | 1 | four | 2 | 4 | 0 | - 4 | 1 | four | 2 | 4 | | - 4 | 1 | four | 2 | 4 | | 0 - 5 | 0 | five | 2 | 4 | 1 | -1 - 5 | 0 | five | 2 | 4 | 2 | 2 - 5 | 0 | five | 2 | 4 | 3 | -3 - 5 | 0 | five | 2 | 4 | 2 | 4 - 5 | 0 | five | 2 | 4 | 5 | -5 - 5 | 0 | five | 2 | 4 | 5 | -5 - 5 | 0 | five | 2 | 4 | 0 | - 5 | 0 | five | 2 | 4 | | - 5 | 0 | five | 2 | 4 | | 0 - 6 | 6 | six | 2 | 4 | 1 | -1 - 6 | 6 | six | 2 | 4 | 2 | 2 - 6 | 6 | six | 2 | 4 | 3 | -3 - 6 | 6 | six | 2 | 4 | 2 | 4 - 6 | 6 | six | 2 | 4 | 5 | -5 - 6 | 6 | six | 2 | 4 | 5 | -5 - 6 | 6 | six | 2 | 4 | 0 | - 6 | 6 | six | 2 | 4 | | - 6 | 6 | six | 2 | 4 | | 0 - 7 | 7 | seven | 2 | 4 | 1 | -1 - 7 | 7 | seven | 2 | 4 | 2 | 2 - 7 | 7 | seven | 2 | 4 | 3 | -3 - 7 | 7 | seven | 2 | 4 | 2 | 4 - 7 | 7 | seven | 2 | 4 | 5 | -5 - 7 | 7 | seven | 2 | 4 | 5 | -5 - 7 | 7 | seven | 2 | 4 | 0 | - 7 | 7 | seven | 2 | 4 | | - 7 | 7 | seven | 2 | 4 | | 0 - 8 | 8 | eight | 2 | 4 | 1 | -1 - 8 | 8 | eight | 2 | 4 | 2 | 2 - 8 | 8 | eight | 2 | 4 | 3 | -3 - 8 | 8 | eight | 2 | 4 | 2 | 4 - 8 | 8 | eight | 2 | 4 | 5 | -5 - 8 | 8 | eight | 2 | 4 | 5 | -5 - 8 | 8 | eight | 2 | 4 | 0 | - 8 | 8 | eight | 2 | 4 | | - 8 | 8 | eight | 2 | 4 | | 0 - 0 | | zero | 2 | 4 | 1 | -1 - 0 | | zero | 2 | 4 | 2 | 2 - 0 | | zero | 2 | 4 | 3 | -3 - 0 | | zero | 2 | 4 | 2 | 4 - 0 | | zero | 2 | 4 | 5 | -5 - 0 | | zero | 2 | 4 | 5 | -5 - 0 | | zero | 2 | 4 | 0 | - 0 | | zero | 2 | 4 | | - 0 | | zero | 2 | 4 | | 0 - | | null | 2 | 4 | 1 | -1 - | | null | 2 | 4 | 2 | 2 - | | null | 2 | 4 | 3 | -3 - | | null | 2 | 4 | 2 | 4 - | | null | 2 | 4 | 5 | -5 - | | null | 2 | 4 | 5 | -5 - | | null | 2 | 4 | 0 | - | | null | 2 | 4 | | - | | null | 2 | 4 | | 0 - | 0 | zero | 2 | 4 | 1 | -1 - | 0 | zero | 2 | 4 | 2 | 2 - | 0 | zero | 2 | 4 | 3 | -3 - | 0 | zero | 2 | 4 | 2 | 4 - | 0 | zero | 2 | 4 | 5 | -5 - | 0 | zero | 2 | 4 | 5 | -5 - | 0 | zero | 2 | 4 | 0 | - | 0 | zero | 2 | 4 | | - | 0 | zero | 2 | 4 | | 0 - 1 | 4 | one | 5 | -5 | 1 | -1 - 1 | 4 | one | 5 | -5 | 2 | 2 - 1 | 4 | one | 5 | -5 | 3 | -3 - 1 | 4 | one | 5 | -5 | 2 | 4 - 1 | 4 | one | 5 | -5 | 5 | -5 - 1 | 4 | one | 5 | -5 | 5 | -5 - 1 | 4 | one | 5 | -5 | 0 | - 1 | 4 | one | 5 | -5 | | - 1 | 4 | one | 5 | -5 | | 0 - 2 | 3 | two | 5 | -5 | 1 | -1 - 2 | 3 | two | 5 | -5 | 2 | 2 - 2 | 3 | two | 5 | -5 | 3 | -3 - 2 | 3 | two | 5 | -5 | 2 | 4 - 2 | 3 | two | 5 | -5 | 5 | -5 - 2 | 3 | two | 5 | -5 | 5 | -5 - 2 | 3 | two | 5 | -5 | 0 | - 2 | 3 | two | 5 | -5 | | - 2 | 3 | two | 5 | -5 | | 0 - 3 | 2 | three | 5 | -5 | 1 | -1 - 3 | 2 | three | 5 | -5 | 2 | 2 - 3 | 2 | three | 5 | -5 | 3 | -3 - 3 | 2 | three | 5 | -5 | 2 | 4 - 3 | 2 | three | 5 | -5 | 5 | -5 - 3 | 2 | three | 5 | -5 | 5 | -5 - 3 | 2 | three | 5 | -5 | 0 | - 3 | 2 | three | 5 | -5 | | - 3 | 2 | three | 5 | -5 | | 0 - 4 | 1 | four | 5 | -5 | 1 | -1 - 4 | 1 | four | 5 | -5 | 2 | 2 - 4 | 1 | four | 5 | -5 | 3 | -3 - 4 | 1 | four | 5 | -5 | 2 | 4 - 4 | 1 | four | 5 | -5 | 5 | -5 - 4 | 1 | four | 5 | -5 | 5 | -5 - 4 | 1 | four | 5 | -5 | 0 | - 4 | 1 | four | 5 | -5 | | - 4 | 1 | four | 5 | -5 | | 0 - 5 | 0 | five | 5 | -5 | 1 | -1 - 5 | 0 | five | 5 | -5 | 2 | 2 - 5 | 0 | five | 5 | -5 | 3 | -3 - 5 | 0 | five | 5 | -5 | 2 | 4 - 5 | 0 | five | 5 | -5 | 5 | -5 - 5 | 0 | five | 5 | -5 | 5 | -5 - 5 | 0 | five | 5 | -5 | 0 | - 5 | 0 | five | 5 | -5 | | - 5 | 0 | five | 5 | -5 | | 0 - 6 | 6 | six | 5 | -5 | 1 | -1 - 6 | 6 | six | 5 | -5 | 2 | 2 - 6 | 6 | six | 5 | -5 | 3 | -3 - 6 | 6 | six | 5 | -5 | 2 | 4 - 6 | 6 | six | 5 | -5 | 5 | -5 - 6 | 6 | six | 5 | -5 | 5 | -5 - 6 | 6 | six | 5 | -5 | 0 | - 6 | 6 | six | 5 | -5 | | - 6 | 6 | six | 5 | -5 | | 0 - 7 | 7 | seven | 5 | -5 | 1 | -1 - 7 | 7 | seven | 5 | -5 | 2 | 2 - 7 | 7 | seven | 5 | -5 | 3 | -3 - 7 | 7 | seven | 5 | -5 | 2 | 4 - 7 | 7 | seven | 5 | -5 | 5 | -5 - 7 | 7 | seven | 5 | -5 | 5 | -5 - 7 | 7 | seven | 5 | -5 | 0 | - 7 | 7 | seven | 5 | -5 | | - 7 | 7 | seven | 5 | -5 | | 0 - 8 | 8 | eight | 5 | -5 | 1 | -1 - 8 | 8 | eight | 5 | -5 | 2 | 2 - 8 | 8 | eight | 5 | -5 | 3 | -3 - 8 | 8 | eight | 5 | -5 | 2 | 4 - 8 | 8 | eight | 5 | -5 | 5 | -5 - 8 | 8 | eight | 5 | -5 | 5 | -5 - 8 | 8 | eight | 5 | -5 | 0 | - 8 | 8 | eight | 5 | -5 | | - 8 | 8 | eight | 5 | -5 | | 0 - 0 | | zero | 5 | -5 | 1 | -1 - 0 | | zero | 5 | -5 | 2 | 2 - 0 | | zero | 5 | -5 | 3 | -3 - 0 | | zero | 5 | -5 | 2 | 4 - 0 | | zero | 5 | -5 | 5 | -5 - 0 | | zero | 5 | -5 | 5 | -5 - 0 | | zero | 5 | -5 | 0 | - 0 | | zero | 5 | -5 | | - 0 | | zero | 5 | -5 | | 0 - | | null | 5 | -5 | 1 | -1 - | | null | 5 | -5 | 2 | 2 - | | null | 5 | -5 | 3 | -3 - | | null | 5 | -5 | 2 | 4 - | | null | 5 | -5 | 5 | -5 - | | null | 5 | -5 | 5 | -5 - | | null | 5 | -5 | 0 | - | | null | 5 | -5 | | - | | null | 5 | -5 | | 0 - | 0 | zero | 5 | -5 | 1 | -1 - | 0 | zero | 5 | -5 | 2 | 2 - | 0 | zero | 5 | -5 | 3 | -3 - | 0 | zero | 5 | -5 | 2 | 4 - | 0 | zero | 5 | -5 | 5 | -5 - | 0 | zero | 5 | -5 | 5 | -5 - | 0 | zero | 5 | -5 | 0 | - | 0 | zero | 5 | -5 | | - | 0 | zero | 5 | -5 | | 0 - 1 | 4 | one | 5 | -5 | 1 | -1 - 1 | 4 | one | 5 | -5 | 2 | 2 - 1 | 4 | one | 5 | -5 | 3 | -3 - 1 | 4 | one | 5 | -5 | 2 | 4 - 1 | 4 | one | 5 | -5 | 5 | -5 - 1 | 4 | one | 5 | -5 | 5 | -5 - 1 | 4 | one | 5 | -5 | 0 | - 1 | 4 | one | 5 | -5 | | - 1 | 4 | one | 5 | -5 | | 0 - 2 | 3 | two | 5 | -5 | 1 | -1 - 2 | 3 | two | 5 | -5 | 2 | 2 - 2 | 3 | two | 5 | -5 | 3 | -3 - 2 | 3 | two | 5 | -5 | 2 | 4 - 2 | 3 | two | 5 | -5 | 5 | -5 - 2 | 3 | two | 5 | -5 | 5 | -5 - 2 | 3 | two | 5 | -5 | 0 | - 2 | 3 | two | 5 | -5 | | - 2 | 3 | two | 5 | -5 | | 0 - 3 | 2 | three | 5 | -5 | 1 | -1 - 3 | 2 | three | 5 | -5 | 2 | 2 - 3 | 2 | three | 5 | -5 | 3 | -3 - 3 | 2 | three | 5 | -5 | 2 | 4 - 3 | 2 | three | 5 | -5 | 5 | -5 - 3 | 2 | three | 5 | -5 | 5 | -5 - 3 | 2 | three | 5 | -5 | 0 | - 3 | 2 | three | 5 | -5 | | - 3 | 2 | three | 5 | -5 | | 0 - 4 | 1 | four | 5 | -5 | 1 | -1 - 4 | 1 | four | 5 | -5 | 2 | 2 - 4 | 1 | four | 5 | -5 | 3 | -3 - 4 | 1 | four | 5 | -5 | 2 | 4 - 4 | 1 | four | 5 | -5 | 5 | -5 - 4 | 1 | four | 5 | -5 | 5 | -5 - 4 | 1 | four | 5 | -5 | 0 | - 4 | 1 | four | 5 | -5 | | - 4 | 1 | four | 5 | -5 | | 0 - 5 | 0 | five | 5 | -5 | 1 | -1 - 5 | 0 | five | 5 | -5 | 2 | 2 - 5 | 0 | five | 5 | -5 | 3 | -3 - 5 | 0 | five | 5 | -5 | 2 | 4 - 5 | 0 | five | 5 | -5 | 5 | -5 - 5 | 0 | five | 5 | -5 | 5 | -5 - 5 | 0 | five | 5 | -5 | 0 | - 5 | 0 | five | 5 | -5 | | - 5 | 0 | five | 5 | -5 | | 0 - 6 | 6 | six | 5 | -5 | 1 | -1 - 6 | 6 | six | 5 | -5 | 2 | 2 - 6 | 6 | six | 5 | -5 | 3 | -3 - 6 | 6 | six | 5 | -5 | 2 | 4 - 6 | 6 | six | 5 | -5 | 5 | -5 - 6 | 6 | six | 5 | -5 | 5 | -5 - 6 | 6 | six | 5 | -5 | 0 | - 6 | 6 | six | 5 | -5 | | - 6 | 6 | six | 5 | -5 | | 0 - 7 | 7 | seven | 5 | -5 | 1 | -1 - 7 | 7 | seven | 5 | -5 | 2 | 2 - 7 | 7 | seven | 5 | -5 | 3 | -3 - 7 | 7 | seven | 5 | -5 | 2 | 4 - 7 | 7 | seven | 5 | -5 | 5 | -5 - 7 | 7 | seven | 5 | -5 | 5 | -5 - 7 | 7 | seven | 5 | -5 | 0 | - 7 | 7 | seven | 5 | -5 | | - 7 | 7 | seven | 5 | -5 | | 0 - 8 | 8 | eight | 5 | -5 | 1 | -1 - 8 | 8 | eight | 5 | -5 | 2 | 2 - 8 | 8 | eight | 5 | -5 | 3 | -3 - 8 | 8 | eight | 5 | -5 | 2 | 4 - 8 | 8 | eight | 5 | -5 | 5 | -5 - 8 | 8 | eight | 5 | -5 | 5 | -5 - 8 | 8 | eight | 5 | -5 | 0 | - 8 | 8 | eight | 5 | -5 | | - 8 | 8 | eight | 5 | -5 | | 0 - 0 | | zero | 5 | -5 | 1 | -1 - 0 | | zero | 5 | -5 | 2 | 2 - 0 | | zero | 5 | -5 | 3 | -3 - 0 | | zero | 5 | -5 | 2 | 4 - 0 | | zero | 5 | -5 | 5 | -5 - 0 | | zero | 5 | -5 | 5 | -5 - 0 | | zero | 5 | -5 | 0 | - 0 | | zero | 5 | -5 | | - 0 | | zero | 5 | -5 | | 0 - | | null | 5 | -5 | 1 | -1 - | | null | 5 | -5 | 2 | 2 - | | null | 5 | -5 | 3 | -3 - | | null | 5 | -5 | 2 | 4 - | | null | 5 | -5 | 5 | -5 - | | null | 5 | -5 | 5 | -5 - | | null | 5 | -5 | 0 | - | | null | 5 | -5 | | - | | null | 5 | -5 | | 0 - | 0 | zero | 5 | -5 | 1 | -1 - | 0 | zero | 5 | -5 | 2 | 2 - | 0 | zero | 5 | -5 | 3 | -3 - | 0 | zero | 5 | -5 | 2 | 4 - | 0 | zero | 5 | -5 | 5 | -5 - | 0 | zero | 5 | -5 | 5 | -5 - | 0 | zero | 5 | -5 | 0 | - | 0 | zero | 5 | -5 | | - | 0 | zero | 5 | -5 | | 0 - 1 | 4 | one | 0 | | 1 | -1 - 1 | 4 | one | 0 | | 2 | 2 - 1 | 4 | one | 0 | | 3 | -3 - 1 | 4 | one | 0 | | 2 | 4 - 1 | 4 | one | 0 | | 5 | -5 - 1 | 4 | one | 0 | | 5 | -5 - 1 | 4 | one | 0 | | 0 | - 1 | 4 | one | 0 | | | - 1 | 4 | one | 0 | | | 0 - 2 | 3 | two | 0 | | 1 | -1 - 2 | 3 | two | 0 | | 2 | 2 - 2 | 3 | two | 0 | | 3 | -3 - 2 | 3 | two | 0 | | 2 | 4 - 2 | 3 | two | 0 | | 5 | -5 - 2 | 3 | two | 0 | | 5 | -5 - 2 | 3 | two | 0 | | 0 | - 2 | 3 | two | 0 | | | - 2 | 3 | two | 0 | | | 0 - 3 | 2 | three | 0 | | 1 | -1 - 3 | 2 | three | 0 | | 2 | 2 - 3 | 2 | three | 0 | | 3 | -3 - 3 | 2 | three | 0 | | 2 | 4 - 3 | 2 | three | 0 | | 5 | -5 - 3 | 2 | three | 0 | | 5 | -5 - 3 | 2 | three | 0 | | 0 | - 3 | 2 | three | 0 | | | - 3 | 2 | three | 0 | | | 0 - 4 | 1 | four | 0 | | 1 | -1 - 4 | 1 | four | 0 | | 2 | 2 - 4 | 1 | four | 0 | | 3 | -3 - 4 | 1 | four | 0 | | 2 | 4 - 4 | 1 | four | 0 | | 5 | -5 - 4 | 1 | four | 0 | | 5 | -5 - 4 | 1 | four | 0 | | 0 | - 4 | 1 | four | 0 | | | - 4 | 1 | four | 0 | | | 0 - 5 | 0 | five | 0 | | 1 | -1 - 5 | 0 | five | 0 | | 2 | 2 - 5 | 0 | five | 0 | | 3 | -3 - 5 | 0 | five | 0 | | 2 | 4 - 5 | 0 | five | 0 | | 5 | -5 - 5 | 0 | five | 0 | | 5 | -5 - 5 | 0 | five | 0 | | 0 | - 5 | 0 | five | 0 | | | - 5 | 0 | five | 0 | | | 0 - 6 | 6 | six | 0 | | 1 | -1 - 6 | 6 | six | 0 | | 2 | 2 - 6 | 6 | six | 0 | | 3 | -3 - 6 | 6 | six | 0 | | 2 | 4 - 6 | 6 | six | 0 | | 5 | -5 - 6 | 6 | six | 0 | | 5 | -5 - 6 | 6 | six | 0 | | 0 | - 6 | 6 | six | 0 | | | - 6 | 6 | six | 0 | | | 0 - 7 | 7 | seven | 0 | | 1 | -1 - 7 | 7 | seven | 0 | | 2 | 2 - 7 | 7 | seven | 0 | | 3 | -3 - 7 | 7 | seven | 0 | | 2 | 4 - 7 | 7 | seven | 0 | | 5 | -5 - 7 | 7 | seven | 0 | | 5 | -5 - 7 | 7 | seven | 0 | | 0 | - 7 | 7 | seven | 0 | | | - 7 | 7 | seven | 0 | | | 0 - 8 | 8 | eight | 0 | | 1 | -1 - 8 | 8 | eight | 0 | | 2 | 2 - 8 | 8 | eight | 0 | | 3 | -3 - 8 | 8 | eight | 0 | | 2 | 4 - 8 | 8 | eight | 0 | | 5 | -5 - 8 | 8 | eight | 0 | | 5 | -5 - 8 | 8 | eight | 0 | | 0 | - 8 | 8 | eight | 0 | | | - 8 | 8 | eight | 0 | | | 0 - 0 | | zero | 0 | | 1 | -1 - 0 | | zero | 0 | | 2 | 2 - 0 | | zero | 0 | | 3 | -3 - 0 | | zero | 0 | | 2 | 4 - 0 | | zero | 0 | | 5 | -5 - 0 | | zero | 0 | | 5 | -5 - 0 | | zero | 0 | | 0 | - 0 | | zero | 0 | | | - 0 | | zero | 0 | | | 0 - | | null | 0 | | 1 | -1 - | | null | 0 | | 2 | 2 - | | null | 0 | | 3 | -3 - | | null | 0 | | 2 | 4 - | | null | 0 | | 5 | -5 - | | null | 0 | | 5 | -5 - | | null | 0 | | 0 | - | | null | 0 | | | - | | null | 0 | | | 0 - | 0 | zero | 0 | | 1 | -1 - | 0 | zero | 0 | | 2 | 2 - | 0 | zero | 0 | | 3 | -3 - | 0 | zero | 0 | | 2 | 4 - | 0 | zero | 0 | | 5 | -5 - | 0 | zero | 0 | | 5 | -5 - | 0 | zero | 0 | | 0 | - | 0 | zero | 0 | | | - | 0 | zero | 0 | | | 0 - 1 | 4 | one | | | 1 | -1 - 1 | 4 | one | | | 2 | 2 - 1 | 4 | one | | | 3 | -3 - 1 | 4 | one | | | 2 | 4 - 1 | 4 | one | | | 5 | -5 - 1 | 4 | one | | | 5 | -5 - 1 | 4 | one | | | 0 | - 1 | 4 | one | | | | - 1 | 4 | one | | | | 0 - 2 | 3 | two | | | 1 | -1 - 2 | 3 | two | | | 2 | 2 - 2 | 3 | two | | | 3 | -3 - 2 | 3 | two | | | 2 | 4 - 2 | 3 | two | | | 5 | -5 - 2 | 3 | two | | | 5 | -5 - 2 | 3 | two | | | 0 | - 2 | 3 | two | | | | - 2 | 3 | two | | | | 0 - 3 | 2 | three | | | 1 | -1 - 3 | 2 | three | | | 2 | 2 - 3 | 2 | three | | | 3 | -3 - 3 | 2 | three | | | 2 | 4 - 3 | 2 | three | | | 5 | -5 - 3 | 2 | three | | | 5 | -5 - 3 | 2 | three | | | 0 | - 3 | 2 | three | | | | - 3 | 2 | three | | | | 0 - 4 | 1 | four | | | 1 | -1 - 4 | 1 | four | | | 2 | 2 - 4 | 1 | four | | | 3 | -3 - 4 | 1 | four | | | 2 | 4 - 4 | 1 | four | | | 5 | -5 - 4 | 1 | four | | | 5 | -5 - 4 | 1 | four | | | 0 | - 4 | 1 | four | | | | - 4 | 1 | four | | | | 0 - 5 | 0 | five | | | 1 | -1 - 5 | 0 | five | | | 2 | 2 - 5 | 0 | five | | | 3 | -3 - 5 | 0 | five | | | 2 | 4 - 5 | 0 | five | | | 5 | -5 - 5 | 0 | five | | | 5 | -5 - 5 | 0 | five | | | 0 | - 5 | 0 | five | | | | - 5 | 0 | five | | | | 0 - 6 | 6 | six | | | 1 | -1 - 6 | 6 | six | | | 2 | 2 - 6 | 6 | six | | | 3 | -3 - 6 | 6 | six | | | 2 | 4 - 6 | 6 | six | | | 5 | -5 - 6 | 6 | six | | | 5 | -5 - 6 | 6 | six | | | 0 | - 6 | 6 | six | | | | - 6 | 6 | six | | | | 0 - 7 | 7 | seven | | | 1 | -1 - 7 | 7 | seven | | | 2 | 2 - 7 | 7 | seven | | | 3 | -3 - 7 | 7 | seven | | | 2 | 4 - 7 | 7 | seven | | | 5 | -5 - 7 | 7 | seven | | | 5 | -5 - 7 | 7 | seven | | | 0 | - 7 | 7 | seven | | | | - 7 | 7 | seven | | | | 0 - 8 | 8 | eight | | | 1 | -1 - 8 | 8 | eight | | | 2 | 2 - 8 | 8 | eight | | | 3 | -3 - 8 | 8 | eight | | | 2 | 4 - 8 | 8 | eight | | | 5 | -5 - 8 | 8 | eight | | | 5 | -5 - 8 | 8 | eight | | | 0 | - 8 | 8 | eight | | | | - 8 | 8 | eight | | | | 0 - 0 | | zero | | | 1 | -1 - 0 | | zero | | | 2 | 2 - 0 | | zero | | | 3 | -3 - 0 | | zero | | | 2 | 4 - 0 | | zero | | | 5 | -5 - 0 | | zero | | | 5 | -5 - 0 | | zero | | | 0 | - 0 | | zero | | | | - 0 | | zero | | | | 0 - | | null | | | 1 | -1 - | | null | | | 2 | 2 - | | null | | | 3 | -3 - | | null | | | 2 | 4 - | | null | | | 5 | -5 - | | null | | | 5 | -5 - | | null | | | 0 | - | | null | | | | - | | null | | | | 0 - | 0 | zero | | | 1 | -1 - | 0 | zero | | | 2 | 2 - | 0 | zero | | | 3 | -3 - | 0 | zero | | | 2 | 4 - | 0 | zero | | | 5 | -5 - | 0 | zero | | | 5 | -5 - | 0 | zero | | | 0 | - | 0 | zero | | | | - | 0 | zero | | | | 0 - 1 | 4 | one | | 0 | 1 | -1 - 1 | 4 | one | | 0 | 2 | 2 - 1 | 4 | one | | 0 | 3 | -3 - 1 | 4 | one | | 0 | 2 | 4 - 1 | 4 | one | | 0 | 5 | -5 - 1 | 4 | one | | 0 | 5 | -5 - 1 | 4 | one | | 0 | 0 | - 1 | 4 | one | | 0 | | - 1 | 4 | one | | 0 | | 0 - 2 | 3 | two | | 0 | 1 | -1 - 2 | 3 | two | | 0 | 2 | 2 - 2 | 3 | two | | 0 | 3 | -3 - 2 | 3 | two | | 0 | 2 | 4 - 2 | 3 | two | | 0 | 5 | -5 - 2 | 3 | two | | 0 | 5 | -5 - 2 | 3 | two | | 0 | 0 | - 2 | 3 | two | | 0 | | - 2 | 3 | two | | 0 | | 0 - 3 | 2 | three | | 0 | 1 | -1 - 3 | 2 | three | | 0 | 2 | 2 - 3 | 2 | three | | 0 | 3 | -3 - 3 | 2 | three | | 0 | 2 | 4 - 3 | 2 | three | | 0 | 5 | -5 - 3 | 2 | three | | 0 | 5 | -5 - 3 | 2 | three | | 0 | 0 | - 3 | 2 | three | | 0 | | - 3 | 2 | three | | 0 | | 0 - 4 | 1 | four | | 0 | 1 | -1 - 4 | 1 | four | | 0 | 2 | 2 - 4 | 1 | four | | 0 | 3 | -3 - 4 | 1 | four | | 0 | 2 | 4 - 4 | 1 | four | | 0 | 5 | -5 - 4 | 1 | four | | 0 | 5 | -5 - 4 | 1 | four | | 0 | 0 | - 4 | 1 | four | | 0 | | - 4 | 1 | four | | 0 | | 0 - 5 | 0 | five | | 0 | 1 | -1 - 5 | 0 | five | | 0 | 2 | 2 - 5 | 0 | five | | 0 | 3 | -3 - 5 | 0 | five | | 0 | 2 | 4 - 5 | 0 | five | | 0 | 5 | -5 - 5 | 0 | five | | 0 | 5 | -5 - 5 | 0 | five | | 0 | 0 | - 5 | 0 | five | | 0 | | - 5 | 0 | five | | 0 | | 0 - 6 | 6 | six | | 0 | 1 | -1 - 6 | 6 | six | | 0 | 2 | 2 - 6 | 6 | six | | 0 | 3 | -3 - 6 | 6 | six | | 0 | 2 | 4 - 6 | 6 | six | | 0 | 5 | -5 - 6 | 6 | six | | 0 | 5 | -5 - 6 | 6 | six | | 0 | 0 | - 6 | 6 | six | | 0 | | - 6 | 6 | six | | 0 | | 0 - 7 | 7 | seven | | 0 | 1 | -1 - 7 | 7 | seven | | 0 | 2 | 2 - 7 | 7 | seven | | 0 | 3 | -3 - 7 | 7 | seven | | 0 | 2 | 4 - 7 | 7 | seven | | 0 | 5 | -5 - 7 | 7 | seven | | 0 | 5 | -5 - 7 | 7 | seven | | 0 | 0 | - 7 | 7 | seven | | 0 | | - 7 | 7 | seven | | 0 | | 0 - 8 | 8 | eight | | 0 | 1 | -1 - 8 | 8 | eight | | 0 | 2 | 2 - 8 | 8 | eight | | 0 | 3 | -3 - 8 | 8 | eight | | 0 | 2 | 4 - 8 | 8 | eight | | 0 | 5 | -5 - 8 | 8 | eight | | 0 | 5 | -5 - 8 | 8 | eight | | 0 | 0 | - 8 | 8 | eight | | 0 | | - 8 | 8 | eight | | 0 | | 0 - 0 | | zero | | 0 | 1 | -1 - 0 | | zero | | 0 | 2 | 2 - 0 | | zero | | 0 | 3 | -3 - 0 | | zero | | 0 | 2 | 4 - 0 | | zero | | 0 | 5 | -5 - 0 | | zero | | 0 | 5 | -5 - 0 | | zero | | 0 | 0 | - 0 | | zero | | 0 | | - 0 | | zero | | 0 | | 0 - | | null | | 0 | 1 | -1 - | | null | | 0 | 2 | 2 - | | null | | 0 | 3 | -3 - | | null | | 0 | 2 | 4 - | | null | | 0 | 5 | -5 - | | null | | 0 | 5 | -5 - | | null | | 0 | 0 | - | | null | | 0 | | - | | null | | 0 | | 0 - | 0 | zero | | 0 | 1 | -1 - | 0 | zero | | 0 | 2 | 2 - | 0 | zero | | 0 | 3 | -3 - | 0 | zero | | 0 | 2 | 4 - | 0 | zero | | 0 | 5 | -5 - | 0 | zero | | 0 | 5 | -5 - | 0 | zero | | 0 | 0 | - | 0 | zero | | 0 | | - | 0 | zero | | 0 | | 0 -(891 rows) - --- --- --- Inner joins (equi-joins) --- --- --- --- Inner joins (equi-joins) with USING clause --- The USING syntax changes the shape of the resulting table --- by including a column in the USING clause only once in the result. --- --- Inner equi-join on specified column -SELECT * - FROM J1_TBL INNER JOIN J2_TBL USING (i); - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - --- Same as above, slightly different syntax -SELECT * - FROM J1_TBL JOIN J2_TBL USING (i); - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, d) USING (a) - ORDER BY a, d; - a | b | c | d ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b) - ORDER BY b, t1.a; - b | a | c | a ----+---+-------+--- - 0 | 5 | five | - 0 | | zero | - 2 | 3 | three | 2 - 4 | 1 | one | 2 -(4 rows) - --- test join using aliases -SELECT * FROM J1_TBL JOIN J2_TBL USING (i) WHERE J1_TBL.t = 'one'; -- ok - i | j | t | k ----+---+-----+---- - 1 | 4 | one | -1 -(1 row) - -SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; -- ok - i | j | t | k ----+---+-----+---- - 1 | 4 | one | -1 -(1 row) - -SELECT * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t = 'one'; -- error -ERROR: invalid reference to FROM-clause entry for table "j1_tbl" -LINE 1: ... * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t =... - ^ -HINT: There is an entry for table "j1_tbl", but it cannot be referenced from this part of the query. -SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.i = 1; -- ok - i | j | t | k ----+---+-----+---- - 1 | 4 | one | -1 -(1 row) - -SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one'; -- error -ERROR: column x.t does not exist -LINE 1: ...CT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one... - ^ -SELECT * FROM (J1_TBL JOIN J2_TBL USING (i) AS x) AS xx WHERE x.i = 1; -- error (XXX could use better hint) -ERROR: missing FROM-clause entry for table "x" -LINE 1: ...ROM (J1_TBL JOIN J2_TBL USING (i) AS x) AS xx WHERE x.i = 1; - ^ -SELECT * FROM J1_TBL a1 JOIN J2_TBL a2 USING (i) AS a1; -- error -ERROR: table name "a1" specified more than once -SELECT x.* FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; - i ---- - 1 -(1 row) - -SELECT ROW(x.*) FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; - row ------ - (1) -(1 row) - -SELECT row_to_json(x.*) FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; - row_to_json -------------- - {"i":1} -(1 row) - --- --- NATURAL JOIN --- Inner equi-join on all columns with the same name --- -SELECT * - FROM J1_TBL NATURAL JOIN J2_TBL; - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (a, d); - a | b | c | d ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - -SELECT * - FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a); - a | b | c | d ----+---+------+--- - 0 | | zero | - 2 | 3 | two | 2 - 4 | 1 | four | 2 -(3 rows) - --- mismatch number of columns --- currently, Postgres will fill in with underlying names -SELECT * - FROM J1_TBL t1 (a, b) NATURAL JOIN J2_TBL t2 (a); - a | b | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 -(7 rows) - --- --- Inner joins (equi-joins) --- -SELECT * - FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.i); - i | j | t | i | k ----+---+-------+---+---- - 0 | | zero | 0 | - 1 | 4 | one | 1 | -1 - 2 | 3 | two | 2 | 2 - 2 | 3 | two | 2 | 4 - 3 | 2 | three | 3 | -3 - 5 | 0 | five | 5 | -5 - 5 | 0 | five | 5 | -5 -(7 rows) - -SELECT * - FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k); - i | j | t | i | k ----+---+------+---+--- - 0 | | zero | | 0 - 2 | 3 | two | 2 | 2 - 4 | 1 | four | 2 | 4 -(3 rows) - --- --- Non-equi-joins --- -SELECT * - FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i <= J2_TBL.k); - i | j | t | i | k ----+---+-------+---+--- - 1 | 4 | one | 2 | 2 - 2 | 3 | two | 2 | 2 - 0 | | zero | 2 | 2 - 1 | 4 | one | 2 | 4 - 2 | 3 | two | 2 | 4 - 3 | 2 | three | 2 | 4 - 4 | 1 | four | 2 | 4 - 0 | | zero | 2 | 4 - 0 | | zero | | 0 -(9 rows) - --- --- Outer joins --- Note that OUTER is a noise word --- -SELECT * - FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i) - ORDER BY i, k, t; - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 4 | 1 | four | - 5 | 0 | five | -5 - 5 | 0 | five | -5 - 6 | 6 | six | - 7 | 7 | seven | - 8 | 8 | eight | - | | null | - | 0 | zero | -(13 rows) - -SELECT * - FROM J1_TBL LEFT JOIN J2_TBL USING (i) - ORDER BY i, k, t; - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 4 | 1 | four | - 5 | 0 | five | -5 - 5 | 0 | five | -5 - 6 | 6 | six | - 7 | 7 | seven | - 8 | 8 | eight | - | | null | - | 0 | zero | -(13 rows) - -SELECT * - FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i); - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 - | | | - | | | 0 -(9 rows) - -SELECT * - FROM J1_TBL RIGHT JOIN J2_TBL USING (i); - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 5 | 0 | five | -5 - 5 | 0 | five | -5 - | | | - | | | 0 -(9 rows) - -SELECT * - FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i) - ORDER BY i, k, t; - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 4 | 1 | four | - 5 | 0 | five | -5 - 5 | 0 | five | -5 - 6 | 6 | six | - 7 | 7 | seven | - 8 | 8 | eight | - | | | 0 - | | null | - | 0 | zero | - | | | -(15 rows) - -SELECT * - FROM J1_TBL FULL JOIN J2_TBL USING (i) - ORDER BY i, k, t; - i | j | t | k ----+---+-------+---- - 0 | | zero | - 1 | 4 | one | -1 - 2 | 3 | two | 2 - 2 | 3 | two | 4 - 3 | 2 | three | -3 - 4 | 1 | four | - 5 | 0 | five | -5 - 5 | 0 | five | -5 - 6 | 6 | six | - 7 | 7 | seven | - 8 | 8 | eight | - | | | 0 - | | null | - | 0 | zero | - | | | -(15 rows) - -SELECT * - FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (k = 1); - i | j | t | k ----+---+---+--- -(0 rows) - -SELECT * - FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (i = 1); - i | j | t | k ----+---+-----+---- - 1 | 4 | one | -1 -(1 row) - --- --- semijoin selectivity for <> --- -explain (costs off) -select * from int4_tbl i4, tenk1 a -where exists(select * from tenk1 b - where a.twothousand = b.twothousand and a.fivethous <> b.fivethous) - and i4.f1 = a.tenthous; - QUERY PLAN ----------------------------------------------- - Hash Semi Join - Hash Cond: (a.twothousand = b.twothousand) - Join Filter: (a.fivethous <> b.fivethous) - -> Hash Join - Hash Cond: (a.tenthous = i4.f1) - -> Seq Scan on tenk1 a - -> Hash - -> Seq Scan on int4_tbl i4 - -> Hash - -> Seq Scan on tenk1 b -(10 rows) - --- --- More complicated constructs --- --- --- Multiway full join --- -CREATE TABLE t1 (name TEXT, n INTEGER); -CREATE TABLE t2 (name TEXT, n INTEGER); -CREATE TABLE t3 (name TEXT, n INTEGER); -INSERT INTO t1 VALUES ( 'bb', 11 ); -INSERT INTO t2 VALUES ( 'bb', 12 ); -INSERT INTO t2 VALUES ( 'cc', 22 ); -INSERT INTO t2 VALUES ( 'ee', 42 ); -INSERT INTO t3 VALUES ( 'bb', 13 ); -INSERT INTO t3 VALUES ( 'cc', 23 ); -INSERT INTO t3 VALUES ( 'dd', 33 ); -SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name); - name | n | n | n -------+----+----+---- - bb | 11 | 12 | 13 - cc | | 22 | 23 - dd | | | 33 - ee | | 42 | -(4 rows) - --- --- Test interactions of join syntax and subqueries --- --- Basic cases (we expect planner to pull up the subquery here) -SELECT * FROM -(SELECT * FROM t2) as s2 -INNER JOIN -(SELECT * FROM t3) s3 -USING (name); - name | n | n -------+----+---- - bb | 12 | 13 - cc | 22 | 23 -(2 rows) - -SELECT * FROM -(SELECT * FROM t2) as s2 -LEFT JOIN -(SELECT * FROM t3) s3 -USING (name); - name | n | n -------+----+---- - bb | 12 | 13 - cc | 22 | 23 - ee | 42 | -(3 rows) - -SELECT * FROM -(SELECT * FROM t2) as s2 -FULL JOIN -(SELECT * FROM t3) s3 -USING (name); - name | n | n -------+----+---- - bb | 12 | 13 - cc | 22 | 23 - dd | | 33 - ee | 42 | -(4 rows) - --- Cases with non-nullable expressions in subquery results; --- make sure these go to null as expected -SELECT * FROM -(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 -NATURAL INNER JOIN -(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; - name | s2_n | s2_2 | s3_n | s3_2 -------+------+------+------+------ - bb | 12 | 2 | 13 | 3 - cc | 22 | 2 | 23 | 3 -(2 rows) - -SELECT * FROM -(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 -NATURAL LEFT JOIN -(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; - name | s2_n | s2_2 | s3_n | s3_2 -------+------+------+------+------ - bb | 12 | 2 | 13 | 3 - cc | 22 | 2 | 23 | 3 - ee | 42 | 2 | | -(3 rows) - -SELECT * FROM -(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 -NATURAL FULL JOIN -(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; - name | s2_n | s2_2 | s3_n | s3_2 -------+------+------+------+------ - bb | 12 | 2 | 13 | 3 - cc | 22 | 2 | 23 | 3 - dd | | | 33 | 3 - ee | 42 | 2 | | -(4 rows) - -SELECT * FROM -(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1 -NATURAL INNER JOIN -(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 -NATURAL INNER JOIN -(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; - name | s1_n | s1_1 | s2_n | s2_2 | s3_n | s3_2 -------+------+------+------+------+------+------ - bb | 11 | 1 | 12 | 2 | 13 | 3 -(1 row) - -SELECT * FROM -(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1 -NATURAL FULL JOIN -(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 -NATURAL FULL JOIN -(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; - name | s1_n | s1_1 | s2_n | s2_2 | s3_n | s3_2 -------+------+------+------+------+------+------ - bb | 11 | 1 | 12 | 2 | 13 | 3 - cc | | | 22 | 2 | 23 | 3 - dd | | | | | 33 | 3 - ee | | | 42 | 2 | | -(4 rows) - -SELECT * FROM -(SELECT name, n as s1_n FROM t1) as s1 -NATURAL FULL JOIN - (SELECT * FROM - (SELECT name, n as s2_n FROM t2) as s2 - NATURAL FULL JOIN - (SELECT name, n as s3_n FROM t3) as s3 - ) ss2; - name | s1_n | s2_n | s3_n -------+------+------+------ - bb | 11 | 12 | 13 - cc | | 22 | 23 - dd | | | 33 - ee | | 42 | -(4 rows) - -SELECT * FROM -(SELECT name, n as s1_n FROM t1) as s1 -NATURAL FULL JOIN - (SELECT * FROM - (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 - NATURAL FULL JOIN - (SELECT name, n as s3_n FROM t3) as s3 - ) ss2; - name | s1_n | s2_n | s2_2 | s3_n -------+------+------+------+------ - bb | 11 | 12 | 2 | 13 - cc | | 22 | 2 | 23 - dd | | | | 33 - ee | | 42 | 2 | -(4 rows) - --- Constants as join keys can also be problematic -SELECT * FROM - (SELECT name, n as s1_n FROM t1) as s1 -FULL JOIN - (SELECT name, 2 as s2_n FROM t2) as s2 -ON (s1_n = s2_n); - name | s1_n | name | s2_n -------+------+------+------ - | | bb | 2 - | | cc | 2 - | | ee | 2 - bb | 11 | | -(4 rows) - --- Test for propagation of nullability constraints into sub-joins -create temp table x (x1 int, x2 int); -insert into x values (1,11); -insert into x values (2,22); -insert into x values (3,null); -insert into x values (4,44); -insert into x values (5,null); -create temp table y (y1 int, y2 int); -insert into y values (1,111); -insert into y values (2,222); -insert into y values (3,333); -insert into y values (4,null); -select * from x; - x1 | x2 -----+---- - 1 | 11 - 2 | 22 - 3 | - 4 | 44 - 5 | -(5 rows) - -select * from y; - y1 | y2 -----+----- - 1 | 111 - 2 | 222 - 3 | 333 - 4 | -(4 rows) - -select * from x left join y on (x1 = y1 and x2 is not null); - x1 | x2 | y1 | y2 -----+----+----+----- - 1 | 11 | 1 | 111 - 2 | 22 | 2 | 222 - 3 | | | - 4 | 44 | 4 | - 5 | | | -(5 rows) - -select * from x left join y on (x1 = y1 and y2 is not null); - x1 | x2 | y1 | y2 -----+----+----+----- - 1 | 11 | 1 | 111 - 2 | 22 | 2 | 222 - 3 | | 3 | 333 - 4 | 44 | | - 5 | | | -(5 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 3 | | 3 | 333 | 3 | - 4 | 44 | 4 | | 4 | 44 - 5 | | | | 5 | -(5 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1 and x2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 3 | | 3 | 333 | | - 4 | 44 | 4 | | 4 | 44 - 5 | | | | | -(5 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1 and y2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 3 | | 3 | 333 | 3 | - 4 | 44 | 4 | | | - 5 | | | | | -(5 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1 and xx2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 3 | | 3 | 333 | | - 4 | 44 | 4 | | 4 | 44 - 5 | | | | | -(5 rows) - --- these should NOT give the same answers as above -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1) where (x2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 4 | 44 | 4 | | 4 | 44 -(3 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1) where (y2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 3 | | 3 | 333 | 3 | -(3 rows) - -select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) -on (x1 = xx1) where (xx2 is not null); - x1 | x2 | y1 | y2 | xx1 | xx2 -----+----+----+-----+-----+----- - 1 | 11 | 1 | 111 | 1 | 11 - 2 | 22 | 2 | 222 | 2 | 22 - 4 | 44 | 4 | | 4 | 44 -(3 rows) - --- --- regression test: check for bug with propagation of implied equality --- to outside an IN --- -select count(*) from tenk1 a where unique1 in - (select unique1 from tenk1 b join tenk1 c using (unique1) - where b.unique2 = 42); - count -------- - 1 -(1 row) - --- --- regression test: check for failure to generate a plan with multiple --- degenerate IN clauses --- -select count(*) from tenk1 x where - x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and - x.unique1 = 0 and - x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1); - count -------- - 1 -(1 row) - --- try that with GEQO too -begin; -set geqo = on; -set geqo_threshold = 2; -select count(*) from tenk1 x where - x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and - x.unique1 = 0 and - x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1); - count -------- - 1 -(1 row) - -rollback; --- --- regression test: be sure we cope with proven-dummy append rels --- -explain (costs off) -select aa, bb, unique1, unique1 - from tenk1 right join b on aa = unique1 - where bb < bb and bb is null; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -select aa, bb, unique1, unique1 - from tenk1 right join b on aa = unique1 - where bb < bb and bb is null; - aa | bb | unique1 | unique1 -----+----+---------+--------- -(0 rows) - --- --- regression test: check handling of empty-FROM subquery underneath outer join --- -explain (costs off) -select * from int8_tbl i1 left join (int8_tbl i2 join - (select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2 -order by 1, 2; - QUERY PLAN -------------------------------------------- - Sort - Sort Key: i1.q1, i1.q2 - -> Hash Left Join - Hash Cond: (i1.q2 = i2.q2) - -> Seq Scan on int8_tbl i1 - -> Hash - -> Seq Scan on int8_tbl i2 - Filter: (q1 = 123) -(8 rows) - -select * from int8_tbl i1 left join (int8_tbl i2 join - (select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2 -order by 1, 2; - q1 | q2 | q1 | q2 | x -------------------+-------------------+-----+------------------+----- - 123 | 456 | 123 | 456 | 123 - 123 | 4567890123456789 | 123 | 4567890123456789 | 123 - 4567890123456789 | -4567890123456789 | | | - 4567890123456789 | 123 | | | - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 123 -(5 rows) - --- --- regression test: check a case where join_clause_is_movable_into() gives --- an imprecise result, causing an assertion failure --- -select count(*) -from - (select t3.tenthous as x1, coalesce(t1.stringu1, t2.stringu1) as x2 - from tenk1 t1 - left join tenk1 t2 on t1.unique1 = t2.unique1 - join tenk1 t3 on t1.unique2 = t3.unique2) ss, - tenk1 t4, - tenk1 t5 -where t4.thousand = t5.unique1 and ss.x1 = t4.tenthous and ss.x2 = t5.stringu1; - count -------- - 1000 -(1 row) - --- --- regression test: check a case where we formerly missed including an EC --- enforcement clause because it was expected to be handled at scan level --- -explain (costs off) -select a.f1, b.f1, t.thousand, t.tenthous from - tenk1 t, - (select sum(f1)+1 as f1 from int4_tbl i4a) a, - (select sum(f1) as f1 from int4_tbl i4b) b -where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ - Nested Loop - -> Aggregate - -> Seq Scan on int4_tbl i4b - -> Nested Loop - Join Filter: ((sum(i4b.f1)) = ((sum(i4a.f1) + 1))) - -> Aggregate - -> Seq Scan on int4_tbl i4a - -> Index Only Scan using tenk1_thous_tenthous on tenk1 t - Index Cond: ((thousand = (sum(i4b.f1))) AND (tenthous = ((((sum(i4a.f1) + 1)) + (sum(i4b.f1))) + 999))) -(9 rows) - -select a.f1, b.f1, t.thousand, t.tenthous from - tenk1 t, - (select sum(f1)+1 as f1 from int4_tbl i4a) a, - (select sum(f1) as f1 from int4_tbl i4b) b -where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous; - f1 | f1 | thousand | tenthous -----+----+----------+---------- -(0 rows) - --- --- check a case where we formerly got confused by conflicting sort orders --- in redundant merge join path keys --- -explain (costs off) -select * from - j1_tbl full join - (select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl - on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k; - QUERY PLAN ------------------------------------------------------------------ - Merge Full Join - Merge Cond: ((j2_tbl.i = j1_tbl.i) AND (j2_tbl.k = j1_tbl.i)) - -> Sort - Sort Key: j2_tbl.i DESC, j2_tbl.k - -> Seq Scan on j2_tbl - -> Sort - Sort Key: j1_tbl.i DESC - -> Seq Scan on j1_tbl -(8 rows) - -select * from - j1_tbl full join - (select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl - on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k; - i | j | t | i | k ----+---+-------+---+---- - | | | | 0 - | | | | - | 0 | zero | | - | | null | | - 8 | 8 | eight | | - 7 | 7 | seven | | - 6 | 6 | six | | - | | | 5 | -5 - | | | 5 | -5 - 5 | 0 | five | | - 4 | 1 | four | | - | | | 3 | -3 - 3 | 2 | three | | - 2 | 3 | two | 2 | 2 - | | | 2 | 4 - | | | 1 | -1 - | | | 0 | - 1 | 4 | one | | - 0 | | zero | | -(19 rows) - --- --- a different check for handling of redundant sort keys in merge joins --- -explain (costs off) -select count(*) from - (select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x - left join - (select * from tenk1 y order by y.unique2) y - on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2; - QUERY PLAN ----------------------------------------------------------------------------------- - Aggregate - -> Merge Left Join - Merge Cond: (x.thousand = y.unique2) - Join Filter: ((x.twothousand = y.hundred) AND (x.fivethous = y.unique2)) - -> Sort - Sort Key: x.thousand, x.twothousand, x.fivethous - -> Seq Scan on tenk1 x - -> Materialize - -> Index Scan using tenk1_unique2 on tenk1 y -(9 rows) - -select count(*) from - (select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x - left join - (select * from tenk1 y order by y.unique2) y - on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2; - count -------- - 10000 -(1 row) - --- --- Clean up --- -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE t3; -DROP TABLE J1_TBL; -DROP TABLE J2_TBL; --- Both DELETE and UPDATE allow the specification of additional tables --- to "join" against to determine which rows should be modified. -CREATE TEMP TABLE t1 (a int, b int); -CREATE TEMP TABLE t2 (a int, b int); -CREATE TEMP TABLE t3 (x int, y int); -INSERT INTO t1 VALUES (5, 10); -INSERT INTO t1 VALUES (15, 20); -INSERT INTO t1 VALUES (100, 100); -INSERT INTO t1 VALUES (200, 1000); -INSERT INTO t2 VALUES (200, 2000); -INSERT INTO t3 VALUES (5, 20); -INSERT INTO t3 VALUES (6, 7); -INSERT INTO t3 VALUES (7, 8); -INSERT INTO t3 VALUES (500, 100); -DELETE FROM t3 USING t1 table1 WHERE t3.x = table1.a; -SELECT * FROM t3; - x | y ------+----- - 6 | 7 - 7 | 8 - 500 | 100 -(3 rows) - -DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a; -SELECT * FROM t3; - x | y ----+--- - 6 | 7 - 7 | 8 -(2 rows) - -DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y; -SELECT * FROM t3; - x | y ----+--- -(0 rows) - --- Test join against inheritance tree -create temp table t2a () inherits (t2); -insert into t2a values (200, 2001); -select * from t1 left join t2 on (t1.a = t2.a); - a | b | a | b ------+------+-----+------ - 5 | 10 | | - 15 | 20 | | - 100 | 100 | | - 200 | 1000 | 200 | 2000 - 200 | 1000 | 200 | 2001 -(5 rows) - --- Test matching of column name with wrong alias -select t1.x from t1 join t3 on (t1.a = t3.x); -ERROR: column t1.x does not exist -LINE 1: select t1.x from t1 join t3 on (t1.a = t3.x); - ^ -HINT: Perhaps you meant to reference the column "t3.x". --- --- regression test for 8.1 merge right join bug --- -CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); -INSERT INTO tt1 VALUES (1, 11); -INSERT INTO tt1 VALUES (2, NULL); -CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); -INSERT INTO tt2 VALUES (21, 11); -INSERT INTO tt2 VALUES (22, 11); -set enable_hashjoin to off; -set enable_nestloop to off; --- these should give the same results -select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; - tt1_id | joincol | tt2_id | joincol ---------+---------+--------+--------- - 1 | 11 | 21 | 11 - 1 | 11 | 22 | 11 - 2 | | | -(3 rows) - -select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; - tt1_id | joincol | tt2_id | joincol ---------+---------+--------+--------- - 1 | 11 | 21 | 11 - 1 | 11 | 22 | 11 - 2 | | | -(3 rows) - -reset enable_hashjoin; -reset enable_nestloop; --- --- regression test for bug #13908 (hash join with skew tuples & nbatch increase) --- -set work_mem to '64kB'; -set enable_mergejoin to off; -set enable_memoize to off; -explain (costs off) -select count(*) from tenk1 a, tenk1 b - where a.hundred = b.thousand and (b.fivethous % 10) < 10; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (a.hundred = b.thousand) - -> Index Only Scan using tenk1_hundred on tenk1 a - -> Hash - -> Seq Scan on tenk1 b - Filter: ((fivethous % 10) < 10) -(7 rows) - -select count(*) from tenk1 a, tenk1 b - where a.hundred = b.thousand and (b.fivethous % 10) < 10; - count --------- - 100000 -(1 row) - -reset work_mem; -reset enable_mergejoin; -reset enable_memoize; --- --- regression test for 8.2 bug with improper re-ordering of left joins --- -create temp table tt3(f1 int, f2 text); -insert into tt3 select x, repeat('xyzzy', 100) from generate_series(1,10000) x; -create index tt3i on tt3(f1); -analyze tt3; -create temp table tt4(f1 int); -insert into tt4 values (0),(1),(9999); -analyze tt4; -SELECT a.f1 -FROM tt4 a -LEFT JOIN ( - SELECT b.f1 - FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1) - WHERE c.f1 IS NULL -) AS d ON (a.f1 = d.f1) -WHERE d.f1 IS NULL; - f1 ------- - 0 - 1 - 9999 -(3 rows) - --- --- regression test for proper handling of outer joins within antijoins --- -create temp table tt4x(c1 int, c2 int, c3 int); -explain (costs off) -select * from tt4x t1 -where not exists ( - select 1 from tt4x t2 - left join tt4x t3 on t2.c3 = t3.c1 - left join ( select t5.c1 as c1 - from tt4x t4 left join tt4x t5 on t4.c2 = t5.c1 - ) a1 on t3.c2 = a1.c1 - where t1.c1 = t2.c2 -); - QUERY PLAN ---------------------------------------------------------- - Hash Anti Join - Hash Cond: (t1.c1 = t2.c2) - -> Seq Scan on tt4x t1 - -> Hash - -> Merge Right Join - Merge Cond: (t5.c1 = t3.c2) - -> Merge Join - Merge Cond: (t4.c2 = t5.c1) - -> Sort - Sort Key: t4.c2 - -> Seq Scan on tt4x t4 - -> Sort - Sort Key: t5.c1 - -> Seq Scan on tt4x t5 - -> Sort - Sort Key: t3.c2 - -> Merge Left Join - Merge Cond: (t2.c3 = t3.c1) - -> Sort - Sort Key: t2.c3 - -> Seq Scan on tt4x t2 - -> Sort - Sort Key: t3.c1 - -> Seq Scan on tt4x t3 -(24 rows) - --- --- regression test for problems of the sort depicted in bug #3494 --- -create temp table tt5(f1 int, f2 int); -create temp table tt6(f1 int, f2 int); -insert into tt5 values(1, 10); -insert into tt5 values(1, 11); -insert into tt6 values(1, 9); -insert into tt6 values(1, 2); -insert into tt6 values(2, 9); -select * from tt5,tt6 where tt5.f1 = tt6.f1 and tt5.f1 = tt5.f2 - tt6.f2; - f1 | f2 | f1 | f2 -----+----+----+---- - 1 | 10 | 1 | 9 -(1 row) - --- --- regression test for problems of the sort depicted in bug #3588 --- -create temp table xx (pkxx int); -create temp table yy (pkyy int, pkxx int); -insert into xx values (1); -insert into xx values (2); -insert into xx values (3); -insert into yy values (101, 1); -insert into yy values (201, 2); -insert into yy values (301, NULL); -select yy.pkyy as yy_pkyy, yy.pkxx as yy_pkxx, yya.pkyy as yya_pkyy, - xxa.pkxx as xxa_pkxx, xxb.pkxx as xxb_pkxx -from yy - left join (SELECT * FROM yy where pkyy = 101) as yya ON yy.pkyy = yya.pkyy - left join xx xxa on yya.pkxx = xxa.pkxx - left join xx xxb on coalesce (xxa.pkxx, 1) = xxb.pkxx; - yy_pkyy | yy_pkxx | yya_pkyy | xxa_pkxx | xxb_pkxx ----------+---------+----------+----------+---------- - 101 | 1 | 101 | 1 | 1 - 201 | 2 | | | 1 - 301 | | | | 1 -(3 rows) - --- --- regression test for improper pushing of constants across outer-join clauses --- (as seen in early 8.2.x releases) --- -create temp table zt1 (f1 int primary key); -create temp table zt2 (f2 int primary key); -create temp table zt3 (f3 int primary key); -insert into zt1 values(53); -insert into zt2 values(53); -select * from - zt2 left join zt3 on (f2 = f3) - left join zt1 on (f3 = f1) -where f2 = 53; - f2 | f3 | f1 -----+----+---- - 53 | | -(1 row) - -create temp view zv1 as select *,'dummy'::text AS junk from zt1; -select * from - zt2 left join zt3 on (f2 = f3) - left join zv1 on (f3 = f1) -where f2 = 53; - f2 | f3 | f1 | junk -----+----+----+------ - 53 | | | -(1 row) - --- --- regression test for improper extraction of OR indexqual conditions --- (as seen in early 8.3.x releases) --- -select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred -from tenk1 a left join tenk1 b on a.unique2 = b.tenthous -where a.unique1 = 42 and - ((b.unique2 is null and a.ten = 2) or b.hundred = 3); - unique2 | ten | tenthous | unique2 | hundred ----------+-----+----------+---------+--------- -(0 rows) - --- --- test proper positioning of one-time quals in EXISTS (8.4devel bug) --- -prepare foo(bool) as - select count(*) from tenk1 a left join tenk1 b - on (a.unique2 = b.unique1 and exists - (select 1 from tenk1 c where c.thousand = b.unique2 and $1)); -execute foo(true); - count -------- - 10000 -(1 row) - -execute foo(false); - count -------- - 10000 -(1 row) - --- --- test for sane behavior with noncanonical merge clauses, per bug #4926 --- -begin; -set enable_mergejoin = 1; -set enable_hashjoin = 0; -set enable_nestloop = 0; -create temp table a (i integer); -create temp table b (x integer, y integer); -select * from a left join b on i = x and i = y and x = i; - i | x | y ----+---+--- -(0 rows) - -rollback; --- --- test handling of merge clauses using record_ops --- -begin; -create type mycomptype as (id int, v bigint); -create temp table tidv (idv mycomptype); -create index on tidv (idv); -explain (costs off) -select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; - QUERY PLAN ----------------------------------------------------------- - Merge Join - Merge Cond: (a.idv = b.idv) - -> Index Only Scan using tidv_idv_idx on tidv a - -> Materialize - -> Index Only Scan using tidv_idv_idx on tidv b -(5 rows) - -set enable_mergejoin = 0; -set enable_hashjoin = 0; -explain (costs off) -select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; - QUERY PLAN ----------------------------------------------------- - Nested Loop - -> Seq Scan on tidv a - -> Index Only Scan using tidv_idv_idx on tidv b - Index Cond: (idv = a.idv) -(4 rows) - -rollback; --- --- test NULL behavior of whole-row Vars, per bug #5025 --- -select t1.q2, count(t2.*) -from int8_tbl t1 left join int8_tbl t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join (select * from int8_tbl) t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join (select * from int8_tbl offset 0) t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join - (select q1, case when q2=1 then 1 else q2 end as q2 from int8_tbl) t2 - on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - --- --- test incorrect failure to NULL pulled-up subexpressions --- -begin; -create temp table a ( - code char not null, - constraint a_pk primary key (code) -); -create temp table b ( - a char not null, - num integer not null, - constraint b_pk primary key (a, num) -); -create temp table c ( - name char not null, - a char, - constraint c_pk primary key (name) -); -insert into a (code) values ('p'); -insert into a (code) values ('q'); -insert into b (a, num) values ('p', 1); -insert into b (a, num) values ('p', 2); -insert into c (name, a) values ('A', 'p'); -insert into c (name, a) values ('B', 'q'); -insert into c (name, a) values ('C', null); -select c.name, ss.code, ss.b_cnt, ss.const -from c left join - (select a.code, coalesce(b_grp.cnt, 0) as b_cnt, -1 as const - from a left join - (select count(1) as cnt, b.a from b group by b.a) as b_grp - on a.code = b_grp.a - ) as ss - on (c.a = ss.code) -order by c.name; - name | code | b_cnt | const -------+------+-------+------- - A | p | 2 | -1 - B | q | 0 | -1 - C | | | -(3 rows) - -rollback; --- --- test incorrect handling of placeholders that only appear in targetlists, --- per bug #6154 --- -SELECT * FROM -( SELECT 1 as key1 ) sub1 -LEFT JOIN -( SELECT sub3.key3, sub4.value2, COALESCE(sub4.value2, 66) as value3 FROM - ( SELECT 1 as key3 ) sub3 - LEFT JOIN - ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM - ( SELECT 1 as key5 ) sub5 - LEFT JOIN - ( SELECT 2 as key6, 42 as value1 ) sub6 - ON sub5.key5 = sub6.key6 - ) sub4 - ON sub4.key5 = sub3.key3 -) sub2 -ON sub1.key1 = sub2.key3; - key1 | key3 | value2 | value3 -------+------+--------+-------- - 1 | 1 | 1 | 1 -(1 row) - --- test the path using join aliases, too -SELECT * FROM -( SELECT 1 as key1 ) sub1 -LEFT JOIN -( SELECT sub3.key3, value2, COALESCE(value2, 66) as value3 FROM - ( SELECT 1 as key3 ) sub3 - LEFT JOIN - ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM - ( SELECT 1 as key5 ) sub5 - LEFT JOIN - ( SELECT 2 as key6, 42 as value1 ) sub6 - ON sub5.key5 = sub6.key6 - ) sub4 - ON sub4.key5 = sub3.key3 -) sub2 -ON sub1.key1 = sub2.key3; - key1 | key3 | value2 | value3 -------+------+--------+-------- - 1 | 1 | 1 | 1 -(1 row) - --- --- test case where a PlaceHolderVar is used as a nestloop parameter --- -EXPLAIN (COSTS OFF) -SELECT qq, unique1 - FROM - ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 - FULL OUTER JOIN - ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 - USING (qq) - INNER JOIN tenk1 c ON qq = unique2; - QUERY PLAN ---------------------------------------------------------------------------------------------------------- - Nested Loop - -> Hash Full Join - Hash Cond: ((COALESCE(a.q1, '0'::bigint)) = (COALESCE(b.q2, '-1'::bigint))) - -> Seq Scan on int8_tbl a - -> Hash - -> Seq Scan on int8_tbl b - -> Index Scan using tenk1_unique2 on tenk1 c - Index Cond: (unique2 = COALESCE((COALESCE(a.q1, '0'::bigint)), (COALESCE(b.q2, '-1'::bigint)))) -(8 rows) - -SELECT qq, unique1 - FROM - ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 - FULL OUTER JOIN - ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 - USING (qq) - INNER JOIN tenk1 c ON qq = unique2; - qq | unique1 ------+--------- - 123 | 4596 - 123 | 4596 - 456 | 7318 -(3 rows) - --- --- nested nestloops can require nested PlaceHolderVars --- -create temp table nt1 ( - id int primary key, - a1 boolean, - a2 boolean -); -create temp table nt2 ( - id int primary key, - nt1_id int, - b1 boolean, - b2 boolean, - foreign key (nt1_id) references nt1(id) -); -create temp table nt3 ( - id int primary key, - nt2_id int, - c1 boolean, - foreign key (nt2_id) references nt2(id) -); -insert into nt1 values (1,true,true); -insert into nt1 values (2,true,false); -insert into nt1 values (3,false,false); -insert into nt2 values (1,1,true,true); -insert into nt2 values (2,2,true,false); -insert into nt2 values (3,3,false,false); -insert into nt3 values (1,1,true); -insert into nt3 values (2,2,false); -insert into nt3 values (3,3,true); -explain (costs off) -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 and ss1.a3) AS b3 - from nt2 as nt2 - left join - (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 - on ss1.id = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Nested Loop - -> Index Scan using nt3_pkey on nt3 - Index Cond: (id = 1) - -> Index Scan using nt2_pkey on nt2 - Index Cond: (id = nt3.nt2_id) - -> Index Only Scan using nt1_pkey on nt1 - Index Cond: (id = nt2.nt1_id) - Filter: (nt2.b1 AND (id IS NOT NULL)) -(9 rows) - -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 and ss1.a3) AS b3 - from nt2 as nt2 - left join - (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 - on ss1.id = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - id ----- - 1 -(1 row) - --- --- test case where a PlaceHolderVar is propagated into a subquery --- -explain (costs off) -select * from - int8_tbl t1 left join - (select q1 as x, 42 as y from int8_tbl t2) ss - on t1.q2 = ss.x -where - 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) -order by 1,2; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: t1.q1, t1.q2 - -> Hash Left Join - Hash Cond: (t1.q2 = t2.q1) - Filter: (1 = (SubPlan 1)) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Seq Scan on int8_tbl t2 - SubPlan 1 - -> Limit - -> Result - One-Time Filter: ((42) IS NOT NULL) - -> Seq Scan on int8_tbl t3 -(13 rows) - -select * from - int8_tbl t1 left join - (select q1 as x, 42 as y from int8_tbl t2) ss - on t1.q2 = ss.x -where - 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) -order by 1,2; - q1 | q2 | x | y -------------------+------------------+------------------+---- - 123 | 4567890123456789 | 4567890123456789 | 42 - 123 | 4567890123456789 | 4567890123456789 | 42 - 123 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 123 | 123 | 42 - 4567890123456789 | 123 | 123 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 -(8 rows) - --- --- variant where a PlaceHolderVar is needed at a join, but not above the join --- -explain (costs off) -select * from - int4_tbl as i41, - lateral - (select 1 as x from - (select i41.f1 as lat, - i42.f1 as loc from - int8_tbl as i81, int4_tbl as i42) as ss1 - right join int4_tbl as i43 on (i43.f1 > 1) - where ss1.loc = ss1.lat) as ss2 -where i41.f1 > 0; - QUERY PLAN --------------------------------------------------- - Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl i41 - Filter: (f1 > 0) - -> Nested Loop - Join Filter: (i41.f1 = i42.f1) - -> Seq Scan on int8_tbl i81 - -> Materialize - -> Seq Scan on int4_tbl i42 - -> Materialize - -> Seq Scan on int4_tbl i43 - Filter: (f1 > 1) -(12 rows) - -select * from - int4_tbl as i41, - lateral - (select 1 as x from - (select i41.f1 as lat, - i42.f1 as loc from - int8_tbl as i81, int4_tbl as i42) as ss1 - right join int4_tbl as i43 on (i43.f1 > 1) - where ss1.loc = ss1.lat) as ss2 -where i41.f1 > 0; - f1 | x -------------+--- - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 -(20 rows) - --- --- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE --- -select * from int4_tbl a full join int4_tbl b on true; - f1 | f1 --------------+------------- - 0 | 0 - 0 | 123456 - 0 | -123456 - 0 | 2147483647 - 0 | -2147483647 - 123456 | 0 - 123456 | 123456 - 123456 | -123456 - 123456 | 2147483647 - 123456 | -2147483647 - -123456 | 0 - -123456 | 123456 - -123456 | -123456 - -123456 | 2147483647 - -123456 | -2147483647 - 2147483647 | 0 - 2147483647 | 123456 - 2147483647 | -123456 - 2147483647 | 2147483647 - 2147483647 | -2147483647 - -2147483647 | 0 - -2147483647 | 123456 - -2147483647 | -123456 - -2147483647 | 2147483647 - -2147483647 | -2147483647 -(25 rows) - -select * from int4_tbl a full join int4_tbl b on false; - f1 | f1 --------------+------------- - | 0 - | 123456 - | -123456 - | 2147483647 - | -2147483647 - 0 | - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(10 rows) - --- --- test for ability to use a cartesian join when necessary --- -create temp table q1 as select 1 as q1; -create temp table q2 as select 0 as q2; -analyze q1; -analyze q2; -explain (costs off) -select * from - tenk1 join int4_tbl on f1 = twothousand, - q1, q2 -where q1 = thousand or q2 = thousand; - QUERY PLAN ------------------------------------------------------------------------- - Hash Join - Hash Cond: (tenk1.twothousand = int4_tbl.f1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on q1 - -> Seq Scan on q2 - -> Bitmap Heap Scan on tenk1 - Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = q1.q1) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = q2.q2) - -> Hash - -> Seq Scan on int4_tbl -(15 rows) - -explain (costs off) -select * from - tenk1 join int4_tbl on f1 = twothousand, - q1, q2 -where thousand = (q1 + q2); - QUERY PLAN --------------------------------------------------------------- - Hash Join - Hash Cond: (tenk1.twothousand = int4_tbl.f1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on q1 - -> Seq Scan on q2 - -> Bitmap Heap Scan on tenk1 - Recheck Cond: (thousand = (q1.q1 + q2.q2)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = (q1.q1 + q2.q2)) - -> Hash - -> Seq Scan on int4_tbl -(12 rows) - --- --- test ability to generate a suitable plan for a star-schema query --- -explain (costs off) -select * from - tenk1, int8_tbl a, int8_tbl b -where thousand = a.q1 and tenthous = b.q1 and a.q2 = 1 and b.q2 = 2; - QUERY PLAN ---------------------------------------------------------------------- - Nested Loop - -> Seq Scan on int8_tbl b - Filter: (q2 = 2) - -> Nested Loop - -> Seq Scan on int8_tbl a - Filter: (q2 = 1) - -> Index Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = a.q1) AND (tenthous = b.q1)) -(8 rows) - --- --- test a corner case in which we shouldn't apply the star-schema optimization --- -explain (costs off) -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (select 1,0 from onerow) v1(x1,x2) - left join (select 3,1 from onerow) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - QUERY PLAN ------------------------------------------------------------------------ - Nested Loop - -> Nested Loop - Join Filter: (t1.stringu1 > t2.stringu2) - -> Nested Loop - -> Nested Loop - -> Seq Scan on onerow - -> Seq Scan on onerow onerow_1 - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: ((unique2 = (11)) AND (unique2 < 42)) - -> Index Scan using tenk1_unique1 on tenk1 t2 - Index Cond: (unique1 = (3)) - -> Seq Scan on int4_tbl i1 - Filter: (f1 = 0) -(13 rows) - -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (select 1,0 from onerow) v1(x1,x2) - left join (select 3,1 from onerow) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - unique2 | stringu1 | unique1 | stringu2 ----------+----------+---------+---------- - 11 | WFAAAA | 3 | LKIAAA -(1 row) - --- variant that isn't quite a star-schema case -select ss1.d1 from - tenk1 as t1 - inner join tenk1 as t2 - on t1.tenthous = t2.ten - inner join - int8_tbl as i8 - left join int4_tbl as i4 - inner join (select 64::information_schema.cardinal_number as d1 - from tenk1 t3, - lateral (select abs(t3.unique1) + random()) ss0(x) - where t3.fivethous < 0) as ss1 - on i4.f1 = ss1.d1 - on i8.q1 = i4.f1 - on t1.tenthous = ss1.d1 -where t1.unique1 < i4.f1; - d1 ----- -(0 rows) - --- this variant is foldable by the remove-useless-RESULT-RTEs code -explain (costs off) -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (values(1,0)) v1(x1,x2) - left join (values(3,1)) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - QUERY PLAN ------------------------------------------------------------------ - Nested Loop - Join Filter: (t1.stringu1 > t2.stringu2) - -> Nested Loop - -> Seq Scan on int4_tbl i1 - Filter: (f1 = 0) - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: ((unique2 = (11)) AND (unique2 < 42)) - -> Index Scan using tenk1_unique1 on tenk1 t2 - Index Cond: (unique1 = (3)) -(9 rows) - -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (values(1,0)) v1(x1,x2) - left join (values(3,1)) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - unique2 | stringu1 | unique1 | stringu2 ----------+----------+---------+---------- - 11 | WFAAAA | 3 | LKIAAA -(1 row) - --- Here's a variant that we can't fold too aggressively, though, --- or we end up with noplace to evaluate the lateral PHV -explain (verbose, costs off) -select * from - (select 1 as x) ss1 left join (select 2 as y) ss2 on (true), - lateral (select ss2.y as z limit 1) ss3; - QUERY PLAN ---------------------------- - Nested Loop - Output: 1, (2), ((2)) - -> Result - Output: 2 - -> Limit - Output: ((2)) - -> Result - Output: (2) -(8 rows) - -select * from - (select 1 as x) ss1 left join (select 2 as y) ss2 on (true), - lateral (select ss2.y as z limit 1) ss3; - x | y | z ----+---+--- - 1 | 2 | 2 -(1 row) - --- Test proper handling of appendrel PHVs during useless-RTE removal -explain (costs off) -select * from - (select 0 as z) as t1 - left join - (select true as a) as t2 - on true, - lateral (select true as b - union all - select a as b) as t3 -where b; - QUERY PLAN ---------------------------------------- - Nested Loop - -> Result - -> Append - -> Result - -> Result - One-Time Filter: (true) -(6 rows) - -select * from - (select 0 as z) as t1 - left join - (select true as a) as t2 - on true, - lateral (select true as b - union all - select a as b) as t3 -where b; - z | a | b ----+---+--- - 0 | t | t - 0 | t | t -(2 rows) - --- --- test inlining of immutable functions --- -create function f_immutable_int4(i integer) returns integer as -$$ begin return i; end; $$ language plpgsql immutable; --- check optimization of function scan with join -explain (costs off) -select unique1 from tenk1, (select * from f_immutable_int4(1) x) x -where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (verbose, costs off) -select unique1, x.* -from tenk1, (select *, random() from f_immutable_int4(1) x) x -where x = unique1; - QUERY PLAN ------------------------------------------------------------ - Nested Loop - Output: tenk1.unique1, (1), (random()) - -> Result - Output: 1, random() - -> Index Only Scan using tenk1_unique1 on public.tenk1 - Output: tenk1.unique1 - Index Cond: (tenk1.unique1 = (1)) -(7 rows) - -explain (costs off) -select unique1 from tenk1, f_immutable_int4(1) x where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1 from tenk1, lateral f_immutable_int4(1) x where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1 from tenk1, lateral f_immutable_int4(1) x where x in (select 17); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) -select unique1, x from tenk1 join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1, x from tenk1 left join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - Join Filter: (tenk1.unique1 = 1) - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Materialize - -> Result -(5 rows) - -explain (costs off) -select unique1, x from tenk1 right join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - -> Result - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(4 rows) - -explain (costs off) -select unique1, x from tenk1 full join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Merge Full Join - Merge Cond: (tenk1.unique1 = (1)) - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Sort - Sort Key: (1) - -> Result -(6 rows) - --- check that pullup of a const function allows further const-folding -explain (costs off) -select unique1 from tenk1, f_immutable_int4(1) x where x = 42; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - --- test inlining of immutable functions with PlaceHolderVars -explain (costs off) -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 or i4 = 42) AS b3 - from nt2 as nt2 - left join - f_immutable_int4(0) i4 - on i4 = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Filter: ((nt2.b1 OR ((0) = 42))) - -> Index Scan using nt3_pkey on nt3 - Index Cond: (id = 1) - -> Nested Loop Left Join - Join Filter: (0 = nt2.nt1_id) - -> Index Scan using nt2_pkey on nt2 - Index Cond: (id = nt3.nt2_id) - -> Result -(9 rows) - -drop function f_immutable_int4(int); --- test inlining when function returns composite -create function mki8(bigint, bigint) returns int8_tbl as -$$select row($1,$2)::int8_tbl$$ language sql; -create function mki4(int) returns int4_tbl as -$$select row($1)::int4_tbl$$ language sql; -explain (verbose, costs off) -select * from mki8(1,2); - QUERY PLAN ------------------------------------- - Function Scan on mki8 - Output: q1, q2 - Function Call: '(1,2)'::int8_tbl -(3 rows) - -select * from mki8(1,2); - q1 | q2 -----+---- - 1 | 2 -(1 row) - -explain (verbose, costs off) -select * from mki4(42); - QUERY PLAN ------------------------------------ - Function Scan on mki4 - Output: f1 - Function Call: '(42)'::int4_tbl -(3 rows) - -select * from mki4(42); - f1 ----- - 42 -(1 row) - -drop function mki8(bigint, bigint); -drop function mki4(int); --- --- test extraction of restriction OR clauses from join OR clause --- (we used to only do this for indexable clauses) --- -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.hundred = 4); - QUERY PLAN -------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = 3) AND (b.hundred = 4))) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: ((unique1 = 2) OR (hundred = 4)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 2) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = 4) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR (unique2 = 3)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 3) -(17 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.ten = 4); - QUERY PLAN ---------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = 3) AND (b.ten = 4))) - -> Seq Scan on tenk1 b - Filter: ((unique1 = 2) OR (ten = 4)) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR (unique2 = 3)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 3) -(12 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or - ((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4); - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4))) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: ((unique1 = 2) OR (hundred = 4)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 2) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = 4) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 3) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 7) -(19 rows) - --- --- test placement of movable quals in a parameterized join tree --- -explain (costs off) -select * from tenk1 t1 left join - (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) - on t1.hundred = t2.hundred and t1.ten = t3.ten -where t1.unique1 = 1; - QUERY PLAN --------------------------------------------------------------- - Nested Loop Left Join - -> Index Scan using tenk1_unique1 on tenk1 t1 - Index Cond: (unique1 = 1) - -> Nested Loop - Join Filter: (t1.ten = t3.ten) - -> Bitmap Heap Scan on tenk1 t2 - Recheck Cond: (t1.hundred = hundred) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = t1.hundred) - -> Memoize - Cache Key: t2.thousand - -> Index Scan using tenk1_unique2 on tenk1 t3 - Index Cond: (unique2 = t2.thousand) -(13 rows) - -explain (costs off) -select * from tenk1 t1 left join - (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) - on t1.hundred = t2.hundred and t1.ten + t2.ten = t3.ten -where t1.unique1 = 1; - QUERY PLAN --------------------------------------------------------------- - Nested Loop Left Join - -> Index Scan using tenk1_unique1 on tenk1 t1 - Index Cond: (unique1 = 1) - -> Nested Loop - Join Filter: ((t1.ten + t2.ten) = t3.ten) - -> Bitmap Heap Scan on tenk1 t2 - Recheck Cond: (t1.hundred = hundred) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = t1.hundred) - -> Memoize - Cache Key: t2.thousand - -> Index Scan using tenk1_unique2 on tenk1 t3 - Index Cond: (unique2 = t2.thousand) -(13 rows) - -explain (costs off) -select count(*) from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand - join int4_tbl on b.thousand = f1; - QUERY PLAN -------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: (a.unique2 = b.unique1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: (thousand = int4_tbl.f1) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = int4_tbl.f1) - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.unique2) - -> Index Only Scan using tenk1_thous_tenthous on tenk1 c - Index Cond: (thousand = a.thousand) -(14 rows) - -select count(*) from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand - join int4_tbl on b.thousand = f1; - count -------- - 10 -(1 row) - -explain (costs off) -select b.unique1 from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand - join int4_tbl i1 on b.thousand = f1 - right join int4_tbl i2 on i2.f1 = b.tenthous - order by 1; - QUERY PLAN ------------------------------------------------------------------------------------------ - Sort - Sort Key: b.unique1 - -> Nested Loop Left Join - -> Seq Scan on int4_tbl i2 - -> Nested Loop Left Join - Join Filter: (b.unique1 = 42) - -> Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl i1 - -> Index Scan using tenk1_thous_tenthous on tenk1 b - Index Cond: ((thousand = i1.f1) AND (tenthous = i2.f1)) - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.unique2) - -> Index Only Scan using tenk1_thous_tenthous on tenk1 c - Index Cond: (thousand = a.thousand) -(15 rows) - -select b.unique1 from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand - join int4_tbl i1 on b.thousand = f1 - right join int4_tbl i2 on i2.f1 = b.tenthous - order by 1; - unique1 ---------- - 0 - - - - -(5 rows) - -explain (costs off) -select * from -( - select unique1, q1, coalesce(unique1, -1) + q1 as fault - from int8_tbl left join tenk1 on (q2 = unique2) -) ss -where fault = 122 -order by fault; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop Left Join - Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122) - -> Seq Scan on int8_tbl - -> Index Scan using tenk1_unique2 on tenk1 - Index Cond: (unique2 = int8_tbl.q2) -(5 rows) - -select * from -( - select unique1, q1, coalesce(unique1, -1) + q1 as fault - from int8_tbl left join tenk1 on (q2 = unique2) -) ss -where fault = 122 -order by fault; - unique1 | q1 | fault ----------+-----+------- - | 123 | 122 -(1 row) - -explain (costs off) -select * from -(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x -left join unnest(v1ys) as u1(u1y) on u1y = v2y; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - -> Values Scan on "*VALUES*" - -> Hash Right Join - Hash Cond: (u1.u1y = "*VALUES*_1".column2) - Filter: ("*VALUES*_1".column1 = "*VALUES*".column1) - -> Function Scan on unnest u1 - -> Hash - -> Values Scan on "*VALUES*_1" -(8 rows) - -select * from -(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x -left join unnest(v1ys) as u1(u1y) on u1y = v2y; - v1x | v1ys | v2x | v2y | u1y ------+---------+-----+-----+----- - 1 | {10,20} | 1 | 10 | 10 - 2 | {20,30} | 2 | 20 | 20 -(2 rows) - --- --- test handling of potential equivalence clauses above outer joins --- -explain (costs off) -select q1, unique2, thousand, hundred - from int8_tbl a left join tenk1 b on q1 = unique2 - where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); - QUERY PLAN --------------------------------------------------------------------------------------- - Nested Loop Left Join - Filter: ((COALESCE(b.thousand, 123) = a.q1) AND (a.q1 = COALESCE(b.hundred, 123))) - -> Seq Scan on int8_tbl a - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = a.q1) -(5 rows) - -select q1, unique2, thousand, hundred - from int8_tbl a left join tenk1 b on q1 = unique2 - where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); - q1 | unique2 | thousand | hundred -----+---------+----------+--------- -(0 rows) - -explain (costs off) -select f1, unique2, case when unique2 is null then f1 else 0 end - from int4_tbl a left join tenk1 b on f1 = unique2 - where (case when unique2 is null then f1 else 0 end) = 0; - QUERY PLAN --------------------------------------------------------------------- - Nested Loop Left Join - Filter: (CASE WHEN (b.unique2 IS NULL) THEN a.f1 ELSE 0 END = 0) - -> Seq Scan on int4_tbl a - -> Index Only Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = a.f1) -(5 rows) - -select f1, unique2, case when unique2 is null then f1 else 0 end - from int4_tbl a left join tenk1 b on f1 = unique2 - where (case when unique2 is null then f1 else 0 end) = 0; - f1 | unique2 | case -----+---------+------ - 0 | 0 | 0 -(1 row) - --- --- another case with equivalence clauses above outer joins (bug #8591) --- -explain (costs off) -select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) - from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) - where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; - QUERY PLAN ---------------------------------------------------------------------------------------------- - Nested Loop Left Join - -> Nested Loop Left Join - Filter: (COALESCE(b.twothousand, a.twothousand) = 44) - -> Index Scan using tenk1_unique2 on tenk1 a - Index Cond: (unique2 < 10) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: (thousand = a.unique1) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = a.unique1) - -> Index Scan using tenk1_unique2 on tenk1 c - Index Cond: ((unique2 = COALESCE(b.twothousand, a.twothousand)) AND (unique2 = 44)) -(11 rows) - -select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) - from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) - where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; - unique1 | unique1 | unique1 | coalesce ----------+---------+---------+---------- -(0 rows) - --- --- check handling of join aliases when flattening multiple levels of subquery --- -explain (verbose, costs off) -select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from - (values (0),(1)) foo1(join_key) -left join - (select join_key, bug_field from - (select ss1.join_key, ss1.bug_field from - (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 - ) foo2 - left join - (select unique2 as join_key from tenk1 i2) ss2 - using (join_key) - ) foo3 -using (join_key); - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop Left Join - Output: "*VALUES*".column1, i1.f1, (666) - Join Filter: ("*VALUES*".column1 = i1.f1) - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Materialize - Output: i1.f1, (666) - -> Nested Loop Left Join - Output: i1.f1, 666 - -> Seq Scan on public.int4_tbl i1 - Output: i1.f1 - -> Index Only Scan using tenk1_unique2 on public.tenk1 i2 - Output: i2.unique2 - Index Cond: (i2.unique2 = i1.f1) -(14 rows) - -select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from - (values (0),(1)) foo1(join_key) -left join - (select join_key, bug_field from - (select ss1.join_key, ss1.bug_field from - (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 - ) foo2 - left join - (select unique2 as join_key from tenk1 i2) ss2 - using (join_key) - ) foo3 -using (join_key); - foo1_id | foo3_id | bug_field ----------+---------+----------- - 0 | 0 | 666 - 1 | | -(2 rows) - --- --- test successful handling of nested outer joins with degenerate join quals --- -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8.q1 = i8b2.q1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b2.q1, (NULL::integer) - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, NULL::integer - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(30 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Right Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8b2.q1 = i8.q1) - -> Nested Loop - Output: i8b2.q1, NULL::integer - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, i8b2.q2 - -> Materialize - -> Seq Scan on public.int4_tbl i4b2 - -> Hash - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(34 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 - where q1 = f1) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Right Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8b2.q1 = i8.q1) - -> Hash Join - Output: i8b2.q1, NULL::integer - Hash Cond: (i8b2.q1 = i4b2.f1) - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, i8b2.q2 - -> Hash - Output: i4b2.f1 - -> Seq Scan on public.int4_tbl i4b2 - Output: i4b2.f1 - -> Hash - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(37 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 - where q1 = f1) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select * from - text_tbl t1 - inner join int8_tbl i8 - on i8.q2 = 456 - right join text_tbl t2 - on t1.f1 = 'doh!' - left join int4_tbl i4 - on i8.q1 = i4.f1; - QUERY PLAN --------------------------------------------------------- - Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2, t2.f1, i4.f1 - -> Seq Scan on public.text_tbl t2 - Output: t2.f1 - -> Materialize - Output: i8.q1, i8.q2, i4.f1, t1.f1 - -> Nested Loop - Output: i8.q1, i8.q2, i4.f1, t1.f1 - -> Nested Loop Left Join - Output: i8.q1, i8.q2, i4.f1 - Join Filter: (i8.q1 = i4.f1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 456) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - Filter: (t1.f1 = 'doh!'::text) -(19 rows) - -select * from - text_tbl t1 - inner join int8_tbl i8 - on i8.q2 = 456 - right join text_tbl t2 - on t1.f1 = 'doh!' - left join int4_tbl i4 - on i8.q1 = i4.f1; - f1 | q1 | q2 | f1 | f1 -------+-----+-----+-------------------+---- - doh! | 123 | 456 | doh! | - doh! | 123 | 456 | hi de ho neighbor | -(2 rows) - --- --- test for appropriate join order in the presence of lateral references --- -explain (verbose, costs off) -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss -where t1.f1 = ss.f1; - QUERY PLAN --------------------------------------------------- - Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1 - Join Filter: (t1.f1 = t2.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Memoize - Output: (i8.q1), t2.f1 - Cache Key: i8.q1 - -> Limit - Output: (i8.q1), t2.f1 - -> Seq Scan on public.text_tbl t2 - Output: i8.q1, t2.f1 -(19 rows) - -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss -where t1.f1 = ss.f1; - f1 | q1 | q2 | q1 | f1 -------+------------------+-----+------------------+------ - doh! | 4567890123456789 | 123 | 4567890123456789 | doh! -(1 row) - -explain (verbose, costs off) -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, - lateral (select ss1.* from text_tbl t3 limit 1) as ss2 -where t1.f1 = ss2.f1; - QUERY PLAN -------------------------------------------------------------------- - Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1, ((i8.q1)), (t2.f1) - Join Filter: (t1.f1 = (t2.f1)) - -> Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1 - -> Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Memoize - Output: (i8.q1), t2.f1 - Cache Key: i8.q1 - -> Limit - Output: (i8.q1), t2.f1 - -> Seq Scan on public.text_tbl t2 - Output: i8.q1, t2.f1 - -> Memoize - Output: ((i8.q1)), (t2.f1) - Cache Key: (i8.q1), t2.f1 - -> Limit - Output: ((i8.q1)), (t2.f1) - -> Seq Scan on public.text_tbl t3 - Output: (i8.q1), t2.f1 -(28 rows) - -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, - lateral (select ss1.* from text_tbl t3 limit 1) as ss2 -where t1.f1 = ss2.f1; - f1 | q1 | q2 | q1 | f1 | q1 | f1 -------+------------------+-----+------------------+------+------------------+------ - doh! | 4567890123456789 | 123 | 4567890123456789 | doh! | 4567890123456789 | doh! -(1 row) - -explain (verbose, costs off) -select 1 from - text_tbl as tt1 - inner join text_tbl as tt2 on (tt1.f1 = 'foo') - left join text_tbl as tt3 on (tt3.f1 = 'foo') - left join text_tbl as tt4 on (tt3.f1 = tt4.f1), - lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 -where tt1.f1 = ss1.c0; - QUERY PLAN ----------------------------------------------------------- - Nested Loop - Output: 1 - -> Nested Loop Left Join - Output: tt1.f1, tt4.f1 - -> Nested Loop - Output: tt1.f1 - -> Seq Scan on public.text_tbl tt1 - Output: tt1.f1 - Filter: (tt1.f1 = 'foo'::text) - -> Seq Scan on public.text_tbl tt2 - Output: tt2.f1 - -> Materialize - Output: tt4.f1 - -> Nested Loop Left Join - Output: tt4.f1 - Join Filter: (tt3.f1 = tt4.f1) - -> Seq Scan on public.text_tbl tt3 - Output: tt3.f1 - Filter: (tt3.f1 = 'foo'::text) - -> Seq Scan on public.text_tbl tt4 - Output: tt4.f1 - Filter: (tt4.f1 = 'foo'::text) - -> Memoize - Output: ss1.c0 - Cache Key: tt4.f1 - -> Subquery Scan on ss1 - Output: ss1.c0 - Filter: (ss1.c0 = 'foo'::text) - -> Limit - Output: (tt4.f1) - -> Seq Scan on public.text_tbl tt5 - Output: tt4.f1 -(32 rows) - -select 1 from - text_tbl as tt1 - inner join text_tbl as tt2 on (tt1.f1 = 'foo') - left join text_tbl as tt3 on (tt3.f1 = 'foo') - left join text_tbl as tt4 on (tt3.f1 = tt4.f1), - lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 -where tt1.f1 = ss1.c0; - ?column? ----------- -(0 rows) - --- --- check a case in which a PlaceHolderVar forces join order --- -explain (verbose, costs off) -select ss2.* from - int4_tbl i41 - left join int8_tbl i8 - join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 - from int4_tbl i42, int4_tbl i43) ss1 - on i8.q1 = ss1.c2 - on i41.f1 = ss1.c1, - lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 -where ss1.c2 = 0; - QUERY PLAN ------------------------------------------------------------------------- - Nested Loop - Output: (i41.f1), (i8.q1), (i8.q2), (i42.f1), (i43.f1), ((42)) - -> Hash Join - Output: i41.f1, i42.f1, i8.q1, i8.q2, i43.f1, 42 - Hash Cond: (i41.f1 = i42.f1) - -> Nested Loop - Output: i8.q1, i8.q2, i43.f1, i41.f1 - -> Nested Loop - Output: i8.q1, i8.q2, i43.f1 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q1 = 0) - -> Seq Scan on public.int4_tbl i43 - Output: i43.f1 - Filter: (i43.f1 = 0) - -> Seq Scan on public.int4_tbl i41 - Output: i41.f1 - -> Hash - Output: i42.f1 - -> Seq Scan on public.int4_tbl i42 - Output: i42.f1 - -> Limit - Output: (i41.f1), (i8.q1), (i8.q2), (i42.f1), (i43.f1), ((42)) - -> Seq Scan on public.text_tbl - Output: i41.f1, i8.q1, i8.q2, i42.f1, i43.f1, (42) -(25 rows) - -select ss2.* from - int4_tbl i41 - left join int8_tbl i8 - join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 - from int4_tbl i42, int4_tbl i43) ss1 - on i8.q1 = ss1.c2 - on i41.f1 = ss1.c1, - lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 -where ss1.c2 = 0; - f1 | q1 | q2 | c1 | c2 | c3 -----+----+----+----+----+---- -(0 rows) - --- --- test successful handling of full join underneath left join (bug #14105) --- -explain (costs off) -select * from - (select 1 as id) as xx - left join - (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) - on (xx.id = coalesce(yy.id)); - QUERY PLAN ---------------------------------------- - Nested Loop Left Join - -> Result - -> Hash Full Join - Hash Cond: (a1.unique1 = (1)) - Filter: (1 = COALESCE((1))) - -> Seq Scan on tenk1 a1 - -> Hash - -> Result -(8 rows) - -select * from - (select 1 as id) as xx - left join - (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) - on (xx.id = coalesce(yy.id)); - id | unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 | id -----+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------+---- - 1 | 1 | 2838 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 3 | BAAAAA | EFEAAA | OOOOxx | 1 -(1 row) - --- --- test ability to push constants through outer join clauses --- -explain (costs off) - select * from int4_tbl a left join tenk1 b on f1 = unique2 where f1 = 0; - QUERY PLAN -------------------------------------------------- - Nested Loop Left Join - Join Filter: (a.f1 = b.unique2) - -> Seq Scan on int4_tbl a - Filter: (f1 = 0) - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = 0) -(6 rows) - -explain (costs off) - select * from tenk1 a full join tenk1 b using(unique2) where unique2 = 42; - QUERY PLAN -------------------------------------------------- - Merge Full Join - Merge Cond: (a.unique2 = b.unique2) - -> Index Scan using tenk1_unique2 on tenk1 a - Index Cond: (unique2 = 42) - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = 42) -(6 rows) - --- --- test that quals attached to an outer join have correct semantics, --- specifically that they don't re-use expressions computed below the join; --- we force a mergejoin so that coalesce(b.q1, 1) appears as a join input --- -set enable_hashjoin to off; -set enable_nestloop to off; -explain (verbose, costs off) - select a.q2, b.q1 - from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) - where coalesce(b.q1, 1) > 0; - QUERY PLAN ---------------------------------------------------------- - Merge Left Join - Output: a.q2, b.q1 - Merge Cond: (a.q2 = (COALESCE(b.q1, '1'::bigint))) - Filter: (COALESCE(b.q1, '1'::bigint) > 0) - -> Sort - Output: a.q2 - Sort Key: a.q2 - -> Seq Scan on public.int8_tbl a - Output: a.q2 - -> Sort - Output: b.q1, (COALESCE(b.q1, '1'::bigint)) - Sort Key: (COALESCE(b.q1, '1'::bigint)) - -> Seq Scan on public.int8_tbl b - Output: b.q1, COALESCE(b.q1, '1'::bigint) -(14 rows) - -select a.q2, b.q1 - from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) - where coalesce(b.q1, 1) > 0; - q2 | q1 --------------------+------------------ - -4567890123456789 | - 123 | 123 - 123 | 123 - 456 | - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 -(10 rows) - -reset enable_hashjoin; -reset enable_nestloop; --- --- test join removal --- -begin; -CREATE TEMP TABLE a (id int PRIMARY KEY, b_id int); -CREATE TEMP TABLE b (id int PRIMARY KEY, c_id int); -CREATE TEMP TABLE c (id int PRIMARY KEY); -CREATE TEMP TABLE d (a int, b int); -INSERT INTO a VALUES (0, 0), (1, NULL); -INSERT INTO b VALUES (0, 0), (1, NULL); -INSERT INTO c VALUES (0), (1); -INSERT INTO d VALUES (1,3), (2,2), (3,1); --- all three cases should be optimizable into a simple seqscan -explain (costs off) SELECT a.* FROM a LEFT JOIN b ON a.b_id = b.id; - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - -explain (costs off) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id; - QUERY PLAN ---------------- - Seq Scan on b -(1 row) - -explain (costs off) - SELECT a.* FROM a LEFT JOIN (b left join c on b.c_id = c.id) - ON (a.b_id = b.id); - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - --- check optimization of outer join within another special join -explain (costs off) -select id from a where id in ( - select b.id from b left join c on b.id = c.id -); - QUERY PLAN ----------------------------- - Hash Join - Hash Cond: (a.id = b.id) - -> Seq Scan on a - -> Hash - -> Seq Scan on b -(5 rows) - --- check that join removal works for a left join when joining a subquery --- that is guaranteed to be unique by its GROUP BY clause -explain (costs off) -select d.* from d left join (select * from b group by b.id, b.c_id) s - on d.a = s.id and d.b = s.c_id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- similarly, but keying off a DISTINCT clause -explain (costs off) -select d.* from d left join (select distinct * from b) s - on d.a = s.id and d.b = s.c_id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- join removal is not possible when the GROUP BY contains a column that is --- not in the join condition. (Note: as of 9.6, we notice that b.id is a --- primary key and so drop b.c_id from the GROUP BY of the resulting plan; --- but this happens too late for join removal in the outer plan level.) -explain (costs off) -select d.* from d left join (select * from b group by b.id, b.c_id) s - on d.a = s.id; - QUERY PLAN ------------------------------------------- - Merge Right Join - Merge Cond: (b.id = d.a) - -> Group - Group Key: b.id - -> Index Scan using b_pkey on b - -> Sort - Sort Key: d.a - -> Seq Scan on d -(8 rows) - --- similarly, but keying off a DISTINCT clause -explain (costs off) -select d.* from d left join (select distinct * from b) s - on d.a = s.id; - QUERY PLAN --------------------------------------- - Merge Right Join - Merge Cond: (b.id = d.a) - -> Unique - -> Sort - Sort Key: b.id, b.c_id - -> Seq Scan on b - -> Sort - Sort Key: d.a - -> Seq Scan on d -(9 rows) - --- check join removal works when uniqueness of the join condition is enforced --- by a UNION -explain (costs off) -select d.* from d left join (select id from a union select id from b) s - on d.a = s.id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- check join removal with a cross-type comparison operator -explain (costs off) -select i8.* from int8_tbl i8 left join (select f1 from int4_tbl group by f1) i4 - on i8.q1 = i4.f1; - QUERY PLAN -------------------------- - Seq Scan on int8_tbl i8 -(1 row) - --- check join removal with lateral references -explain (costs off) -select 1 from (select a.id FROM a left join b on a.b_id = b.id) q, - lateral generate_series(1, q.id) gs(i) where q.id = gs.i; - QUERY PLAN -------------------------------------------- - Nested Loop - -> Seq Scan on a - -> Function Scan on generate_series gs - Filter: (a.id = i) -(4 rows) - -rollback; -create temp table parent (k int primary key, pd int); -create temp table child (k int unique, cd int); -insert into parent values (1, 10), (2, 20), (3, 30); -insert into child values (1, 100), (4, 400); --- this case is optimizable -select p.* from parent p left join child c on (p.k = c.k); - k | pd ----+---- - 1 | 10 - 2 | 20 - 3 | 30 -(3 rows) - -explain (costs off) - select p.* from parent p left join child c on (p.k = c.k); - QUERY PLAN ----------------------- - Seq Scan on parent p -(1 row) - --- this case is not -select p.*, linked from parent p - left join (select c.*, true as linked from child c) as ss - on (p.k = ss.k); - k | pd | linked ----+----+-------- - 1 | 10 | t - 2 | 20 | - 3 | 30 | -(3 rows) - -explain (costs off) - select p.*, linked from parent p - left join (select c.*, true as linked from child c) as ss - on (p.k = ss.k); - QUERY PLAN ---------------------------------- - Hash Left Join - Hash Cond: (p.k = c.k) - -> Seq Scan on parent p - -> Hash - -> Seq Scan on child c -(5 rows) - --- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling -select p.* from - parent p left join child c on (p.k = c.k) - where p.k = 1 and p.k = 2; - k | pd ----+---- -(0 rows) - -explain (costs off) -select p.* from - parent p left join child c on (p.k = c.k) - where p.k = 1 and p.k = 2; - QUERY PLAN ------------------------------------------------- - Result - One-Time Filter: false - -> Index Scan using parent_pkey on parent p - Index Cond: (k = 1) -(4 rows) - -select p.* from - (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k - where p.k = 1 and p.k = 2; - k | pd ----+---- -(0 rows) - -explain (costs off) -select p.* from - (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k - where p.k = 1 and p.k = 2; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - --- bug 5255: this is not optimizable by join removal -begin; -CREATE TEMP TABLE a (id int PRIMARY KEY); -CREATE TEMP TABLE b (id int PRIMARY KEY, a_id int); -INSERT INTO a VALUES (0), (1); -INSERT INTO b VALUES (0, 0), (1, NULL); -SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); - id | a_id | id -----+------+---- - 1 | | -(1 row) - -SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); - id | a_id -----+------ - 1 | -(1 row) - -rollback; --- another join removal bug: this is not optimizable, either -begin; -create temp table innertab (id int8 primary key, dat1 int8); -insert into innertab values(123, 42); -SELECT * FROM - (SELECT 1 AS x) ss1 - LEFT JOIN - (SELECT q1, q2, COALESCE(dat1, q1) AS y - FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2 - ON true; - x | q1 | q2 | y ----+------------------+-------------------+------------------ - 1 | 123 | 456 | 123 - 1 | 123 | 4567890123456789 | 123 - 1 | 4567890123456789 | 123 | 42 - 1 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 1 | 4567890123456789 | -4567890123456789 | 4567890123456789 -(5 rows) - -rollback; --- another join removal bug: we must clean up correctly when removing a PHV -begin; -create temp table uniquetbl (f1 text unique); -explain (costs off) -select t1.* from - uniquetbl as t1 - left join (select *, '***'::text as d1 from uniquetbl) t2 - on t1.f1 = t2.f1 - left join uniquetbl t3 - on t2.d1 = t3.f1; - QUERY PLAN --------------------------- - Seq Scan on uniquetbl t1 -(1 row) - -explain (costs off) -select t0.* -from - text_tbl t0 - left join - (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, - t1.stringu2 - from tenk1 t1 - join int4_tbl i4 ON i4.f1 = t1.unique2 - left join uniquetbl u1 ON u1.f1 = t1.string4) ss - on t0.f1 = ss.case1 -where ss.stringu2 !~* ss.case1; - QUERY PLAN --------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END = t0.f1) - -> Nested Loop - -> Seq Scan on int4_tbl i4 - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: (unique2 = i4.f1) - Filter: (stringu2 !~* CASE ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END) - -> Materialize - -> Seq Scan on text_tbl t0 -(9 rows) - -select t0.* -from - text_tbl t0 - left join - (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, - t1.stringu2 - from tenk1 t1 - join int4_tbl i4 ON i4.f1 = t1.unique2 - left join uniquetbl u1 ON u1.f1 = t1.string4) ss - on t0.f1 = ss.case1 -where ss.stringu2 !~* ss.case1; - f1 ------- - doh! -(1 row) - -rollback; --- test case to expose miscomputation of required relid set for a PHV -explain (verbose, costs off) -select i8.*, ss.v, t.unique2 - from int8_tbl i8 - left join int4_tbl i4 on i4.f1 = 1 - left join lateral (select i4.f1 + 1 as v) as ss on true - left join tenk1 t on t.unique2 = ss.v -where q2 = 456; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - Output: i8.q1, i8.q2, ((i4.f1 + 1)), t.unique2 - -> Nested Loop Left Join - Output: i8.q1, i8.q2, (i4.f1 + 1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 456) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - Filter: (i4.f1 = 1) - -> Index Only Scan using tenk1_unique2 on public.tenk1 t - Output: t.unique2 - Index Cond: (t.unique2 = ((i4.f1 + 1))) -(13 rows) - -select i8.*, ss.v, t.unique2 - from int8_tbl i8 - left join int4_tbl i4 on i4.f1 = 1 - left join lateral (select i4.f1 + 1 as v) as ss on true - left join tenk1 t on t.unique2 = ss.v -where q2 = 456; - q1 | q2 | v | unique2 ------+-----+---+--------- - 123 | 456 | | -(1 row) - --- and check a related issue where we miscompute required relids for --- a PHV that's been translated to a child rel -create temp table parttbl (a integer primary key) partition by range (a); -create temp table parttbl1 partition of parttbl for values from (1) to (100); -insert into parttbl values (11), (12); -explain (costs off) -select * from - (select *, 12 as phv from parttbl) as ss - right join int4_tbl on true -where ss.a = ss.phv and f1 = 0; - QUERY PLAN ------------------------------------- - Nested Loop - -> Seq Scan on int4_tbl - Filter: (f1 = 0) - -> Seq Scan on parttbl1 parttbl - Filter: (a = 12) -(5 rows) - -select * from - (select *, 12 as phv from parttbl) as ss - right join int4_tbl on true -where ss.a = ss.phv and f1 = 0; - a | phv | f1 -----+-----+---- - 12 | 12 | 0 -(1 row) - --- bug #8444: we've historically allowed duplicate aliases within aliased JOINs -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error -ERROR: column reference "f1" is ambiguous -LINE 2: ..._tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; - ^ -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; -- error -ERROR: invalid reference to FROM-clause entry for table "y" -LINE 2: ...bl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; - ^ -HINT: There is an entry for table "y", but it cannot be referenced from this part of the query. -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y(ff)) j on q1 = f1; -- ok - q1 | q2 | f1 | ff -----+----+----+---- -(0 rows) - --- --- Test hints given on incorrect column references are useful --- -select t1.uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t1" suggestion -ERROR: column t1.uunique1 does not exist -LINE 1: select t1.uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t1.unique1". -select t2.uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t2" suggestion -ERROR: column t2.uunique1 does not exist -LINE 1: select t2.uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t2.unique1". -select uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, suggest both at once -ERROR: column "uunique1" does not exist -LINE 1: select uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t1.unique1" or the column "t2.unique1". --- --- Take care to reference the correct RTE --- -select atts.relid::regclass, s.* from pg_stats s join - pg_attribute a on s.attname = a.attname and s.tablename = - a.attrelid::regclass::text join (select unnest(indkey) attnum, - indexrelid from pg_index i) atts on atts.attnum = a.attnum where - schemaname != 'pg_catalog'; -ERROR: column atts.relid does not exist -LINE 1: select atts.relid::regclass, s.* from pg_stats s join - ^ --- --- Test LATERAL --- -select unique2, x.* -from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; - unique2 | f1 ----------+---- - 9998 | 0 -(1 row) - -explain (costs off) - select unique2, x.* - from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on int4_tbl b - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.f1) -(4 rows) - -select unique2, x.* -from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; - unique2 | f1 ----------+---- - 9998 | 0 -(1 row) - -explain (costs off) - select unique2, x.* - from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - -explain (costs off) - select unique2, x.* - from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - -select unique2, x.* -from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; - unique2 | f1 ----------+------------- - 9998 | 0 - | 123456 - | -123456 - | 2147483647 - | -2147483647 -(5 rows) - -explain (costs off) - select unique2, x.* - from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; - QUERY PLAN ------------------------------------------------ - Nested Loop Left Join - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - --- check scoping of lateral versus parent references --- the first of these should return int8_tbl.q2, the second int8_tbl.q1 -select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; - q1 | q2 | r -------------------+-------------------+------------------- - 123 | 456 | 456 - 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | -4567890123456789 -(5 rows) - -select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; - q1 | q2 | r -------------------+-------------------+------------------ - 123 | 456 | 123 - 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 -(5 rows) - --- lateral with function in FROM -select count(*) from tenk1 a, lateral generate_series(1,two) g; - count -------- - 5000 -(1 row) - -explain (costs off) - select count(*) from tenk1 a, lateral generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - -> Function Scan on generate_series g -(6 rows) - -explain (costs off) - select count(*) from tenk1 a cross join lateral generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - -> Function Scan on generate_series g -(6 rows) - --- don't need the explicit LATERAL keyword for functions -explain (costs off) - select count(*) from tenk1 a, generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - -> Function Scan on generate_series g -(6 rows) - --- lateral with UNION ALL subselect -explain (costs off) - select * from generate_series(100,200) g, - lateral (select * from int8_tbl a where g = q1 union all - select * from int8_tbl b where g = q2) ss; - QUERY PLAN ------------------------------------------- - Nested Loop - -> Function Scan on generate_series g - -> Append - -> Seq Scan on int8_tbl a - Filter: (g.g = q1) - -> Seq Scan on int8_tbl b - Filter: (g.g = q2) -(7 rows) - -select * from generate_series(100,200) g, - lateral (select * from int8_tbl a where g = q1 union all - select * from int8_tbl b where g = q2) ss; - g | q1 | q2 ------+------------------+------------------ - 123 | 123 | 456 - 123 | 123 | 4567890123456789 - 123 | 4567890123456789 | 123 -(3 rows) - --- lateral with VALUES -explain (costs off) - select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Merge Join - Merge Cond: (a.unique1 = b.unique2) - -> Index Only Scan using tenk1_unique1 on tenk1 a - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - -select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; - count -------- - 10000 -(1 row) - --- lateral with VALUES, no flattening possible -explain (costs off) - select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; - QUERY PLAN ------------------------------------------------------------------- - Aggregate - -> Nested Loop - -> Nested Loop - -> Index Only Scan using tenk1_unique1 on tenk1 a - -> Values Scan on "*VALUES*" - -> Memoize - Cache Key: "*VALUES*".column1 - -> Index Only Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = "*VALUES*".column1) -(9 rows) - -select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; - count -------- - 10000 -(1 row) - --- lateral injecting a strange outer join condition -explain (costs off) - select * from int8_tbl a, - int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) - on x.q2 = ss.z - order by a.q1, a.q2, x.q1, x.q2, ss.z; - QUERY PLAN ------------------------------------------------- - Sort - Sort Key: a.q1, a.q2, x.q1, x.q2, (a.q1) - -> Nested Loop - -> Seq Scan on int8_tbl a - -> Hash Right Join - Hash Cond: ((a.q1) = x.q2) - -> Seq Scan on int4_tbl y - -> Hash - -> Seq Scan on int8_tbl x -(9 rows) - -select * from int8_tbl a, - int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) - on x.q2 = ss.z - order by a.q1, a.q2, x.q1, x.q2, ss.z; - q1 | q2 | q1 | q2 | z -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | 123 | 456 | - 123 | 456 | 123 | 4567890123456789 | - 123 | 456 | 4567890123456789 | -4567890123456789 | - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 4567890123456789 | - 123 | 4567890123456789 | 123 | 456 | - 123 | 4567890123456789 | 123 | 4567890123456789 | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | - 4567890123456789 | -4567890123456789 | 123 | 456 | - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | -4567890123456789 | 4567890123456789 | 123 | - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | 123 | 4567890123456789 | 123 | - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 456 | - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 -(57 rows) - --- lateral reference to a join alias variable -select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, - lateral (select x) ss2(y); - x | f1 | y ----+----+--- - 0 | 0 | 0 -(1 row) - -select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, - lateral (values(x)) ss2(y); - x | f1 | y --------------+-------------+------------- - 0 | 0 | 0 - 123456 | 123456 | 123456 - -123456 | -123456 | -123456 - 2147483647 | 2147483647 | 2147483647 - -2147483647 | -2147483647 | -2147483647 -(5 rows) - -select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, - lateral (select x) ss2(y); - x | f1 | y ----+----+--- - 0 | 0 | 0 -(1 row) - --- lateral references requiring pullup -select * from (values(1)) x(lb), - lateral generate_series(lb,4) x4; - lb | x4 -----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 -(4 rows) - -select * from (select f1/1000000000 from int4_tbl) x(lb), - lateral generate_series(lb,4) x4; - lb | x4 -----+---- - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 2 | 2 - 2 | 3 - 2 | 4 - -2 | -2 - -2 | -1 - -2 | 0 - -2 | 1 - -2 | 2 - -2 | 3 - -2 | 4 -(25 rows) - -select * from (values(1)) x(lb), - lateral (values(lb)) y(lbcopy); - lb | lbcopy -----+-------- - 1 | 1 -(1 row) - -select * from (values(1)) x(lb), - lateral (select lb from int4_tbl) y(lbcopy); - lb | lbcopy -----+-------- - 1 | 1 - 1 | 1 - 1 | 1 - 1 | 1 - 1 | 1 -(5 rows) - -select * from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); - q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 -------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- - 123 | 456 | | | 123 | | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | -(10 rows) - -select * from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); - q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 -------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- - 123 | 456 | | | 123 | | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | -(10 rows) - -select x.* from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 123 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(10 rows) - -select v.* from - (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); - vx | vy --------------------+------------------- - 123 | - 456 | - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | - -4567890123456789 | -(20 rows) - -select v.* from - (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); - vx | vy --------------------+------------------- - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | - 456 | - 4567890123456789 | - -4567890123456789 | -(20 rows) - -select v.* from - (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 from onerow union all select x.q2,y.q2 from onerow) v(vx,vy); - vx | vy --------------------+------------------- - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | - 456 | - 4567890123456789 | - -4567890123456789 | -(20 rows) - -explain (verbose, costs off) -select * from - int8_tbl a left join - lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, b.q2, (a.q2) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2, a.q2 - Filter: (a.q2 = b.q1) -(7 rows) - -select * from - int8_tbl a left join - lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; - q1 | q2 | q1 | q2 | x -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(10 rows) - -explain (verbose, costs off) -select * from - int8_tbl a left join - lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; - QUERY PLAN ------------------------------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, b.q2, (COALESCE(a.q2, '42'::bigint)) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2, COALESCE(a.q2, '42'::bigint) - Filter: (a.q2 = b.q1) -(7 rows) - -select * from - int8_tbl a left join - lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; - q1 | q2 | q1 | q2 | x -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(10 rows) - --- lateral can result in join conditions appearing below their --- real semantic level -explain (verbose, costs off) -select * from int4_tbl i left join - lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; - QUERY PLAN -------------------------------------------- - Hash Left Join - Output: i.f1, j.f1 - Hash Cond: (i.f1 = j.f1) - -> Seq Scan on public.int4_tbl i - Output: i.f1 - -> Hash - Output: j.f1 - -> Seq Scan on public.int2_tbl j - Output: j.f1 -(9 rows) - -select * from int4_tbl i left join - lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; - f1 | f1 --------------+---- - 0 | 0 - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(5 rows) - -explain (verbose, costs off) -select * from int4_tbl i left join - lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; - QUERY PLAN -------------------------------------- - Nested Loop Left Join - Output: i.f1, (COALESCE(i.*)) - -> Seq Scan on public.int4_tbl i - Output: i.f1, i.* - -> Seq Scan on public.int2_tbl j - Output: j.f1, COALESCE(i.*) - Filter: (i.f1 = j.f1) -(7 rows) - -select * from int4_tbl i left join - lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; - f1 | coalesce --------------+---------- - 0 | (0) - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(5 rows) - -explain (verbose, costs off) -select * from int4_tbl a, - lateral ( - select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) - ) ss; - QUERY PLAN -------------------------------------------------- - Nested Loop - Output: a.f1, b.f1, c.q1, c.q2 - -> Seq Scan on public.int4_tbl a - Output: a.f1 - -> Hash Left Join - Output: b.f1, c.q1, c.q2 - Hash Cond: (b.f1 = c.q1) - -> Seq Scan on public.int4_tbl b - Output: b.f1 - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - Filter: (a.f1 = c.q2) -(14 rows) - -select * from int4_tbl a, - lateral ( - select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) - ) ss; - f1 | f1 | q1 | q2 --------------+-------------+----+---- - 0 | 0 | | - 0 | 123456 | | - 0 | -123456 | | - 0 | 2147483647 | | - 0 | -2147483647 | | - 123456 | 0 | | - 123456 | 123456 | | - 123456 | -123456 | | - 123456 | 2147483647 | | - 123456 | -2147483647 | | - -123456 | 0 | | - -123456 | 123456 | | - -123456 | -123456 | | - -123456 | 2147483647 | | - -123456 | -2147483647 | | - 2147483647 | 0 | | - 2147483647 | 123456 | | - 2147483647 | -123456 | | - 2147483647 | 2147483647 | | - 2147483647 | -2147483647 | | - -2147483647 | 0 | | - -2147483647 | 123456 | | - -2147483647 | -123456 | | - -2147483647 | 2147483647 | | - -2147483647 | -2147483647 | | -(25 rows) - --- lateral reference in a PlaceHolderVar evaluated at join level -explain (verbose, costs off) -select * from - int8_tbl a left join lateral - (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from - int8_tbl b cross join int8_tbl c) ss - on a.q2 = ss.bq1; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, c.q1, (LEAST(a.q1, b.q1, c.q1)) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Nested Loop - Output: b.q1, c.q1, LEAST(a.q1, b.q1, c.q1) - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2 - Filter: (a.q2 = b.q1) - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 -(11 rows) - -select * from - int8_tbl a left join lateral - (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from - int8_tbl b cross join int8_tbl c) ss - on a.q2 = ss.bq1; - q1 | q2 | bq1 | cq1 | least -------------------+-------------------+------------------+------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(42 rows) - --- case requiring nested PlaceHolderVars -explain (verbose, costs off) -select * from - int8_tbl c left join ( - int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 - on a.q2 = ss1.q1 - cross join - lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 - ) on c.q2 = ss2.q1, - lateral (select ss2.y offset 0) ss3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)), d.q1, (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)), ((COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2))) - -> Hash Right Join - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, '42'::bigint)), (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) - Hash Cond: (d.q1 = c.q2) - -> Nested Loop - Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, '42'::bigint)), (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) - -> Hash Left Join - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)) - Hash Cond: (a.q2 = b.q1) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Hash - Output: b.q1, (COALESCE(b.q2, '42'::bigint)) - -> Seq Scan on public.int8_tbl b - Output: b.q1, COALESCE(b.q2, '42'::bigint) - -> Seq Scan on public.int8_tbl d - Output: d.q1, COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2) - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - -> Result - Output: (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) -(24 rows) - --- case that breaks the old ph_may_need optimization -explain (verbose, costs off) -select c.*,a.*,ss1.q1,ss2.q1,ss3.* from - int8_tbl c left join ( - int8_tbl a left join - (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 - where q1 < f1) ss1 - on a.q2 = ss1.q1 - cross join - lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 - ) on c.q2 = ss2.q1, - lateral (select * from int4_tbl i where ss2.y > f1) ss3; - QUERY PLAN ---------------------------------------------------------------------------------------------------------- - Nested Loop - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, i.f1 - Join Filter: ((COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) > i.f1) - -> Hash Right Join - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) - Hash Cond: (d.q1 = c.q2) - -> Nested Loop - Output: a.q1, a.q2, b.q1, d.q1, (COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) - -> Hash Right Join - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, (b2.f1)::bigint)) - Hash Cond: (b.q1 = a.q2) - -> Nested Loop - Output: b.q1, COALESCE(b.q2, (b2.f1)::bigint) - Join Filter: (b.q1 < b2.f1) - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2 - -> Materialize - Output: b2.f1 - -> Seq Scan on public.int4_tbl b2 - Output: b2.f1 - -> Hash - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl d - Output: d.q1, COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2) - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - -> Materialize - Output: i.f1 - -> Seq Scan on public.int4_tbl i - Output: i.f1 -(34 rows) - --- check processing of postponed quals (bug #9041) -explain (verbose, costs off) -select * from - (select 1 as x offset 0) x cross join (select 2 as y offset 0) y - left join lateral ( - select * from (select 3 as z offset 0) z where z.z = x.x - ) zz on zz.z = y.y; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Output: (1), (2), (3) - Join Filter: (((3) = (1)) AND ((3) = (2))) - -> Nested Loop - Output: (1), (2) - -> Result - Output: 1 - -> Result - Output: 2 - -> Result - Output: 3 -(11 rows) - --- check dummy rels with lateral references (bug #15694) -explain (verbose, costs off) -select * from int8_tbl i8 left join lateral - (select *, i8.q2 from int4_tbl where false) ss on true; - QUERY PLAN --------------------------------------- - Nested Loop Left Join - Output: i8.q1, i8.q2, f1, (i8.q2) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Result - Output: f1, i8.q2 - One-Time Filter: false -(7 rows) - -explain (verbose, costs off) -select * from int8_tbl i8 left join lateral - (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; - QUERY PLAN ------------------------------------------ - Nested Loop Left Join - Output: i8.q1, i8.q2, f1, f1, (i8.q2) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Result - Output: f1, f1, i8.q2 - One-Time Filter: false -(7 rows) - --- check handling of nested appendrels inside LATERAL -select * from - ((select 2 as v) union all (select 3 as v)) as q1 - cross join lateral - ((select * from - ((select 4 as v) union all (select 5 as v)) as q3) - union all - (select q1.v) - ) as q2; - v | v ----+--- - 2 | 4 - 2 | 5 - 2 | 2 - 3 | 4 - 3 | 5 - 3 | 3 -(6 rows) - --- check we don't try to do a unique-ified semijoin with LATERAL -explain (verbose, costs off) -select * from - (values (0,9998), (1,1000)) v(id,x), - lateral (select f1 from int4_tbl - where f1 = any (select unique1 from tenk1 - where unique2 = v.x offset 0)) ss; - QUERY PLAN ----------------------------------------------------------------------- - Nested Loop - Output: "*VALUES*".column1, "*VALUES*".column2, int4_tbl.f1 - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1, "*VALUES*".column2 - -> Nested Loop Semi Join - Output: int4_tbl.f1 - Join Filter: (int4_tbl.f1 = tenk1.unique1) - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - -> Materialize - Output: tenk1.unique1 - -> Index Scan using tenk1_unique2 on public.tenk1 - Output: tenk1.unique1 - Index Cond: (tenk1.unique2 = "*VALUES*".column2) -(14 rows) - -select * from - (values (0,9998), (1,1000)) v(id,x), - lateral (select f1 from int4_tbl - where f1 = any (select unique1 from tenk1 - where unique2 = v.x offset 0)) ss; - id | x | f1 -----+------+---- - 0 | 9998 | 0 -(1 row) - --- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, --- but we can make the test case much more compact with LATERAL) -explain (verbose, costs off) -select * from (values (0), (1)) v(id), -lateral (select * from int8_tbl t1, - lateral (select * from - (select * from int8_tbl t2 - where q1 = any (select q2 from int8_tbl t3 - where q2 = (select greatest(t1.q1,t2.q2)) - and (select v.id=0)) offset 0) ss2) ss - where t1.q1 = ss.q2) ss0; - QUERY PLAN ------------------------------------------------------------------ - Nested Loop - Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 - -> Seq Scan on public.int8_tbl t1 - Output: t1.q1, t1.q2 - -> Nested Loop - Output: "*VALUES*".column1, ss2.q1, ss2.q2 - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Subquery Scan on ss2 - Output: ss2.q1, ss2.q2 - Filter: (t1.q1 = ss2.q2) - -> Seq Scan on public.int8_tbl t2 - Output: t2.q1, t2.q2 - Filter: (SubPlan 3) - SubPlan 3 - -> Result - Output: t3.q2 - One-Time Filter: $4 - InitPlan 1 (returns $2) - -> Result - Output: GREATEST($0, t2.q2) - InitPlan 2 (returns $4) - -> Result - Output: ($3 = 0) - -> Seq Scan on public.int8_tbl t3 - Output: t3.q1, t3.q2 - Filter: (t3.q2 = $2) -(27 rows) - -select * from (values (0), (1)) v(id), -lateral (select * from int8_tbl t1, - lateral (select * from - (select * from int8_tbl t2 - where q1 = any (select q2 from int8_tbl t3 - where q2 = (select greatest(t1.q1,t2.q2)) - and (select v.id=0)) offset 0) ss2) ss - where t1.q1 = ss.q2) ss0; - id | q1 | q2 | q1 | q2 -----+------------------+-------------------+------------------+------------------ - 0 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 0 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 0 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 -(3 rows) - --- test some error cases where LATERAL should have been used but wasn't -select f1,g from int4_tbl a, (select f1 as g) ss; -ERROR: column "f1" does not exist -LINE 1: select f1,g from int4_tbl a, (select f1 as g) ss; - ^ -HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. -select f1,g from int4_tbl a, (select a.f1 as g) ss; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: select f1,g from int4_tbl a, (select a.f1 as g) ss; - ^ -HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. -select f1,g from int4_tbl a cross join (select f1 as g) ss; -ERROR: column "f1" does not exist -LINE 1: select f1,g from int4_tbl a cross join (select f1 as g) ss; - ^ -HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. -select f1,g from int4_tbl a cross join (select a.f1 as g) ss; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: select f1,g from int4_tbl a cross join (select a.f1 as g) ss... - ^ -HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. --- SQL:2008 says the left table is in scope but illegal to access here -select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: ... int4_tbl a right join lateral generate_series(0, a.f1) g on... - ^ -DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. -select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: ...m int4_tbl a full join lateral generate_series(0, a.f1) g on... - ^ -DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. --- check we complain about ambiguous table references -select * from - int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); -ERROR: table reference "x" is ambiguous -LINE 2: ...cross join (int4_tbl x cross join lateral (select x.f1) ss); - ^ --- LATERAL can be used to put an aggregate into the FROM clause of its query -select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 1: select 1 from tenk1 a, lateral (select max(a.unique1) from i... - ^ --- check behavior of LATERAL in UPDATE/DELETE -create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; --- error, can't do this: -update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; -ERROR: column "x1" does not exist -LINE 1: ... set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. -update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...t x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss... - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- can't do it even with LATERAL: -update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...= f1 from lateral (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- we might in future allow something like this, but for now it's an error: -update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: table name "xx1" specified more than once --- also errors: -delete from xx1 using (select * from int4_tbl where f1 = x1) ss; -ERROR: column "x1" does not exist -LINE 1: ...te from xx1 using (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. -delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss... - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. -delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...xx1 using lateral (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- --- test LATERAL reference propagation down a multi-level inheritance hierarchy --- produced for a multi-level partitioned table hierarchy. --- -create table join_pt1 (a int, b int, c varchar) partition by range(a); -create table join_pt1p1 partition of join_pt1 for values from (0) to (100) partition by range(b); -create table join_pt1p2 partition of join_pt1 for values from (100) to (200); -create table join_pt1p1p1 partition of join_pt1p1 for values from (0) to (100); -insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); -create table join_ut1 (a int, b int, c varchar); -insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); -explain (verbose, costs off) -select t1.b, ss.phv from join_ut1 t1 left join lateral - (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv - from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss - on t1.a = ss.t2a order by t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a - Sort Key: t1.a - -> Nested Loop Left Join - Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a - -> Seq Scan on public.join_ut1 t1 - Output: t1.a, t1.b, t1.c - -> Hash Join - Output: t2.a, LEAST(t1.a, t2.a, t3.a) - Hash Cond: (t3.b = t2.a) - -> Seq Scan on public.join_ut1 t3 - Output: t3.a, t3.b, t3.c - -> Hash - Output: t2.a - -> Append - -> Seq Scan on public.join_pt1p1p1 t2_1 - Output: t2_1.a - Filter: (t1.a = t2_1.a) - -> Seq Scan on public.join_pt1p2 t2_2 - Output: t2_2.a - Filter: (t1.a = t2_2.a) -(21 rows) - -select t1.b, ss.phv from join_ut1 t1 left join lateral - (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv - from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss - on t1.a = ss.t2a order by t1.a; - b | phv ------+----- - 2 | - 101 | 101 -(2 rows) - -drop table join_pt1; -drop table join_ut1; --- --- test estimation behavior with multi-column foreign key and constant qual --- -begin; -create table fkest (x integer, x10 integer, x10b integer, x100 integer); -insert into fkest select x, x/10, x/10, x/100 from generate_series(1,1000) x; -create unique index on fkest(x, x10, x100); -analyze fkest; -explain (costs off) -select * from fkest f1 - join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100) - join fkest f3 on f1.x = f3.x - where f1.x100 = 2; - QUERY PLAN ------------------------------------------------------------ - Nested Loop - -> Hash Join - Hash Cond: ((f2.x = f1.x) AND (f2.x10b = f1.x10)) - -> Seq Scan on fkest f2 - Filter: (x100 = 2) - -> Hash - -> Seq Scan on fkest f1 - Filter: (x100 = 2) - -> Index Scan using fkest_x_x10_x100_idx on fkest f3 - Index Cond: (x = f1.x) -(10 rows) - -alter table fkest add constraint fk - foreign key (x, x10b, x100) references fkest (x, x10, x100); -explain (costs off) -select * from fkest f1 - join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100) - join fkest f3 on f1.x = f3.x - where f1.x100 = 2; - QUERY PLAN ------------------------------------------------------ - Hash Join - Hash Cond: ((f2.x = f1.x) AND (f2.x10b = f1.x10)) - -> Hash Join - Hash Cond: (f3.x = f2.x) - -> Seq Scan on fkest f3 - -> Hash - -> Seq Scan on fkest f2 - Filter: (x100 = 2) - -> Hash - -> Seq Scan on fkest f1 - Filter: (x100 = 2) -(11 rows) - -rollback; --- --- test that foreign key join estimation performs sanely for outer joins --- -begin; -create table fkest (a int, b int, c int unique, primary key(a,b)); -create table fkest1 (a int, b int, primary key(a,b)); -insert into fkest select x/10, x%10, x from generate_series(1,1000) x; -insert into fkest1 select x/10, x%10 from generate_series(1,1000) x; -alter table fkest1 - add constraint fkest1_a_b_fkey foreign key (a,b) references fkest; -analyze fkest; -analyze fkest1; -explain (costs off) -select * -from fkest f - left join fkest1 f1 on f.a = f1.a and f.b = f1.b - left join fkest1 f2 on f.a = f2.a and f.b = f2.b - left join fkest1 f3 on f.a = f3.a and f.b = f3.b -where f.c = 1; - QUERY PLAN ------------------------------------------------------------------- - Nested Loop Left Join - -> Nested Loop Left Join - -> Nested Loop Left Join - -> Index Scan using fkest_c_key on fkest f - Index Cond: (c = 1) - -> Index Only Scan using fkest1_pkey on fkest1 f1 - Index Cond: ((a = f.a) AND (b = f.b)) - -> Index Only Scan using fkest1_pkey on fkest1 f2 - Index Cond: ((a = f.a) AND (b = f.b)) - -> Index Only Scan using fkest1_pkey on fkest1 f3 - Index Cond: ((a = f.a) AND (b = f.b)) -(11 rows) - -rollback; --- --- test planner's ability to mark joins as unique --- -create table j1 (id int primary key); -create table j2 (id int primary key); -create table j3 (id int); -insert into j1 values(1),(2),(3); -insert into j2 values(1),(2),(3); -insert into j3 values(1),(1); -analyze j1; -analyze j2; -analyze j3; --- ensure join is properly marked as unique -explain (verbose, costs off) -select * from j1 inner join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure join is not unique when not an equi-join -explain (verbose, costs off) -select * from j1 inner join j2 on j1.id > j2.id; - QUERY PLAN ------------------------------------ - Nested Loop - Output: j1.id, j2.id - Join Filter: (j1.id > j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Materialize - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(9 rows) - --- ensure non-unique rel is not chosen as inner -explain (verbose, costs off) -select * from j1 inner join j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id, j3.id - Inner Unique: true - Hash Cond: (j3.id = j1.id) - -> Seq Scan on public.j3 - Output: j3.id - -> Hash - Output: j1.id - -> Seq Scan on public.j1 - Output: j1.id -(10 rows) - --- ensure left join is marked as unique -explain (verbose, costs off) -select * from j1 left join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Left Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure right join is marked as unique -explain (verbose, costs off) -select * from j1 right join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Left Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j2.id = j1.id) - -> Seq Scan on public.j2 - Output: j2.id - -> Hash - Output: j1.id - -> Seq Scan on public.j1 - Output: j1.id -(10 rows) - --- ensure full join is marked as unique -explain (verbose, costs off) -select * from j1 full join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Full Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- a clauseless (cross) join can't be unique -explain (verbose, costs off) -select * from j1 cross join j2; - QUERY PLAN ------------------------------------ - Nested Loop - Output: j1.id, j2.id - -> Seq Scan on public.j1 - Output: j1.id - -> Materialize - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(8 rows) - --- ensure a natural join is marked as unique -explain (verbose, costs off) -select * from j1 natural join j2; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure a distinct clause allows the inner to become unique -explain (verbose, costs off) -select * from j1 -inner join (select distinct id from j3) j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------------ - Nested Loop - Output: j1.id, j3.id - Inner Unique: true - Join Filter: (j1.id = j3.id) - -> Unique - Output: j3.id - -> Sort - Output: j3.id - Sort Key: j3.id - -> Seq Scan on public.j3 - Output: j3.id - -> Seq Scan on public.j1 - Output: j1.id -(13 rows) - --- ensure group by clause allows the inner to become unique -explain (verbose, costs off) -select * from j1 -inner join (select id from j3 group by id) j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------------ - Nested Loop - Output: j1.id, j3.id - Inner Unique: true - Join Filter: (j1.id = j3.id) - -> Group - Output: j3.id - Group Key: j3.id - -> Sort - Output: j3.id - Sort Key: j3.id - -> Seq Scan on public.j3 - Output: j3.id - -> Seq Scan on public.j1 - Output: j1.id -(14 rows) - -drop table j1; -drop table j2; -drop table j3; --- test more complex permutations of unique joins -create table j1 (id1 int, id2 int, primary key(id1,id2)); -create table j2 (id1 int, id2 int, primary key(id1,id2)); -create table j3 (id1 int, id2 int, primary key(id1,id2)); -insert into j1 values(1,1),(1,2); -insert into j2 values(1,1); -insert into j3 values(1,1); -analyze j1; -analyze j2; -analyze j3; --- ensure there's no unique join when not all columns which are part of the --- unique index are seen in the join clause -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1; - QUERY PLAN ------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 -(7 rows) - --- ensure proper unique detection with multiple join quals -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2; - QUERY PLAN ----------------------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Inner Unique: true - Join Filter: ((j1.id1 = j2.id1) AND (j1.id2 = j2.id2)) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 -(8 rows) - --- ensure we don't detect the join to be unique when quals are not part of the --- join condition -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1 where j1.id2 = 1; - QUERY PLAN ------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 - Filter: (j1.id2 = 1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 -(8 rows) - --- as above, but for left joins. -explain (verbose, costs off) -select * from j1 -left join j2 on j1.id1 = j2.id1 where j1.id2 = 1; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 - Filter: (j1.id2 = 1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 -(8 rows) - --- validate logic in merge joins which skips mark and restore. --- it should only do this if all quals which were used to detect the unique --- are present as join quals, and not plain quals. -set enable_nestloop to 0; -set enable_hashjoin to 0; -set enable_sort to 0; --- create indexes that will be preferred over the PKs to perform the join -create index j1_id1_idx on j1 (id1) where id1 % 1000 = 1; -create index j2_id1_idx on j2 (id1) where id1 % 1000 = 1; --- need an additional row in j2, if we want j2_id1_idx to be preferred -insert into j2 values(1,2); -analyze j2; -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; - QUERY PLAN ------------------------------------------ - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j1.id2 = j2.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Scan using j2_id1_idx on j2 -(5 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - --- Exercise array keys mark/restore B-Tree code -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]); - QUERY PLAN ----------------------------------------------------- - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j1.id2 = j2.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Scan using j2_id1_idx on j2 - Index Cond: (id1 = ANY ('{1}'::integer[])) -(6 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]); - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - --- Exercise array keys "find extreme element" B-Tree code -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]); - QUERY PLAN -------------------------------------------------------- - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j1.id2 = j2.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Only Scan using j2_pkey on j2 - Index Cond: (id1 >= ANY ('{1,5}'::integer[])) - Filter: ((id1 % 1000) = 1) -(7 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]); - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - -reset enable_nestloop; -reset enable_hashjoin; -reset enable_sort; -drop table j1; -drop table j2; -drop table j3; --- check that semijoin inner is not seen as unique for a portion of the outerrel -explain (verbose, costs off) -select t1.unique1, t2.hundred -from onek t1, tenk1 t2 -where exists (select 1 from tenk1 t3 - where t3.thousand = t1.unique1 and t3.tenthous = t2.hundred) - and t1.unique1 < 1; - QUERY PLAN ---------------------------------------------------------------------------------- - Nested Loop - Output: t1.unique1, t2.hundred - -> Hash Join - Output: t1.unique1, t3.tenthous - Hash Cond: (t3.thousand = t1.unique1) - -> HashAggregate - Output: t3.thousand, t3.tenthous - Group Key: t3.thousand, t3.tenthous - -> Index Only Scan using tenk1_thous_tenthous on public.tenk1 t3 - Output: t3.thousand, t3.tenthous - -> Hash - Output: t1.unique1 - -> Index Only Scan using onek_unique1 on public.onek t1 - Output: t1.unique1 - Index Cond: (t1.unique1 < 1) - -> Index Only Scan using tenk1_hundred on public.tenk1 t2 - Output: t2.hundred - Index Cond: (t2.hundred = t3.tenthous) -(18 rows) - --- ... unless it actually is unique -create table j3 as select unique1, tenthous from onek; -vacuum analyze j3; -create unique index on j3(unique1, tenthous); -explain (verbose, costs off) -select t1.unique1, t2.hundred -from onek t1, tenk1 t2 -where exists (select 1 from j3 - where j3.unique1 = t1.unique1 and j3.tenthous = t2.hundred) - and t1.unique1 < 1; - QUERY PLAN ------------------------------------------------------------------------- - Nested Loop - Output: t1.unique1, t2.hundred - -> Nested Loop - Output: t1.unique1, j3.tenthous - -> Index Only Scan using onek_unique1 on public.onek t1 - Output: t1.unique1 - Index Cond: (t1.unique1 < 1) - -> Index Only Scan using j3_unique1_tenthous_idx on public.j3 - Output: j3.unique1, j3.tenthous - Index Cond: (j3.unique1 = t1.unique1) - -> Index Only Scan using tenk1_hundred on public.tenk1 t2 - Output: t2.hundred - Index Cond: (t2.hundred = j3.tenthous) -(13 rows) - -drop table j3; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/aggregates.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/aggregates.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/aggregates.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/aggregates.out 2021-10-30 22:04:12 -0600 @@ -1,2815 +1,2 @@ --- --- AGGREGATES --- --- avoid bit-exact output here because operations may not be bit-exact. -SET extra_float_digits = 0; -SELECT avg(four) AS avg_1 FROM onek; - avg_1 --------------------- - 1.5000000000000000 -(1 row) - -SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; - avg_32 ---------------------- - 32.6666666666666667 -(1 row) - --- In 7.1, avg(float4) is computed using float8 arithmetic. --- Round the result to 3 digits to avoid platform-specific results. -SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest; - avg_107_943 -------------- - 107.943 -(1 row) - -SELECT avg(gpa) AS avg_3_4 FROM ONLY student; - avg_3_4 ---------- - 3.4 -(1 row) - -SELECT sum(four) AS sum_1500 FROM onek; - sum_1500 ----------- - 1500 -(1 row) - -SELECT sum(a) AS sum_198 FROM aggtest; - sum_198 ---------- - 198 -(1 row) - -SELECT sum(b) AS avg_431_773 FROM aggtest; - avg_431_773 -------------- - 431.773 -(1 row) - -SELECT sum(gpa) AS avg_6_8 FROM ONLY student; - avg_6_8 ---------- - 6.8 -(1 row) - -SELECT max(four) AS max_3 FROM onek; - max_3 -------- - 3 -(1 row) - -SELECT max(a) AS max_100 FROM aggtest; - max_100 ---------- - 100 -(1 row) - -SELECT max(aggtest.b) AS max_324_78 FROM aggtest; - max_324_78 ------------- - 324.78 -(1 row) - -SELECT max(student.gpa) AS max_3_7 FROM student; - max_3_7 ---------- - 3.7 -(1 row) - -SELECT stddev_pop(b) FROM aggtest; - stddev_pop ------------------ - 131.10703231895 -(1 row) - -SELECT stddev_samp(b) FROM aggtest; - stddev_samp ------------------- - 151.389360803998 -(1 row) - -SELECT var_pop(b) FROM aggtest; - var_pop ------------------- - 17189.0539234823 -(1 row) - -SELECT var_samp(b) FROM aggtest; - var_samp ------------------- - 22918.7385646431 -(1 row) - -SELECT stddev_pop(b::numeric) FROM aggtest; - stddev_pop ------------------- - 131.107032862199 -(1 row) - -SELECT stddev_samp(b::numeric) FROM aggtest; - stddev_samp ------------------- - 151.389361431288 -(1 row) - -SELECT var_pop(b::numeric) FROM aggtest; - var_pop --------------------- - 17189.054065929769 -(1 row) - -SELECT var_samp(b::numeric) FROM aggtest; - var_samp --------------------- - 22918.738754573025 -(1 row) - --- population variance is defined for a single tuple, sample variance --- is not -SELECT var_pop(1.0::float8), var_samp(2.0::float8); - var_pop | var_samp ----------+---------- - 0 | -(1 row) - -SELECT stddev_pop(3.0::float8), stddev_samp(4.0::float8); - stddev_pop | stddev_samp -------------+------------- - 0 | -(1 row) - -SELECT var_pop('inf'::float8), var_samp('inf'::float8); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('inf'::float8), stddev_samp('inf'::float8); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - -SELECT var_pop('nan'::float8), var_samp('nan'::float8); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('nan'::float8), stddev_samp('nan'::float8); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - -SELECT var_pop(1.0::float4), var_samp(2.0::float4); - var_pop | var_samp ----------+---------- - 0 | -(1 row) - -SELECT stddev_pop(3.0::float4), stddev_samp(4.0::float4); - stddev_pop | stddev_samp -------------+------------- - 0 | -(1 row) - -SELECT var_pop('inf'::float4), var_samp('inf'::float4); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('inf'::float4), stddev_samp('inf'::float4); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - -SELECT var_pop('nan'::float4), var_samp('nan'::float4); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('nan'::float4), stddev_samp('nan'::float4); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - -SELECT var_pop(1.0::numeric), var_samp(2.0::numeric); - var_pop | var_samp ----------+---------- - 0 | -(1 row) - -SELECT stddev_pop(3.0::numeric), stddev_samp(4.0::numeric); - stddev_pop | stddev_samp -------------+------------- - 0 | -(1 row) - -SELECT var_pop('inf'::numeric), var_samp('inf'::numeric); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('inf'::numeric), stddev_samp('inf'::numeric); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - -SELECT var_pop('nan'::numeric), var_samp('nan'::numeric); - var_pop | var_samp ----------+---------- - NaN | -(1 row) - -SELECT stddev_pop('nan'::numeric), stddev_samp('nan'::numeric); - stddev_pop | stddev_samp -------------+------------- - NaN | -(1 row) - --- verify correct results for null and NaN inputs -select sum(null::int4) from generate_series(1,3); - sum ------ - -(1 row) - -select sum(null::int8) from generate_series(1,3); - sum ------ - -(1 row) - -select sum(null::numeric) from generate_series(1,3); - sum ------ - -(1 row) - -select sum(null::float8) from generate_series(1,3); - sum ------ - -(1 row) - -select avg(null::int4) from generate_series(1,3); - avg ------ - -(1 row) - -select avg(null::int8) from generate_series(1,3); - avg ------ - -(1 row) - -select avg(null::numeric) from generate_series(1,3); - avg ------ - -(1 row) - -select avg(null::float8) from generate_series(1,3); - avg ------ - -(1 row) - -select sum('NaN'::numeric) from generate_series(1,3); - sum ------ - NaN -(1 row) - -select avg('NaN'::numeric) from generate_series(1,3); - avg ------ - NaN -(1 row) - --- verify correct results for infinite inputs -SELECT sum(x::float8), avg(x::float8), var_pop(x::float8) -FROM (VALUES ('1'), ('infinity')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::float8), avg(x::float8), var_pop(x::float8) -FROM (VALUES ('infinity'), ('1')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::float8), avg(x::float8), var_pop(x::float8) -FROM (VALUES ('infinity'), ('infinity')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::float8), avg(x::float8), var_pop(x::float8) -FROM (VALUES ('-infinity'), ('infinity')) v(x); - sum | avg | var_pop ------+-----+--------- - NaN | NaN | NaN -(1 row) - -SELECT sum(x::float8), avg(x::float8), var_pop(x::float8) -FROM (VALUES ('-infinity'), ('-infinity')) v(x); - sum | avg | var_pop ------------+-----------+--------- - -Infinity | -Infinity | NaN -(1 row) - -SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric) -FROM (VALUES ('1'), ('infinity')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric) -FROM (VALUES ('infinity'), ('1')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric) -FROM (VALUES ('infinity'), ('infinity')) v(x); - sum | avg | var_pop -----------+----------+--------- - Infinity | Infinity | NaN -(1 row) - -SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric) -FROM (VALUES ('-infinity'), ('infinity')) v(x); - sum | avg | var_pop ------+-----+--------- - NaN | NaN | NaN -(1 row) - -SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric) -FROM (VALUES ('-infinity'), ('-infinity')) v(x); - sum | avg | var_pop ------------+-----------+--------- - -Infinity | -Infinity | NaN -(1 row) - --- test accuracy with a large input offset -SELECT avg(x::float8), var_pop(x::float8) -FROM (VALUES (100000003), (100000004), (100000006), (100000007)) v(x); - avg | var_pop ------------+--------- - 100000005 | 2.5 -(1 row) - -SELECT avg(x::float8), var_pop(x::float8) -FROM (VALUES (7000000000005), (7000000000007)) v(x); - avg | var_pop ----------------+--------- - 7000000000006 | 1 -(1 row) - --- SQL2003 binary aggregates -SELECT regr_count(b, a) FROM aggtest; - regr_count ------------- - 4 -(1 row) - -SELECT regr_sxx(b, a) FROM aggtest; - regr_sxx ----------- - 5099 -(1 row) - -SELECT regr_syy(b, a) FROM aggtest; - regr_syy ------------------- - 68756.2156939293 -(1 row) - -SELECT regr_sxy(b, a) FROM aggtest; - regr_sxy ------------------- - 2614.51582155004 -(1 row) - -SELECT regr_avgx(b, a), regr_avgy(b, a) FROM aggtest; - regr_avgx | regr_avgy ------------+------------------ - 49.5 | 107.943152273074 -(1 row) - -SELECT regr_r2(b, a) FROM aggtest; - regr_r2 --------------------- - 0.0194977982031803 -(1 row) - -SELECT regr_slope(b, a), regr_intercept(b, a) FROM aggtest; - regr_slope | regr_intercept --------------------+------------------ - 0.512750700441271 | 82.5619926012309 -(1 row) - -SELECT covar_pop(b, a), covar_samp(b, a) FROM aggtest; - covar_pop | covar_samp ------------------+------------------ - 653.62895538751 | 871.505273850014 -(1 row) - -SELECT corr(b, a) FROM aggtest; - corr -------------------- - 0.139634516517873 -(1 row) - --- check single-tuple behavior -SELECT covar_pop(1::float8,2::float8), covar_samp(3::float8,4::float8); - covar_pop | covar_samp ------------+------------ - 0 | -(1 row) - -SELECT covar_pop(1::float8,'inf'::float8), covar_samp(3::float8,'inf'::float8); - covar_pop | covar_samp ------------+------------ - NaN | -(1 row) - -SELECT covar_pop(1::float8,'nan'::float8), covar_samp(3::float8,'nan'::float8); - covar_pop | covar_samp ------------+------------ - NaN | -(1 row) - --- test accum and combine functions directly -CREATE TABLE regr_test (x float8, y float8); -INSERT INTO regr_test VALUES (10,150),(20,250),(30,350),(80,540),(100,200); -SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) -FROM regr_test WHERE x IN (10,20,30,80); - count | sum | regr_sxx | sum | regr_syy | regr_sxy --------+-----+----------+------+----------+---------- - 4 | 140 | 2900 | 1290 | 83075 | 15050 -(1 row) - -SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) -FROM regr_test; - count | sum | regr_sxx | sum | regr_syy | regr_sxy --------+-----+----------+------+----------+---------- - 5 | 240 | 6280 | 1490 | 95080 | 8680 -(1 row) - -SELECT float8_accum('{4,140,2900}'::float8[], 100); - float8_accum --------------- - {5,240,6280} -(1 row) - -SELECT float8_regr_accum('{4,140,2900,1290,83075,15050}'::float8[], 200, 100); - float8_regr_accum ------------------------------- - {5,240,6280,1490,95080,8680} -(1 row) - -SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) -FROM regr_test WHERE x IN (10,20,30); - count | sum | regr_sxx | sum | regr_syy | regr_sxy --------+-----+----------+-----+----------+---------- - 3 | 60 | 200 | 750 | 20000 | 2000 -(1 row) - -SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) -FROM regr_test WHERE x IN (80,100); - count | sum | regr_sxx | sum | regr_syy | regr_sxy --------+-----+----------+-----+----------+---------- - 2 | 180 | 200 | 740 | 57800 | -3400 -(1 row) - -SELECT float8_combine('{3,60,200}'::float8[], '{0,0,0}'::float8[]); - float8_combine ----------------- - {3,60,200} -(1 row) - -SELECT float8_combine('{0,0,0}'::float8[], '{2,180,200}'::float8[]); - float8_combine ----------------- - {2,180,200} -(1 row) - -SELECT float8_combine('{3,60,200}'::float8[], '{2,180,200}'::float8[]); - float8_combine ----------------- - {5,240,6280} -(1 row) - -SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], - '{0,0,0,0,0,0}'::float8[]); - float8_regr_combine ---------------------------- - {3,60,200,750,20000,2000} -(1 row) - -SELECT float8_regr_combine('{0,0,0,0,0,0}'::float8[], - '{2,180,200,740,57800,-3400}'::float8[]); - float8_regr_combine ------------------------------ - {2,180,200,740,57800,-3400} -(1 row) - -SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], - '{2,180,200,740,57800,-3400}'::float8[]); - float8_regr_combine ------------------------------- - {5,240,6280,1490,95080,8680} -(1 row) - -DROP TABLE regr_test; --- test count, distinct -SELECT count(four) AS cnt_1000 FROM onek; - cnt_1000 ----------- - 1000 -(1 row) - -SELECT count(DISTINCT four) AS cnt_4 FROM onek; - cnt_4 -------- - 4 -(1 row) - -select ten, count(*), sum(four) from onek -group by ten order by ten; - ten | count | sum ------+-------+----- - 0 | 100 | 100 - 1 | 100 | 200 - 2 | 100 | 100 - 3 | 100 | 200 - 4 | 100 | 100 - 5 | 100 | 200 - 6 | 100 | 100 - 7 | 100 | 200 - 8 | 100 | 100 - 9 | 100 | 200 -(10 rows) - -select ten, count(four), sum(DISTINCT four) from onek -group by ten order by ten; - ten | count | sum ------+-------+----- - 0 | 100 | 2 - 1 | 100 | 4 - 2 | 100 | 2 - 3 | 100 | 4 - 4 | 100 | 2 - 5 | 100 | 4 - 6 | 100 | 2 - 7 | 100 | 4 - 8 | 100 | 2 - 9 | 100 | 4 -(10 rows) - --- user-defined aggregates -SELECT newavg(four) AS avg_1 FROM onek; - avg_1 --------------------- - 1.5000000000000000 -(1 row) - -SELECT newsum(four) AS sum_1500 FROM onek; - sum_1500 ----------- - 1500 -(1 row) - -SELECT newcnt(four) AS cnt_1000 FROM onek; - cnt_1000 ----------- - 1000 -(1 row) - -SELECT newcnt(*) AS cnt_1000 FROM onek; - cnt_1000 ----------- - 1000 -(1 row) - -SELECT oldcnt(*) AS cnt_1000 FROM onek; - cnt_1000 ----------- - 1000 -(1 row) - -SELECT sum2(q1,q2) FROM int8_tbl; - sum2 -------------------- - 18271560493827981 -(1 row) - --- test for outer-level aggregates --- this should work -select ten, sum(distinct four) from onek a -group by ten -having exists (select 1 from onek b where sum(distinct a.four) = b.four); - ten | sum ------+----- - 0 | 2 - 2 | 2 - 4 | 2 - 6 | 2 - 8 | 2 -(5 rows) - --- this should fail because subquery has an agg of its own in WHERE -select ten, sum(distinct four) from onek a -group by ten -having exists (select 1 from onek b - where sum(distinct a.four + b.four) = b.four); -ERROR: aggregate functions are not allowed in WHERE -LINE 4: where sum(distinct a.four + b.four) = b.four)... - ^ --- Test handling of sublinks within outer-level aggregates. --- Per bug report from Daniel Grace. -select - (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1))) -from tenk1 o; - max ------- - 9999 -(1 row) - --- Test handling of Params within aggregate arguments in hashed aggregation. --- Per bug report from Jeevan Chalke. -explain (verbose, costs off) -select s1, s2, sm -from generate_series(1, 3) s1, - lateral (select s2, sum(s1 + s2) sm - from generate_series(1, 3) s2 group by s2) ss -order by 1, 2; - QUERY PLAN ------------------------------------------------------------------- - Sort - Output: s1.s1, s2.s2, (sum((s1.s1 + s2.s2))) - Sort Key: s1.s1, s2.s2 - -> Nested Loop - Output: s1.s1, s2.s2, (sum((s1.s1 + s2.s2))) - -> Function Scan on pg_catalog.generate_series s1 - Output: s1.s1 - Function Call: generate_series(1, 3) - -> HashAggregate - Output: s2.s2, sum((s1.s1 + s2.s2)) - Group Key: s2.s2 - -> Function Scan on pg_catalog.generate_series s2 - Output: s2.s2 - Function Call: generate_series(1, 3) -(14 rows) - -select s1, s2, sm -from generate_series(1, 3) s1, - lateral (select s2, sum(s1 + s2) sm - from generate_series(1, 3) s2 group by s2) ss -order by 1, 2; - s1 | s2 | sm -----+----+---- - 1 | 1 | 2 - 1 | 2 | 3 - 1 | 3 | 4 - 2 | 1 | 3 - 2 | 2 | 4 - 2 | 3 | 5 - 3 | 1 | 4 - 3 | 2 | 5 - 3 | 3 | 6 -(9 rows) - -explain (verbose, costs off) -select array(select sum(x+y) s - from generate_series(1,3) y group by y order by s) - from generate_series(1,3) x; - QUERY PLAN -------------------------------------------------------------------- - Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) - Function Call: generate_series(1, 3) - SubPlan 1 - -> Sort - Output: (sum((x.x + y.y))), y.y - Sort Key: (sum((x.x + y.y))) - -> HashAggregate - Output: sum((x.x + y.y)), y.y - Group Key: y.y - -> Function Scan on pg_catalog.generate_series y - Output: y.y - Function Call: generate_series(1, 3) -(13 rows) - -select array(select sum(x+y) s - from generate_series(1,3) y group by y order by s) - from generate_series(1,3) x; - array ---------- - {2,3,4} - {3,4,5} - {4,5,6} -(3 rows) - --- --- test for bitwise integer aggregates --- -CREATE TEMPORARY TABLE bitwise_test( - i2 INT2, - i4 INT4, - i8 INT8, - i INTEGER, - x INT2, - y BIT(4) -); --- empty case -SELECT - BIT_AND(i2) AS "?", - BIT_OR(i4) AS "?", - BIT_XOR(i8) AS "?" -FROM bitwise_test; - ? | ? | ? ----+---+--- - | | -(1 row) - -COPY bitwise_test FROM STDIN NULL 'null'; -SELECT - BIT_AND(i2) AS "1", - BIT_AND(i4) AS "1", - BIT_AND(i8) AS "1", - BIT_AND(i) AS "?", - BIT_AND(x) AS "0", - BIT_AND(y) AS "0100", - BIT_OR(i2) AS "7", - BIT_OR(i4) AS "7", - BIT_OR(i8) AS "7", - BIT_OR(i) AS "?", - BIT_OR(x) AS "7", - BIT_OR(y) AS "1101", - BIT_XOR(i2) AS "5", - BIT_XOR(i4) AS "5", - BIT_XOR(i8) AS "5", - BIT_XOR(i) AS "?", - BIT_XOR(x) AS "7", - BIT_XOR(y) AS "1101" -FROM bitwise_test; - 1 | 1 | 1 | ? | 0 | 0100 | 7 | 7 | 7 | ? | 7 | 1101 | 5 | 5 | 5 | ? | 7 | 1101 ----+---+---+---+---+------+---+---+---+---+---+------+---+---+---+---+---+------ - 1 | 1 | 1 | 1 | 0 | 0100 | 7 | 7 | 7 | 3 | 7 | 1101 | 5 | 5 | 5 | 2 | 7 | 1101 -(1 row) - --- --- test boolean aggregates --- --- first test all possible transition and final states -SELECT - -- boolean and transitions - -- null because strict - booland_statefunc(NULL, NULL) IS NULL AS "t", - booland_statefunc(TRUE, NULL) IS NULL AS "t", - booland_statefunc(FALSE, NULL) IS NULL AS "t", - booland_statefunc(NULL, TRUE) IS NULL AS "t", - booland_statefunc(NULL, FALSE) IS NULL AS "t", - -- and actual computations - booland_statefunc(TRUE, TRUE) AS "t", - NOT booland_statefunc(TRUE, FALSE) AS "t", - NOT booland_statefunc(FALSE, TRUE) AS "t", - NOT booland_statefunc(FALSE, FALSE) AS "t"; - t | t | t | t | t | t | t | t | t ----+---+---+---+---+---+---+---+--- - t | t | t | t | t | t | t | t | t -(1 row) - -SELECT - -- boolean or transitions - -- null because strict - boolor_statefunc(NULL, NULL) IS NULL AS "t", - boolor_statefunc(TRUE, NULL) IS NULL AS "t", - boolor_statefunc(FALSE, NULL) IS NULL AS "t", - boolor_statefunc(NULL, TRUE) IS NULL AS "t", - boolor_statefunc(NULL, FALSE) IS NULL AS "t", - -- actual computations - boolor_statefunc(TRUE, TRUE) AS "t", - boolor_statefunc(TRUE, FALSE) AS "t", - boolor_statefunc(FALSE, TRUE) AS "t", - NOT boolor_statefunc(FALSE, FALSE) AS "t"; - t | t | t | t | t | t | t | t | t ----+---+---+---+---+---+---+---+--- - t | t | t | t | t | t | t | t | t -(1 row) - -CREATE TEMPORARY TABLE bool_test( - b1 BOOL, - b2 BOOL, - b3 BOOL, - b4 BOOL); --- empty case -SELECT - BOOL_AND(b1) AS "n", - BOOL_OR(b3) AS "n" -FROM bool_test; - n | n ----+--- - | -(1 row) - -COPY bool_test FROM STDIN NULL 'null'; -SELECT - BOOL_AND(b1) AS "f", - BOOL_AND(b2) AS "t", - BOOL_AND(b3) AS "f", - BOOL_AND(b4) AS "n", - BOOL_AND(NOT b2) AS "f", - BOOL_AND(NOT b3) AS "t" -FROM bool_test; - f | t | f | n | f | t ----+---+---+---+---+--- - f | t | f | | f | t -(1 row) - -SELECT - EVERY(b1) AS "f", - EVERY(b2) AS "t", - EVERY(b3) AS "f", - EVERY(b4) AS "n", - EVERY(NOT b2) AS "f", - EVERY(NOT b3) AS "t" -FROM bool_test; - f | t | f | n | f | t ----+---+---+---+---+--- - f | t | f | | f | t -(1 row) - -SELECT - BOOL_OR(b1) AS "t", - BOOL_OR(b2) AS "t", - BOOL_OR(b3) AS "f", - BOOL_OR(b4) AS "n", - BOOL_OR(NOT b2) AS "f", - BOOL_OR(NOT b3) AS "t" -FROM bool_test; - t | t | f | n | f | t ----+---+---+---+---+--- - t | t | f | | f | t -(1 row) - --- --- Test cases that should be optimized into indexscans instead of --- the generic aggregate implementation. --- --- Basic cases -explain (costs off) - select min(unique1) from tenk1; - QUERY PLAN ------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 IS NOT NULL) -(5 rows) - -select min(unique1) from tenk1; - min ------ - 0 -(1 row) - -explain (costs off) - select max(unique1) from tenk1; - QUERY PLAN ---------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique1 on tenk1 - Index Cond: (unique1 IS NOT NULL) -(5 rows) - -select max(unique1) from tenk1; - max ------- - 9999 -(1 row) - -explain (costs off) - select max(unique1) from tenk1 where unique1 < 42; - QUERY PLAN ------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique1 on tenk1 - Index Cond: ((unique1 IS NOT NULL) AND (unique1 < 42)) -(5 rows) - -select max(unique1) from tenk1 where unique1 < 42; - max ------ - 41 -(1 row) - -explain (costs off) - select max(unique1) from tenk1 where unique1 > 42; - QUERY PLAN ------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique1 on tenk1 - Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42)) -(5 rows) - -select max(unique1) from tenk1 where unique1 > 42; - max ------- - 9999 -(1 row) - --- the planner may choose a generic aggregate here if parallel query is --- enabled, since that plan will be parallel safe and the "optimized" --- plan, which has almost identical cost, will not be. we want to test --- the optimized plan, so temporarily disable parallel query. -begin; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select max(unique1) from tenk1 where unique1 > 42000; - QUERY PLAN ---------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique1 on tenk1 - Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42000)) -(5 rows) - -select max(unique1) from tenk1 where unique1 > 42000; - max ------ - -(1 row) - -rollback; --- multi-column index (uses tenk1_thous_tenthous) -explain (costs off) - select max(tenthous) from tenk1 where thousand = 33; - QUERY PLAN ----------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) -(5 rows) - -select max(tenthous) from tenk1 where thousand = 33; - max ------- - 9033 -(1 row) - -explain (costs off) - select min(tenthous) from tenk1 where thousand = 33; - QUERY PLAN --------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) -(5 rows) - -select min(tenthous) from tenk1 where thousand = 33; - min ------ - 33 -(1 row) - --- check parameter propagation into an indexscan subquery -explain (costs off) - select f1, (select min(unique1) from tenk1 where unique1 > f1) AS gt - from int4_tbl; - QUERY PLAN ------------------------------------------------------------------------------------------ - Seq Scan on int4_tbl - SubPlan 2 - -> Result - InitPlan 1 (returns $1) - -> Limit - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: ((unique1 IS NOT NULL) AND (unique1 > int4_tbl.f1)) -(7 rows) - -select f1, (select min(unique1) from tenk1 where unique1 > f1) AS gt - from int4_tbl; - f1 | gt --------------+---- - 0 | 1 - 123456 | - -123456 | 0 - 2147483647 | - -2147483647 | 0 -(5 rows) - --- check some cases that were handled incorrectly in 8.3.0 -explain (costs off) - select distinct max(unique2) from tenk1; - QUERY PLAN ---------------------------------------------------------------------- - HashAggregate - Group Key: $0 - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique2 on tenk1 - Index Cond: (unique2 IS NOT NULL) - -> Result -(7 rows) - -select distinct max(unique2) from tenk1; - max ------- - 9999 -(1 row) - -explain (costs off) - select max(unique2) from tenk1 order by 1; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: ($0) - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique2 on tenk1 - Index Cond: (unique2 IS NOT NULL) - -> Result -(7 rows) - -select max(unique2) from tenk1 order by 1; - max ------- - 9999 -(1 row) - -explain (costs off) - select max(unique2) from tenk1 order by max(unique2); - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: ($0) - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique2 on tenk1 - Index Cond: (unique2 IS NOT NULL) - -> Result -(7 rows) - -select max(unique2) from tenk1 order by max(unique2); - max ------- - 9999 -(1 row) - -explain (costs off) - select max(unique2) from tenk1 order by max(unique2)+1; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: (($0 + 1)) - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique2 on tenk1 - Index Cond: (unique2 IS NOT NULL) - -> Result -(7 rows) - -select max(unique2) from tenk1 order by max(unique2)+1; - max ------- - 9999 -(1 row) - -explain (costs off) - select max(unique2), generate_series(1,3) as g from tenk1 order by g desc; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: (generate_series(1, 3)) DESC - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan Backward using tenk1_unique2 on tenk1 - Index Cond: (unique2 IS NOT NULL) - -> ProjectSet - -> Result -(8 rows) - -select max(unique2), generate_series(1,3) as g from tenk1 order by g desc; - max | g -------+--- - 9999 | 3 - 9999 | 2 - 9999 | 1 -(3 rows) - --- interesting corner case: constant gets optimized into a seqscan -explain (costs off) - select max(100) from tenk1; - QUERY PLAN ----------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Result - One-Time Filter: (100 IS NOT NULL) - -> Seq Scan on tenk1 -(6 rows) - -select max(100) from tenk1; - max ------ - 100 -(1 row) - --- try it on an inheritance tree -create table minmaxtest(f1 int); -create table minmaxtest1() inherits (minmaxtest); -create table minmaxtest2() inherits (minmaxtest); -create table minmaxtest3() inherits (minmaxtest); -create index minmaxtesti on minmaxtest(f1); -create index minmaxtest1i on minmaxtest1(f1); -create index minmaxtest2i on minmaxtest2(f1 desc); -create index minmaxtest3i on minmaxtest3(f1) where f1 is not null; -insert into minmaxtest values(11), (12); -insert into minmaxtest1 values(13), (14); -insert into minmaxtest2 values(15), (16); -insert into minmaxtest3 values(17), (18); -explain (costs off) - select min(f1), max(f1) from minmaxtest; - QUERY PLAN ---------------------------------------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Merge Append - Sort Key: minmaxtest.f1 - -> Index Only Scan using minmaxtesti on minmaxtest minmaxtest_1 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest1i on minmaxtest1 minmaxtest_2 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) - -> Limit - -> Merge Append - Sort Key: minmaxtest_5.f1 DESC - -> Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_6 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest_7 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest_8 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest_9 -(23 rows) - -select min(f1), max(f1) from minmaxtest; - min | max ------+----- - 11 | 18 -(1 row) - --- DISTINCT doesn't do anything useful here, but it shouldn't fail -explain (costs off) - select distinct min(f1), max(f1) from minmaxtest; - QUERY PLAN ---------------------------------------------------------------------------------------------- - Unique - InitPlan 1 (returns $0) - -> Limit - -> Merge Append - Sort Key: minmaxtest.f1 - -> Index Only Scan using minmaxtesti on minmaxtest minmaxtest_1 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest1i on minmaxtest1 minmaxtest_2 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) - -> Limit - -> Merge Append - Sort Key: minmaxtest_5.f1 DESC - -> Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_6 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest_7 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest_8 - Index Cond: (f1 IS NOT NULL) - -> Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest_9 - -> Sort - Sort Key: ($0), ($1) - -> Result -(26 rows) - -select distinct min(f1), max(f1) from minmaxtest; - min | max ------+----- - 11 | 18 -(1 row) - -drop table minmaxtest cascade; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table minmaxtest1 -drop cascades to table minmaxtest2 -drop cascades to table minmaxtest3 --- check for correct detection of nested-aggregate errors -select max(min(unique1)) from tenk1; -ERROR: aggregate function calls cannot be nested -LINE 1: select max(min(unique1)) from tenk1; - ^ -select (select max(min(unique1)) from int8_tbl) from tenk1; -ERROR: aggregate function calls cannot be nested -LINE 1: select (select max(min(unique1)) from int8_tbl) from tenk1; - ^ --- --- Test removal of redundant GROUP BY columns --- -create temp table t1 (a int, b int, c int, d int, primary key (a, b)); -create temp table t2 (x int, y int, z int, primary key (x, y)); -create temp table t3 (a int, b int, c int, primary key(a, b) deferrable); --- Non-primary-key columns can be removed from GROUP BY -explain (costs off) select * from t1 group by a,b,c,d; - QUERY PLAN ----------------------- - HashAggregate - Group Key: a, b - -> Seq Scan on t1 -(3 rows) - --- No removal can happen if the complete PK is not present in GROUP BY -explain (costs off) select a,c from t1 group by a,c,d; - QUERY PLAN ----------------------- - HashAggregate - Group Key: a, c, d - -> Seq Scan on t1 -(3 rows) - --- Test removal across multiple relations -explain (costs off) select * -from t1 inner join t2 on t1.a = t2.x and t1.b = t2.y -group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.y,t2.z; - QUERY PLAN ------------------------------------------------------- - HashAggregate - Group Key: t1.a, t1.b, t2.x, t2.y - -> Hash Join - Hash Cond: ((t2.x = t1.a) AND (t2.y = t1.b)) - -> Seq Scan on t2 - -> Hash - -> Seq Scan on t1 -(7 rows) - --- Test case where t1 can be optimized but not t2 -explain (costs off) select t1.*,t2.x,t2.z -from t1 inner join t2 on t1.a = t2.x and t1.b = t2.y -group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.z; - QUERY PLAN ------------------------------------------------------- - HashAggregate - Group Key: t1.a, t1.b, t2.x, t2.z - -> Hash Join - Hash Cond: ((t2.x = t1.a) AND (t2.y = t1.b)) - -> Seq Scan on t2 - -> Hash - -> Seq Scan on t1 -(7 rows) - --- Cannot optimize when PK is deferrable -explain (costs off) select * from t3 group by a,b,c; - QUERY PLAN ----------------------- - HashAggregate - Group Key: a, b, c - -> Seq Scan on t3 -(3 rows) - -create temp table t1c () inherits (t1); --- Ensure we don't remove any columns when t1 has a child table -explain (costs off) select * from t1 group by a,b,c,d; - QUERY PLAN -------------------------------------- - HashAggregate - Group Key: t1.a, t1.b, t1.c, t1.d - -> Append - -> Seq Scan on t1 t1_1 - -> Seq Scan on t1c t1_2 -(5 rows) - --- Okay to remove columns if we're only querying the parent. -explain (costs off) select * from only t1 group by a,b,c,d; - QUERY PLAN ----------------------- - HashAggregate - Group Key: a, b - -> Seq Scan on t1 -(3 rows) - -create temp table p_t1 ( - a int, - b int, - c int, - d int, - primary key(a,b) -) partition by list(a); -create temp table p_t1_1 partition of p_t1 for values in(1); -create temp table p_t1_2 partition of p_t1 for values in(2); --- Ensure we can remove non-PK columns for partitioned tables. -explain (costs off) select * from p_t1 group by a,b,c,d; - QUERY PLAN --------------------------------- - HashAggregate - Group Key: p_t1.a, p_t1.b - -> Append - -> Seq Scan on p_t1_1 - -> Seq Scan on p_t1_2 -(5 rows) - -drop table t1 cascade; -NOTICE: drop cascades to table t1c -drop table t2; -drop table t3; -drop table p_t1; --- --- Test GROUP BY matching of join columns that are type-coerced due to USING --- -create temp table t1(f1 int, f2 bigint); -create temp table t2(f1 bigint, f22 bigint); -select f1 from t1 left join t2 using (f1) group by f1; - f1 ----- -(0 rows) - -select f1 from t1 left join t2 using (f1) group by t1.f1; - f1 ----- -(0 rows) - -select t1.f1 from t1 left join t2 using (f1) group by t1.f1; - f1 ----- -(0 rows) - --- only this one should fail: -select t1.f1 from t1 left join t2 using (f1) group by f1; -ERROR: column "t1.f1" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: select t1.f1 from t1 left join t2 using (f1) group by f1; - ^ -drop table t1, t2; --- --- Test combinations of DISTINCT and/or ORDER BY --- -select array_agg(a order by b) - from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); - array_agg ------------ - {3,4,2,1} -(1 row) - -select array_agg(a order by a) - from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); - array_agg ------------ - {1,2,3,4} -(1 row) - -select array_agg(a order by a desc) - from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); - array_agg ------------ - {4,3,2,1} -(1 row) - -select array_agg(b order by a desc) - from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); - array_agg ------------ - {2,1,3,4} -(1 row) - -select array_agg(distinct a) - from (values (1),(2),(1),(3),(null),(2)) v(a); - array_agg --------------- - {1,2,3,NULL} -(1 row) - -select array_agg(distinct a order by a) - from (values (1),(2),(1),(3),(null),(2)) v(a); - array_agg --------------- - {1,2,3,NULL} -(1 row) - -select array_agg(distinct a order by a desc) - from (values (1),(2),(1),(3),(null),(2)) v(a); - array_agg --------------- - {NULL,3,2,1} -(1 row) - -select array_agg(distinct a order by a desc nulls last) - from (values (1),(2),(1),(3),(null),(2)) v(a); - array_agg --------------- - {3,2,1,NULL} -(1 row) - --- multi-arg aggs, strict/nonstrict, distinct/order by -select aggfstr(a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); - aggfstr ---------------------------------------- - {"(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select aggfns(a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); - aggfns ------------------------------------------------ - {"(1,3,foo)","(0,,)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select aggfstr(distinct a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; - aggfstr ---------------------------------------- - {"(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select aggfns(distinct a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; - aggfns ------------------------------------------------ - {"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select aggfstr(distinct a,b,c order by b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; - aggfstr ---------------------------------------- - {"(3,1,baz)","(2,2,bar)","(1,3,foo)"} -(1 row) - -select aggfns(distinct a,b,c order by b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; - aggfns ------------------------------------------------ - {"(3,1,baz)","(2,2,bar)","(1,3,foo)","(0,,)"} -(1 row) - --- test specific code paths -select aggfns(distinct a,a,c order by c using ~<~,a) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; - aggfns ------------------------------------------------- - {"(2,2,bar)","(3,3,baz)","(1,1,foo)","(0,0,)"} -(1 row) - -select aggfns(distinct a,a,c order by c using ~<~) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; - aggfns ------------------------------------------------- - {"(2,2,bar)","(3,3,baz)","(1,1,foo)","(0,0,)"} -(1 row) - -select aggfns(distinct a,a,c order by a) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; - aggfns ------------------------------------------------- - {"(0,0,)","(1,1,foo)","(2,2,bar)","(3,3,baz)"} -(1 row) - -select aggfns(distinct a,b,c order by a,c using ~<~,b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; - aggfns ------------------------------------------------ - {"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - --- check node I/O via view creation and usage, also deparsing logic -create view agg_view1 as - select aggfns(a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); -select * from agg_view1; - aggfns ------------------------------------------------ - {"(1,3,foo)","(0,,)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); -(1 row) - -create or replace view agg_view1 as - select aggfns(distinct a,b,c) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; -select * from agg_view1; - aggfns ------------------------------------------------ - {"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(DISTINCT v.a, v.b, v.c) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c),+ - generate_series(1, 3) i(i); -(1 row) - -create or replace view agg_view1 as - select aggfns(distinct a,b,c order by b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,3) i; -select * from agg_view1; - aggfns ------------------------------------------------ - {"(3,1,baz)","(2,2,bar)","(1,3,foo)","(0,,)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(DISTINCT v.a, v.b, v.c ORDER BY v.b) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c),+ - generate_series(1, 3) i(i); -(1 row) - -create or replace view agg_view1 as - select aggfns(a,b,c order by b+1) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); -select * from agg_view1; - aggfns ------------------------------------------------ - {"(3,1,baz)","(2,2,bar)","(1,3,foo)","(0,,)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); -(1 row) - -create or replace view agg_view1 as - select aggfns(a,a,c order by b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); -select * from agg_view1; - aggfns ------------------------------------------------- - {"(3,3,baz)","(2,2,bar)","(1,1,foo)","(0,0,)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); -(1 row) - -create or replace view agg_view1 as - select aggfns(a,b,c order by c using ~<~) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); -select * from agg_view1; - aggfns ------------------------------------------------ - {"(2,2,bar)","(3,1,baz)","(1,3,foo)","(0,,)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); -(1 row) - -create or replace view agg_view1 as - select aggfns(distinct a,b,c order by a,c using ~<~,b) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; -select * from agg_view1; - aggfns ------------------------------------------------ - {"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"} -(1 row) - -select pg_get_viewdef('agg_view1'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(DISTINCT v.a, v.b, v.c ORDER BY v.a, v.c USING ~<~ NULLS LAST, v.b) AS aggfns + - FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c),+ - generate_series(1, 2) i(i); -(1 row) - -drop view agg_view1; --- incorrect DISTINCT usage errors -select aggfns(distinct a,b,c order by i) - from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select aggfns(distinct a,b,c order by i) - ^ -select aggfns(distinct a,b,c order by a,b+1) - from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select aggfns(distinct a,b,c order by a,b+1) - ^ -select aggfns(distinct a,b,c order by a,b,i,c) - from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select aggfns(distinct a,b,c order by a,b,i,c) - ^ -select aggfns(distinct a,a,c order by a,b) - from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select aggfns(distinct a,a,c order by a,b) - ^ --- string_agg tests -select string_agg(a,',') from (values('aaaa'),('bbbb'),('cccc')) g(a); - string_agg ----------------- - aaaa,bbbb,cccc -(1 row) - -select string_agg(a,',') from (values('aaaa'),(null),('bbbb'),('cccc')) g(a); - string_agg ----------------- - aaaa,bbbb,cccc -(1 row) - -select string_agg(a,'AB') from (values(null),(null),('bbbb'),('cccc')) g(a); - string_agg ------------- - bbbbABcccc -(1 row) - -select string_agg(a,',') from (values(null),(null)) g(a); - string_agg ------------- - -(1 row) - --- check some implicit casting cases, as per bug #5564 -select string_agg(distinct f1, ',' order by f1) from varchar_tbl; -- ok - string_agg ------------- - a,ab,abcd -(1 row) - -select string_agg(distinct f1::text, ',' order by f1) from varchar_tbl; -- not ok -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select string_agg(distinct f1::text, ',' order by f1) from v... - ^ -select string_agg(distinct f1, ',' order by f1::text) from varchar_tbl; -- not ok -ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list -LINE 1: select string_agg(distinct f1, ',' order by f1::text) from v... - ^ -select string_agg(distinct f1::text, ',' order by f1::text) from varchar_tbl; -- ok - string_agg ------------- - a,ab,abcd -(1 row) - --- string_agg bytea tests -create table bytea_test_table(v bytea); -select string_agg(v, '') from bytea_test_table; - string_agg ------------- - -(1 row) - -insert into bytea_test_table values(decode('ff','hex')); -select string_agg(v, '') from bytea_test_table; - string_agg ------------- - \xff -(1 row) - -insert into bytea_test_table values(decode('aa','hex')); -select string_agg(v, '') from bytea_test_table; - string_agg ------------- - \xffaa -(1 row) - -select string_agg(v, NULL) from bytea_test_table; - string_agg ------------- - \xffaa -(1 row) - -select string_agg(v, decode('ee', 'hex')) from bytea_test_table; - string_agg ------------- - \xffeeaa -(1 row) - -drop table bytea_test_table; --- FILTER tests -select min(unique1) filter (where unique1 > 100) from tenk1; - min ------ - 101 -(1 row) - -select sum(1/ten) filter (where ten > 0) from tenk1; - sum ------- - 1000 -(1 row) - -select ten, sum(distinct four) filter (where four::text ~ '123') from onek a -group by ten; - ten | sum ------+----- - 0 | - 1 | - 2 | - 3 | - 4 | - 5 | - 6 | - 7 | - 8 | - 9 | -(10 rows) - -select ten, sum(distinct four) filter (where four > 10) from onek a -group by ten -having exists (select 1 from onek b where sum(distinct a.four) = b.four); - ten | sum ------+----- - 0 | - 2 | - 4 | - 6 | - 8 | -(5 rows) - -select max(foo COLLATE "C") filter (where (bar collate "POSIX") > '0') -from (values ('a', 'b')) AS v(foo,bar); - max ------ - a -(1 row) - --- outer reference in FILTER (PostgreSQL extension) -select (select count(*) - from (values (1)) t0(inner_c)) -from (values (2),(3)) t1(outer_c); -- inner query is aggregation query - count -------- - 1 - 1 -(2 rows) - -select (select count(*) filter (where outer_c <> 0) - from (values (1)) t0(inner_c)) -from (values (2),(3)) t1(outer_c); -- outer query is aggregation query - count -------- - 2 -(1 row) - -select (select count(inner_c) filter (where outer_c <> 0) - from (values (1)) t0(inner_c)) -from (values (2),(3)) t1(outer_c); -- inner query is aggregation query - count -------- - 1 - 1 -(2 rows) - -select - (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1)) - filter (where o.unique1 < 10)) -from tenk1 o; -- outer query is aggregation query - max ------- - 9998 -(1 row) - --- subquery in FILTER clause (PostgreSQL extension) -select sum(unique1) FILTER (WHERE - unique1 IN (SELECT unique1 FROM onek where unique1 < 100)) FROM tenk1; - sum ------- - 4950 -(1 row) - --- exercise lots of aggregate parts with FILTER -select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1) - from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), - generate_series(1,2) i; - aggfns ---------------------------- - {"(2,2,bar)","(3,1,baz)"} -(1 row) - --- check handling of bare boolean Var in FILTER -select max(0) filter (where b1) from bool_test; - max ------ - 0 -(1 row) - -select (select max(0) filter (where b1)) from bool_test; - max ------ - 0 -(1 row) - --- check for correct detection of nested-aggregate errors in FILTER -select max(unique1) filter (where sum(ten) > 0) from tenk1; -ERROR: aggregate functions are not allowed in FILTER -LINE 1: select max(unique1) filter (where sum(ten) > 0) from tenk1; - ^ -select (select max(unique1) filter (where sum(ten) > 0) from int8_tbl) from tenk1; -ERROR: aggregate function calls cannot be nested -LINE 1: select (select max(unique1) filter (where sum(ten) > 0) from... - ^ -select max(unique1) filter (where bool_or(ten > 0)) from tenk1; -ERROR: aggregate functions are not allowed in FILTER -LINE 1: select max(unique1) filter (where bool_or(ten > 0)) from ten... - ^ -select (select max(unique1) filter (where bool_or(ten > 0)) from int8_tbl) from tenk1; -ERROR: aggregate function calls cannot be nested -LINE 1: select (select max(unique1) filter (where bool_or(ten > 0)) ... - ^ --- ordered-set aggregates -select p, percentile_cont(p) within group (order by x::float8) -from generate_series(1,5) x, - (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) -group by p order by p; - p | percentile_cont -------+----------------- - 0 | 1 - 0.1 | 1.4 - 0.25 | 2 - 0.4 | 2.6 - 0.5 | 3 - 0.6 | 3.4 - 0.75 | 4 - 0.9 | 4.6 - 1 | 5 -(9 rows) - -select p, percentile_cont(p order by p) within group (order by x) -- error -from generate_series(1,5) x, - (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) -group by p order by p; -ERROR: cannot use multiple ORDER BY clauses with WITHIN GROUP -LINE 1: select p, percentile_cont(p order by p) within group (order ... - ^ -select p, sum() within group (order by x::float8) -- error -from generate_series(1,5) x, - (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) -group by p order by p; -ERROR: sum is not an ordered-set aggregate, so it cannot have WITHIN GROUP -LINE 1: select p, sum() within group (order by x::float8) - ^ -select p, percentile_cont(p,p) -- error -from generate_series(1,5) x, - (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) -group by p order by p; -ERROR: WITHIN GROUP is required for ordered-set aggregate percentile_cont -LINE 1: select p, percentile_cont(p,p) - ^ -select percentile_cont(0.5) within group (order by b) from aggtest; - percentile_cont ------------------- - 53.4485001564026 -(1 row) - -select percentile_cont(0.5) within group (order by b), sum(b) from aggtest; - percentile_cont | sum -------------------+--------- - 53.4485001564026 | 431.773 -(1 row) - -select percentile_cont(0.5) within group (order by thousand) from tenk1; - percentile_cont ------------------ - 499.5 -(1 row) - -select percentile_disc(0.5) within group (order by thousand) from tenk1; - percentile_disc ------------------ - 499 -(1 row) - -select rank(3) within group (order by x) -from (values (1),(1),(2),(2),(3),(3),(4)) v(x); - rank ------- - 5 -(1 row) - -select cume_dist(3) within group (order by x) -from (values (1),(1),(2),(2),(3),(3),(4)) v(x); - cume_dist ------------ - 0.875 -(1 row) - -select percent_rank(3) within group (order by x) -from (values (1),(1),(2),(2),(3),(3),(4),(5)) v(x); - percent_rank --------------- - 0.5 -(1 row) - -select dense_rank(3) within group (order by x) -from (values (1),(1),(2),(2),(3),(3),(4)) v(x); - dense_rank ------------- - 3 -(1 row) - -select percentile_disc(array[0,0.1,0.25,0.5,0.75,0.9,1]) within group (order by thousand) -from tenk1; - percentile_disc ----------------------------- - {0,99,249,499,749,899,999} -(1 row) - -select percentile_cont(array[0,0.25,0.5,0.75,1]) within group (order by thousand) -from tenk1; - percentile_cont ------------------------------ - {0,249.75,499.5,749.25,999} -(1 row) - -select percentile_disc(array[[null,1,0.5],[0.75,0.25,null]]) within group (order by thousand) -from tenk1; - percentile_disc ---------------------------------- - {{NULL,999,499},{749,249,NULL}} -(1 row) - -select percentile_cont(array[0,1,0.25,0.75,0.5,1,0.3,0.32,0.35,0.38,0.4]) within group (order by x) -from generate_series(1,6) x; - percentile_cont ------------------------------------------- - {1,6,2.25,4.75,3.5,6,2.5,2.6,2.75,2.9,3} -(1 row) - -select ten, mode() within group (order by string4) from tenk1 group by ten; - ten | mode ------+-------- - 0 | HHHHxx - 1 | OOOOxx - 2 | VVVVxx - 3 | OOOOxx - 4 | HHHHxx - 5 | HHHHxx - 6 | OOOOxx - 7 | AAAAxx - 8 | VVVVxx - 9 | VVVVxx -(10 rows) - -select percentile_disc(array[0.25,0.5,0.75]) within group (order by x) -from unnest('{fred,jim,fred,jack,jill,fred,jill,jim,jim,sheila,jim,sheila}'::text[]) u(x); - percentile_disc ------------------ - {fred,jill,jim} -(1 row) - --- check collation propagates up in suitable cases: -select pg_collation_for(percentile_disc(1) within group (order by x collate "POSIX")) - from (values ('fred'),('jim')) v(x); - pg_collation_for ------------------- - "POSIX" -(1 row) - --- ordered-set aggs created with CREATE AGGREGATE -select test_rank(3) within group (order by x) -from (values (1),(1),(2),(2),(3),(3),(4)) v(x); - test_rank ------------ - 5 -(1 row) - -select test_percentile_disc(0.5) within group (order by thousand) from tenk1; - test_percentile_disc ----------------------- - 499 -(1 row) - --- ordered-set aggs can't use ungrouped vars in direct args: -select rank(x) within group (order by x) from generate_series(1,5) x; -ERROR: column "x.x" must appear in the GROUP BY clause or be used in an aggregate function -LINE 1: select rank(x) within group (order by x) from generate_serie... - ^ -DETAIL: Direct arguments of an ordered-set aggregate must use only grouped columns. --- outer-level agg can't use a grouped arg of a lower level, either: -select array(select percentile_disc(a) within group (order by x) - from (values (0.3),(0.7)) v(a) group by a) - from generate_series(1,5) g(x); -ERROR: outer-level aggregate cannot contain a lower-level variable in its direct arguments -LINE 1: select array(select percentile_disc(a) within group (order b... - ^ --- agg in the direct args is a grouping violation, too: -select rank(sum(x)) within group (order by x) from generate_series(1,5) x; -ERROR: aggregate function calls cannot be nested -LINE 1: select rank(sum(x)) within group (order by x) from generate_... - ^ --- hypothetical-set type unification and argument-count failures: -select rank(3) within group (order by x) from (values ('fred'),('jim')) v(x); -ERROR: WITHIN GROUP types text and integer cannot be matched -LINE 1: select rank(3) within group (order by x) from (values ('fred... - ^ -select rank(3) within group (order by stringu1,stringu2) from tenk1; -ERROR: function rank(integer, name, name) does not exist -LINE 1: select rank(3) within group (order by stringu1,stringu2) fro... - ^ -HINT: To use the hypothetical-set aggregate rank, the number of hypothetical direct arguments (here 1) must match the number of ordering columns (here 2). -select rank('fred') within group (order by x) from generate_series(1,5) x; -ERROR: invalid input syntax for type integer: "fred" -LINE 1: select rank('fred') within group (order by x) from generate_... - ^ -select rank('adam'::text collate "C") within group (order by x collate "POSIX") - from (values ('fred'),('jim')) v(x); -ERROR: collation mismatch between explicit collations "C" and "POSIX" -LINE 1: ...adam'::text collate "C") within group (order by x collate "P... - ^ --- hypothetical-set type unification successes: -select rank('adam'::varchar) within group (order by x) from (values ('fred'),('jim')) v(x); - rank ------- - 1 -(1 row) - -select rank('3') within group (order by x) from generate_series(1,5) x; - rank ------- - 3 -(1 row) - --- divide by zero check -select percent_rank(0) within group (order by x) from generate_series(1,0) x; - percent_rank --------------- - 0 -(1 row) - --- deparse and multiple features: -create view aggordview1 as -select ten, - percentile_disc(0.5) within group (order by thousand) as p50, - percentile_disc(0.5) within group (order by thousand) filter (where hundred=1) as px, - rank(5,'AZZZZ',50) within group (order by hundred, string4 desc, hundred) - from tenk1 - group by ten order by ten; -select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; -(1 row) - -select * from aggordview1 order by ten; - ten | p50 | px | rank ------+-----+-----+------ - 0 | 490 | | 101 - 1 | 491 | 401 | 101 - 2 | 492 | | 101 - 3 | 493 | | 101 - 4 | 494 | | 101 - 5 | 495 | | 67 - 6 | 496 | | 1 - 7 | 497 | | 1 - 8 | 498 | | 1 - 9 | 499 | | 1 -(10 rows) - -drop view aggordview1; --- variadic aggregates -select least_agg(q1,q2) from int8_tbl; - least_agg -------------------- - -4567890123456789 -(1 row) - -select least_agg(variadic array[q1,q2]) from int8_tbl; - least_agg -------------------- - -4567890123456789 -(1 row) - -select cleast_agg(q1,q2) from int8_tbl; - cleast_agg -------------------- - -4567890123456789 -(1 row) - -select cleast_agg(4.5,f1) from int4_tbl; - cleast_agg -------------- - -2147483647 -(1 row) - -select cleast_agg(variadic array[4.5,f1]) from int4_tbl; - cleast_agg -------------- - -2147483647 -(1 row) - -select pg_typeof(cleast_agg(variadic array[4.5,f1])) from int4_tbl; - pg_typeof ------------ - numeric -(1 row) - --- test aggregates with common transition functions share the same states -begin work; -create type avg_state as (total bigint, count bigint); -create or replace function avg_transfn(state avg_state, n int) returns avg_state as -$$ -declare new_state avg_state; -begin - raise notice 'avg_transfn called with %', n; - if state is null then - if n is not null then - new_state.total := n; - new_state.count := 1; - return new_state; - end if; - return null; - elsif n is not null then - state.total := state.total + n; - state.count := state.count + 1; - return state; - end if; - - return null; -end -$$ language plpgsql; -create function avg_finalfn(state avg_state) returns int4 as -$$ -begin - if state is null then - return NULL; - else - return state.total / state.count; - end if; -end -$$ language plpgsql; -create function sum_finalfn(state avg_state) returns int4 as -$$ -begin - if state is null then - return NULL; - else - return state.total; - end if; -end -$$ language plpgsql; -create aggregate my_avg(int4) -( - stype = avg_state, - sfunc = avg_transfn, - finalfunc = avg_finalfn -); -create aggregate my_sum(int4) -( - stype = avg_state, - sfunc = avg_transfn, - finalfunc = sum_finalfn -); --- aggregate state should be shared as aggs are the same. -select my_avg(one),my_avg(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 - my_avg | my_avg ---------+-------- - 2 | 2 -(1 row) - --- aggregate state should be shared as transfn is the same for both aggs. -select my_avg(one),my_sum(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 - my_avg | my_sum ---------+-------- - 2 | 4 -(1 row) - --- same as previous one, but with DISTINCT, which requires sorting the input. -select my_avg(distinct one),my_sum(distinct one) from (values(1),(3),(1)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 - my_avg | my_sum ---------+-------- - 2 | 4 -(1 row) - --- shouldn't share states due to the distinctness not matching. -select my_avg(distinct one),my_sum(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 - my_avg | my_sum ---------+-------- - 2 | 4 -(1 row) - --- shouldn't share states due to the filter clause not matching. -select my_avg(one) filter (where one > 1),my_sum(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 -NOTICE: avg_transfn called with 3 - my_avg | my_sum ---------+-------- - 3 | 4 -(1 row) - --- this should not share the state due to different input columns. -select my_avg(one),my_sum(two) from (values(1,2),(3,4)) t(one,two); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 2 -NOTICE: avg_transfn called with 3 -NOTICE: avg_transfn called with 4 - my_avg | my_sum ---------+-------- - 2 | 6 -(1 row) - --- exercise cases where OSAs share state -select - percentile_cont(0.5) within group (order by a), - percentile_disc(0.5) within group (order by a) -from (values(1::float8),(3),(5),(7)) t(a); - percentile_cont | percentile_disc ------------------+----------------- - 4 | 3 -(1 row) - -select - percentile_cont(0.25) within group (order by a), - percentile_disc(0.5) within group (order by a) -from (values(1::float8),(3),(5),(7)) t(a); - percentile_cont | percentile_disc ------------------+----------------- - 2.5 | 3 -(1 row) - --- these can't share state currently -select - rank(4) within group (order by a), - dense_rank(4) within group (order by a) -from (values(1),(3),(5),(7)) t(a); - rank | dense_rank -------+------------ - 3 | 3 -(1 row) - --- test that aggs with the same sfunc and initcond share the same agg state -create aggregate my_sum_init(int4) -( - stype = avg_state, - sfunc = avg_transfn, - finalfunc = sum_finalfn, - initcond = '(10,0)' -); -create aggregate my_avg_init(int4) -( - stype = avg_state, - sfunc = avg_transfn, - finalfunc = avg_finalfn, - initcond = '(10,0)' -); -create aggregate my_avg_init2(int4) -( - stype = avg_state, - sfunc = avg_transfn, - finalfunc = avg_finalfn, - initcond = '(4,0)' -); --- state should be shared if INITCONDs are matching -select my_sum_init(one),my_avg_init(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 - my_sum_init | my_avg_init --------------+------------- - 14 | 7 -(1 row) - --- Varying INITCONDs should cause the states not to be shared. -select my_sum_init(one),my_avg_init2(one) from (values(1),(3)) t(one); -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 3 -NOTICE: avg_transfn called with 3 - my_sum_init | my_avg_init2 --------------+-------------- - 14 | 4 -(1 row) - -rollback; --- test aggregate state sharing to ensure it works if one aggregate has a --- finalfn and the other one has none. -begin work; -create or replace function sum_transfn(state int4, n int4) returns int4 as -$$ -declare new_state int4; -begin - raise notice 'sum_transfn called with %', n; - if state is null then - if n is not null then - new_state := n; - return new_state; - end if; - return null; - elsif n is not null then - state := state + n; - return state; - end if; - - return null; -end -$$ language plpgsql; -create function halfsum_finalfn(state int4) returns int4 as -$$ -begin - if state is null then - return NULL; - else - return state / 2; - end if; -end -$$ language plpgsql; -create aggregate my_sum(int4) -( - stype = int4, - sfunc = sum_transfn -); -create aggregate my_half_sum(int4) -( - stype = int4, - sfunc = sum_transfn, - finalfunc = halfsum_finalfn -); --- Agg state should be shared even though my_sum has no finalfn -select my_sum(one),my_half_sum(one) from (values(1),(2),(3),(4)) t(one); -NOTICE: sum_transfn called with 1 -NOTICE: sum_transfn called with 2 -NOTICE: sum_transfn called with 3 -NOTICE: sum_transfn called with 4 - my_sum | my_half_sum ---------+------------- - 10 | 5 -(1 row) - -rollback; --- test that the aggregate transition logic correctly handles --- transition / combine functions returning NULL --- First test the case of a normal transition function returning NULL -BEGIN; -CREATE FUNCTION balkifnull(int8, int4) -RETURNS int8 -STRICT -LANGUAGE plpgsql AS $$ -BEGIN - IF $1 IS NULL THEN - RAISE 'erroneously called with NULL argument'; - END IF; - RETURN NULL; -END$$; -CREATE AGGREGATE balk(int4) -( - SFUNC = balkifnull(int8, int4), - STYPE = int8, - PARALLEL = SAFE, - INITCOND = '0' -); -SELECT balk(hundred) FROM tenk1; - balk ------- - -(1 row) - -ROLLBACK; --- Secondly test the case of a parallel aggregate combiner function --- returning NULL. For that use normal transition function, but a --- combiner function returning NULL. -BEGIN; -CREATE FUNCTION balkifnull(int8, int8) -RETURNS int8 -PARALLEL SAFE -STRICT -LANGUAGE plpgsql AS $$ -BEGIN - IF $1 IS NULL THEN - RAISE 'erroneously called with NULL argument'; - END IF; - RETURN NULL; -END$$; -CREATE AGGREGATE balk(int4) -( - SFUNC = int4_sum(int8, int4), - STYPE = int8, - COMBINEFUNC = balkifnull(int8, int8), - PARALLEL = SAFE, - INITCOND = '0' -); --- force use of parallelism -ALTER TABLE tenk1 set (parallel_workers = 4); -SET LOCAL parallel_setup_cost=0; -SET LOCAL max_parallel_workers_per_gather=4; -EXPLAIN (COSTS OFF) SELECT balk(hundred) FROM tenk1; - QUERY PLAN -------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Only Scan using tenk1_hundred on tenk1 -(5 rows) - -SELECT balk(hundred) FROM tenk1; - balk ------- - -(1 row) - -ROLLBACK; --- test coverage for aggregate combine/serial/deserial functions -BEGIN; -SET parallel_setup_cost = 0; -SET parallel_tuple_cost = 0; -SET min_parallel_table_scan_size = 0; -SET max_parallel_workers_per_gather = 4; -SET parallel_leader_participation = off; -SET enable_indexonlyscan = off; --- variance(int4) covers numeric_poly_combine --- sum(int8) covers int8_avg_combine --- regr_count(float8, float8) covers int8inc_float8_float8 and aggregates with > 1 arg -EXPLAIN (COSTS OFF, VERBOSE) -SELECT variance(unique1::int4), sum(unique1::int8), regr_count(unique1::float8, unique1::float8) -FROM (SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1) u; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Finalize Aggregate - Output: variance(tenk1.unique1), sum((tenk1.unique1)::bigint), regr_count((tenk1.unique1)::double precision, (tenk1.unique1)::double precision) - -> Gather - Output: (PARTIAL variance(tenk1.unique1)), (PARTIAL sum((tenk1.unique1)::bigint)), (PARTIAL regr_count((tenk1.unique1)::double precision, (tenk1.unique1)::double precision)) - Workers Planned: 4 - -> Partial Aggregate - Output: PARTIAL variance(tenk1.unique1), PARTIAL sum((tenk1.unique1)::bigint), PARTIAL regr_count((tenk1.unique1)::double precision, (tenk1.unique1)::double precision) - -> Parallel Append - -> Parallel Seq Scan on public.tenk1 - Output: tenk1.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_1 - Output: tenk1_1.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_2 - Output: tenk1_2.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_3 - Output: tenk1_3.unique1 -(16 rows) - -SELECT variance(unique1::int4), sum(unique1::int8), regr_count(unique1::float8, unique1::float8) -FROM (SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1) u; - variance | sum | regr_count -----------------------+-----------+------------ - 8333541.588539713493 | 199980000 | 40000 -(1 row) - --- variance(int8) covers numeric_combine --- avg(numeric) covers numeric_avg_combine -EXPLAIN (COSTS OFF, VERBOSE) -SELECT variance(unique1::int8), avg(unique1::numeric) -FROM (SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1) u; - QUERY PLAN --------------------------------------------------------------------------------------------------------- - Finalize Aggregate - Output: variance((tenk1.unique1)::bigint), avg((tenk1.unique1)::numeric) - -> Gather - Output: (PARTIAL variance((tenk1.unique1)::bigint)), (PARTIAL avg((tenk1.unique1)::numeric)) - Workers Planned: 4 - -> Partial Aggregate - Output: PARTIAL variance((tenk1.unique1)::bigint), PARTIAL avg((tenk1.unique1)::numeric) - -> Parallel Append - -> Parallel Seq Scan on public.tenk1 - Output: tenk1.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_1 - Output: tenk1_1.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_2 - Output: tenk1_2.unique1 - -> Parallel Seq Scan on public.tenk1 tenk1_3 - Output: tenk1_3.unique1 -(16 rows) - -SELECT variance(unique1::int8), avg(unique1::numeric) -FROM (SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1 - UNION ALL SELECT * FROM tenk1) u; - variance | avg -----------------------+----------------------- - 8333541.588539713493 | 4999.5000000000000000 -(1 row) - -ROLLBACK; --- test coverage for dense_rank -SELECT dense_rank(x) WITHIN GROUP (ORDER BY x) FROM (VALUES (1),(1),(2),(2),(3),(3)) v(x) GROUP BY (x) ORDER BY 1; - dense_rank ------------- - 1 - 1 - 1 -(3 rows) - --- Ensure that the STRICT checks for aggregates does not take NULLness --- of ORDER BY columns into account. See bug report around --- 2a505161-2727-2473-7c46-591ed108ac52@email.cz -SELECT min(x ORDER BY y) FROM (VALUES(1, NULL)) AS d(x,y); - min ------ - 1 -(1 row) - -SELECT min(x ORDER BY y) FROM (VALUES(1, 2)) AS d(x,y); - min ------ - 1 -(1 row) - --- check collation-sensitive matching between grouping expressions -select v||'a', case v||'a' when 'aa' then 1 else 0 end, count(*) - from unnest(array['a','b']) u(v) - group by v||'a' order by 1; - ?column? | case | count -----------+------+------- - aa | 1 | 1 - ba | 0 | 1 -(2 rows) - -select v||'a', case when v||'a' = 'aa' then 1 else 0 end, count(*) - from unnest(array['a','b']) u(v) - group by v||'a' order by 1; - ?column? | case | count -----------+------+------- - aa | 1 | 1 - ba | 0 | 1 -(2 rows) - --- Make sure that generation of HashAggregate for uniqification purposes --- does not lead to array overflow due to unexpected duplicate hash keys --- see CAFeeJoKKu0u+A_A9R9316djW-YW3-+Gtgvy3ju655qRHR3jtdA@mail.gmail.com -set enable_memoize to off; -explain (costs off) - select 1 from tenk1 - where (hundred, thousand) in (select twothousand, twothousand from onek); - QUERY PLAN -------------------------------------------------------------- - Hash Join - Hash Cond: (tenk1.hundred = onek.twothousand) - -> Seq Scan on tenk1 - Filter: (hundred = thousand) - -> Hash - -> HashAggregate - Group Key: onek.twothousand, onek.twothousand - -> Seq Scan on onek -(8 rows) - -reset enable_memoize; --- --- Hash Aggregation Spill tests --- -set enable_sort=false; -set work_mem='64kB'; -select unique1, count(*), sum(twothousand) from tenk1 -group by unique1 -having sum(fivethous) > 4975 -order by sum(twothousand); - unique1 | count | sum ----------+-------+------ - 4976 | 1 | 976 - 4977 | 1 | 977 - 4978 | 1 | 978 - 4979 | 1 | 979 - 4980 | 1 | 980 - 4981 | 1 | 981 - 4982 | 1 | 982 - 4983 | 1 | 983 - 4984 | 1 | 984 - 4985 | 1 | 985 - 4986 | 1 | 986 - 4987 | 1 | 987 - 4988 | 1 | 988 - 4989 | 1 | 989 - 4990 | 1 | 990 - 4991 | 1 | 991 - 4992 | 1 | 992 - 4993 | 1 | 993 - 4994 | 1 | 994 - 4995 | 1 | 995 - 4996 | 1 | 996 - 4997 | 1 | 997 - 4998 | 1 | 998 - 4999 | 1 | 999 - 9976 | 1 | 1976 - 9977 | 1 | 1977 - 9978 | 1 | 1978 - 9979 | 1 | 1979 - 9980 | 1 | 1980 - 9981 | 1 | 1981 - 9982 | 1 | 1982 - 9983 | 1 | 1983 - 9984 | 1 | 1984 - 9985 | 1 | 1985 - 9986 | 1 | 1986 - 9987 | 1 | 1987 - 9988 | 1 | 1988 - 9989 | 1 | 1989 - 9990 | 1 | 1990 - 9991 | 1 | 1991 - 9992 | 1 | 1992 - 9993 | 1 | 1993 - 9994 | 1 | 1994 - 9995 | 1 | 1995 - 9996 | 1 | 1996 - 9997 | 1 | 1997 - 9998 | 1 | 1998 - 9999 | 1 | 1999 -(48 rows) - -set work_mem to default; -set enable_sort to default; --- --- Compare results between plans using sorting and plans using hash --- aggregation. Force spilling in both cases by setting work_mem low. --- -set work_mem='64kB'; -create table agg_data_2k as -select g from generate_series(0, 1999) g; -analyze agg_data_2k; -create table agg_data_20k as -select g from generate_series(0, 19999) g; -analyze agg_data_20k; --- Produce results with sorting. -set enable_hashagg = false; -set jit_above_cost = 0; -explain (costs off) -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; - QUERY PLAN --------------------------------------- - GroupAggregate - Group Key: ((g % 10000)) - -> Sort - Sort Key: ((g % 10000)) - -> Seq Scan on agg_data_20k -(5 rows) - -create table agg_group_1 as -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; -create table agg_group_2 as -select * from - (values (100), (300), (500)) as r(a), - lateral ( - select (g/2)::numeric as c1, - array_agg(g::numeric) as c2, - count(*) as c3 - from agg_data_2k - where g < r.a - group by g/2) as s; -set jit_above_cost to default; -create table agg_group_3 as -select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 - from agg_data_2k group by g/2; -create table agg_group_4 as -select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 - from agg_data_2k group by g/2; --- Produce results with hash aggregation -set enable_hashagg = true; -set enable_sort = false; -set jit_above_cost = 0; -explain (costs off) -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; - QUERY PLAN --------------------------------- - HashAggregate - Group Key: (g % 10000) - -> Seq Scan on agg_data_20k -(3 rows) - -create table agg_hash_1 as -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; -create table agg_hash_2 as -select * from - (values (100), (300), (500)) as r(a), - lateral ( - select (g/2)::numeric as c1, - array_agg(g::numeric) as c2, - count(*) as c3 - from agg_data_2k - where g < r.a - group by g/2) as s; -set jit_above_cost to default; -create table agg_hash_3 as -select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 - from agg_data_2k group by g/2; -create table agg_hash_4 as -select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 - from agg_data_2k group by g/2; -set enable_sort = true; -set work_mem to default; --- Compare group aggregation results to hash aggregation results -(select * from agg_hash_1 except select * from agg_group_1) - union all -(select * from agg_group_1 except select * from agg_hash_1); - c1 | c2 | c3 -----+----+---- -(0 rows) - -(select * from agg_hash_2 except select * from agg_group_2) - union all -(select * from agg_group_2 except select * from agg_hash_2); - a | c1 | c2 | c3 ----+----+----+---- -(0 rows) - -(select * from agg_hash_3 except select * from agg_group_3) - union all -(select * from agg_group_3 except select * from agg_hash_3); - c1 | c2 | c3 -----+----+---- -(0 rows) - -(select * from agg_hash_4 except select * from agg_group_4) - union all -(select * from agg_group_4 except select * from agg_hash_4); - c1 | c2 | c3 -----+----+---- -(0 rows) - -drop table agg_group_1; -drop table agg_group_2; -drop table agg_group_3; -drop table agg_group_4; -drop table agg_hash_1; -drop table agg_hash_2; -drop table agg_hash_3; -drop table agg_hash_4; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/transactions.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/transactions.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/transactions.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/transactions.out 2021-10-30 22:04:11 -0600 @@ -1,1064 +1,2 @@ --- --- TRANSACTIONS --- -BEGIN; -SELECT * - INTO TABLE xacttest - FROM aggtest; -INSERT INTO xacttest (a, b) VALUES (777, 777.777); -END; --- should retrieve one value-- -SELECT a FROM xacttest WHERE a > 100; - a ------ - 777 -(1 row) - -BEGIN; -CREATE TABLE disappear (a int4); -DELETE FROM aggtest; --- should be empty -SELECT * FROM aggtest; - a | b ----+--- -(0 rows) - -ABORT; --- should not exist -SELECT oid FROM pg_class WHERE relname = 'disappear'; - oid ------ -(0 rows) - --- should have members again -SELECT * FROM aggtest; - a | b ------+--------- - 56 | 7.8 - 100 | 99.097 - 0 | 0.09561 - 42 | 324.78 -(4 rows) - --- Read-only tests -CREATE TABLE writetest (a int); -CREATE TEMPORARY TABLE temptest (a int); -BEGIN; -SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -SET TRANSACTION READ WRITE; --fail -ERROR: transaction read-write mode must be set before any query -COMMIT; -BEGIN; -SET TRANSACTION READ ONLY; -- ok -SET TRANSACTION READ WRITE; -- ok -SET TRANSACTION READ ONLY; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -SAVEPOINT x; -SET TRANSACTION READ ONLY; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -SET TRANSACTION READ ONLY; -- ok -SET TRANSACTION READ WRITE; --fail -ERROR: cannot set transaction read-write mode inside a read-only transaction -COMMIT; -BEGIN; -SET TRANSACTION READ WRITE; -- ok -SAVEPOINT x; -SET TRANSACTION READ WRITE; -- ok -SET TRANSACTION READ ONLY; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -SET TRANSACTION READ ONLY; -- ok -SET TRANSACTION READ WRITE; --fail -ERROR: cannot set transaction read-write mode inside a read-only transaction -COMMIT; -BEGIN; -SET TRANSACTION READ WRITE; -- ok -SAVEPOINT x; -SET TRANSACTION READ ONLY; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -ROLLBACK TO SAVEPOINT x; -SHOW transaction_read_only; -- off - transaction_read_only ------------------------ - off -(1 row) - -SAVEPOINT y; -SET TRANSACTION READ ONLY; -- ok -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -RELEASE SAVEPOINT y; -SHOW transaction_read_only; -- off - transaction_read_only ------------------------ - off -(1 row) - -COMMIT; -SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; -DROP TABLE writetest; -- fail -ERROR: cannot execute DROP TABLE in a read-only transaction -INSERT INTO writetest VALUES (1); -- fail -ERROR: cannot execute INSERT in a read-only transaction -SELECT * FROM writetest; -- ok - a ---- -(0 rows) - -DELETE FROM temptest; -- ok -UPDATE temptest SET a = 0 FROM writetest WHERE temptest.a = 1 AND writetest.a = temptest.a; -- ok -PREPARE test AS UPDATE writetest SET a = 0; -- ok -EXECUTE test; -- fail -ERROR: cannot execute UPDATE in a read-only transaction -SELECT * FROM writetest, temptest; -- ok - a | a ----+--- -(0 rows) - -CREATE TABLE test AS SELECT * FROM writetest; -- fail -ERROR: cannot execute CREATE TABLE AS in a read-only transaction -START TRANSACTION READ WRITE; -DROP TABLE writetest; -- ok -COMMIT; --- Subtransactions, basic tests --- create & drop tables -SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE; -CREATE TABLE trans_foobar (a int); -BEGIN; - CREATE TABLE trans_foo (a int); - SAVEPOINT one; - DROP TABLE trans_foo; - CREATE TABLE trans_bar (a int); - ROLLBACK TO SAVEPOINT one; - RELEASE SAVEPOINT one; - SAVEPOINT two; - CREATE TABLE trans_baz (a int); - RELEASE SAVEPOINT two; - drop TABLE trans_foobar; - CREATE TABLE trans_barbaz (a int); -COMMIT; --- should exist: trans_barbaz, trans_baz, trans_foo -SELECT * FROM trans_foo; -- should be empty - a ---- -(0 rows) - -SELECT * FROM trans_bar; -- shouldn't exist -ERROR: relation "trans_bar" does not exist -LINE 1: SELECT * FROM trans_bar; - ^ -SELECT * FROM trans_barbaz; -- should be empty - a ---- -(0 rows) - -SELECT * FROM trans_baz; -- should be empty - a ---- -(0 rows) - --- inserts -BEGIN; - INSERT INTO trans_foo VALUES (1); - SAVEPOINT one; - INSERT into trans_bar VALUES (1); -ERROR: relation "trans_bar" does not exist -LINE 1: INSERT into trans_bar VALUES (1); - ^ - ROLLBACK TO one; - RELEASE SAVEPOINT one; - SAVEPOINT two; - INSERT into trans_barbaz VALUES (1); - RELEASE two; - SAVEPOINT three; - SAVEPOINT four; - INSERT INTO trans_foo VALUES (2); - RELEASE SAVEPOINT four; - ROLLBACK TO SAVEPOINT three; - RELEASE SAVEPOINT three; - INSERT INTO trans_foo VALUES (3); -COMMIT; -SELECT * FROM trans_foo; -- should have 1 and 3 - a ---- - 1 - 3 -(2 rows) - -SELECT * FROM trans_barbaz; -- should have 1 - a ---- - 1 -(1 row) - --- test whole-tree commit -BEGIN; - SAVEPOINT one; - SELECT trans_foo; -ERROR: column "trans_foo" does not exist -LINE 1: SELECT trans_foo; - ^ - ROLLBACK TO SAVEPOINT one; - RELEASE SAVEPOINT one; - SAVEPOINT two; - CREATE TABLE savepoints (a int); - SAVEPOINT three; - INSERT INTO savepoints VALUES (1); - SAVEPOINT four; - INSERT INTO savepoints VALUES (2); - SAVEPOINT five; - INSERT INTO savepoints VALUES (3); - ROLLBACK TO SAVEPOINT five; -COMMIT; -COMMIT; -- should not be in a transaction block -WARNING: there is no transaction in progress -SELECT * FROM savepoints; - a ---- - 1 - 2 -(2 rows) - --- test whole-tree rollback -BEGIN; - SAVEPOINT one; - DELETE FROM savepoints WHERE a=1; - RELEASE SAVEPOINT one; - SAVEPOINT two; - DELETE FROM savepoints WHERE a=1; - SAVEPOINT three; - DELETE FROM savepoints WHERE a=2; -ROLLBACK; -COMMIT; -- should not be in a transaction block -WARNING: there is no transaction in progress -SELECT * FROM savepoints; - a ---- - 1 - 2 -(2 rows) - --- test whole-tree commit on an aborted subtransaction -BEGIN; - INSERT INTO savepoints VALUES (4); - SAVEPOINT one; - INSERT INTO savepoints VALUES (5); - SELECT trans_foo; -ERROR: column "trans_foo" does not exist -LINE 1: SELECT trans_foo; - ^ -COMMIT; -SELECT * FROM savepoints; - a ---- - 1 - 2 -(2 rows) - -BEGIN; - INSERT INTO savepoints VALUES (6); - SAVEPOINT one; - INSERT INTO savepoints VALUES (7); - RELEASE SAVEPOINT one; - INSERT INTO savepoints VALUES (8); -COMMIT; --- rows 6 and 8 should have been created by the same xact -SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=8; - ?column? ----------- - t -(1 row) - --- rows 6 and 7 should have been created by different xacts -SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=7; - ?column? ----------- - f -(1 row) - -BEGIN; - INSERT INTO savepoints VALUES (9); - SAVEPOINT one; - INSERT INTO savepoints VALUES (10); - ROLLBACK TO SAVEPOINT one; - INSERT INTO savepoints VALUES (11); -COMMIT; -SELECT a FROM savepoints WHERE a in (9, 10, 11); - a ----- - 9 - 11 -(2 rows) - --- rows 9 and 11 should have been created by different xacts -SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=9 AND b.a=11; - ?column? ----------- - f -(1 row) - -BEGIN; - INSERT INTO savepoints VALUES (12); - SAVEPOINT one; - INSERT INTO savepoints VALUES (13); - SAVEPOINT two; - INSERT INTO savepoints VALUES (14); - ROLLBACK TO SAVEPOINT one; - INSERT INTO savepoints VALUES (15); - SAVEPOINT two; - INSERT INTO savepoints VALUES (16); - SAVEPOINT three; - INSERT INTO savepoints VALUES (17); -COMMIT; -SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17; - a ----- - 12 - 15 - 16 - 17 -(4 rows) - -BEGIN; - INSERT INTO savepoints VALUES (18); - SAVEPOINT one; - INSERT INTO savepoints VALUES (19); - SAVEPOINT two; - INSERT INTO savepoints VALUES (20); - ROLLBACK TO SAVEPOINT one; - INSERT INTO savepoints VALUES (21); - ROLLBACK TO SAVEPOINT one; - INSERT INTO savepoints VALUES (22); -COMMIT; -SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22; - a ----- - 18 - 22 -(2 rows) - -DROP TABLE savepoints; --- only in a transaction block: -SAVEPOINT one; -ERROR: SAVEPOINT can only be used in transaction blocks -ROLLBACK TO SAVEPOINT one; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -RELEASE SAVEPOINT one; -ERROR: RELEASE SAVEPOINT can only be used in transaction blocks --- Only "rollback to" allowed in aborted state -BEGIN; - SAVEPOINT one; - SELECT 0/0; -ERROR: division by zero - SAVEPOINT two; -- ignored till the end of ... -ERROR: current transaction is aborted, commands ignored until end of transaction block - RELEASE SAVEPOINT one; -- ignored till the end of ... -ERROR: current transaction is aborted, commands ignored until end of transaction block - ROLLBACK TO SAVEPOINT one; - SELECT 1; - ?column? ----------- - 1 -(1 row) - -COMMIT; -SELECT 1; -- this should work - ?column? ----------- - 1 -(1 row) - --- check non-transactional behavior of cursors -BEGIN; - DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2; - SAVEPOINT one; - FETCH 10 FROM c; - unique2 ---------- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -(10 rows) - - ROLLBACK TO SAVEPOINT one; - FETCH 10 FROM c; - unique2 ---------- - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 -(10 rows) - - RELEASE SAVEPOINT one; - FETCH 10 FROM c; - unique2 ---------- - 20 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 -(10 rows) - - CLOSE c; - DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1 ORDER BY unique2; - SAVEPOINT two; - FETCH 10 FROM c; -ERROR: division by zero - ROLLBACK TO SAVEPOINT two; - -- c is now dead to the world ... - FETCH 10 FROM c; -ERROR: portal "c" cannot be run - ROLLBACK TO SAVEPOINT two; - RELEASE SAVEPOINT two; - FETCH 10 FROM c; -ERROR: portal "c" cannot be run -COMMIT; --- --- Check that "stable" functions are really stable. They should not be --- able to see the partial results of the calling query. (Ideally we would --- also check that they don't see commits of concurrent transactions, but --- that's a mite hard to do within the limitations of pg_regress.) --- -select * from xacttest; - a | b ------+--------- - 56 | 7.8 - 100 | 99.097 - 0 | 0.09561 - 42 | 324.78 - 777 | 777.777 -(5 rows) - -create or replace function max_xacttest() returns smallint language sql as -'select max(a) from xacttest' stable; -begin; -update xacttest set a = max_xacttest() + 10 where a > 0; -select * from xacttest; - a | b ------+--------- - 0 | 0.09561 - 787 | 7.8 - 787 | 99.097 - 787 | 324.78 - 787 | 777.777 -(5 rows) - -rollback; --- But a volatile function can see the partial results of the calling query -create or replace function max_xacttest() returns smallint language sql as -'select max(a) from xacttest' volatile; -begin; -update xacttest set a = max_xacttest() + 10 where a > 0; -select * from xacttest; - a | b ------+--------- - 0 | 0.09561 - 787 | 7.8 - 797 | 99.097 - 807 | 324.78 - 817 | 777.777 -(5 rows) - -rollback; --- Now the same test with plpgsql (since it depends on SPI which is different) -create or replace function max_xacttest() returns smallint language plpgsql as -'begin return max(a) from xacttest; end' stable; -begin; -update xacttest set a = max_xacttest() + 10 where a > 0; -select * from xacttest; - a | b ------+--------- - 0 | 0.09561 - 787 | 7.8 - 787 | 99.097 - 787 | 324.78 - 787 | 777.777 -(5 rows) - -rollback; -create or replace function max_xacttest() returns smallint language plpgsql as -'begin return max(a) from xacttest; end' volatile; -begin; -update xacttest set a = max_xacttest() + 10 where a > 0; -select * from xacttest; - a | b ------+--------- - 0 | 0.09561 - 787 | 7.8 - 797 | 99.097 - 807 | 324.78 - 817 | 777.777 -(5 rows) - -rollback; --- test case for problems with dropping an open relation during abort -BEGIN; - savepoint x; - CREATE TABLE koju (a INT UNIQUE); - INSERT INTO koju VALUES (1); - INSERT INTO koju VALUES (1); -ERROR: duplicate key value violates unique constraint "koju_a_key" -DETAIL: Key (a)=(1) already exists. - rollback to x; - CREATE TABLE koju (a INT UNIQUE); - INSERT INTO koju VALUES (1); - INSERT INTO koju VALUES (1); -ERROR: duplicate key value violates unique constraint "koju_a_key" -DETAIL: Key (a)=(1) already exists. -ROLLBACK; -DROP TABLE trans_foo; -DROP TABLE trans_baz; -DROP TABLE trans_barbaz; --- test case for problems with revalidating an open relation during abort -create function inverse(int) returns float8 as -$$ -begin - analyze revalidate_bug; - return 1::float8/$1; -exception - when division_by_zero then return 0; -end$$ language plpgsql volatile; -create table revalidate_bug (c float8 unique); -insert into revalidate_bug values (1); -insert into revalidate_bug values (inverse(0)); -drop table revalidate_bug; -drop function inverse(int); --- verify that cursors created during an aborted subtransaction are --- closed, but that we do not rollback the effect of any FETCHs --- performed in the aborted subtransaction -begin; -savepoint x; -create table abc (a int); -insert into abc values (5); -insert into abc values (10); -declare foo cursor for select * from abc; -fetch from foo; - a ---- - 5 -(1 row) - -rollback to x; --- should fail -fetch from foo; -ERROR: cursor "foo" does not exist -commit; -begin; -create table abc (a int); -insert into abc values (5); -insert into abc values (10); -insert into abc values (15); -declare foo cursor for select * from abc; -fetch from foo; - a ---- - 5 -(1 row) - -savepoint x; -fetch from foo; - a ----- - 10 -(1 row) - -rollback to x; -fetch from foo; - a ----- - 15 -(1 row) - -abort; --- Test for proper cleanup after a failure in a cursor portal --- that was created in an outer subtransaction -CREATE FUNCTION invert(x float8) RETURNS float8 LANGUAGE plpgsql AS -$$ begin return 1/x; end $$; -CREATE FUNCTION create_temp_tab() RETURNS text -LANGUAGE plpgsql AS $$ -BEGIN - CREATE TEMP TABLE new_table (f1 float8); - -- case of interest is that we fail while holding an open - -- relcache reference to new_table - INSERT INTO new_table SELECT invert(0.0); - RETURN 'foo'; -END $$; -BEGIN; -DECLARE ok CURSOR FOR SELECT * FROM int8_tbl; -DECLARE ctt CURSOR FOR SELECT create_temp_tab(); -FETCH ok; - q1 | q2 ------+----- - 123 | 456 -(1 row) - -SAVEPOINT s1; -FETCH ok; -- should work - q1 | q2 ------+------------------ - 123 | 4567890123456789 -(1 row) - -FETCH ctt; -- error occurs here -ERROR: division by zero -CONTEXT: PL/pgSQL function invert(double precision) line 1 at RETURN -SQL statement "INSERT INTO new_table SELECT invert(0.0)" -PL/pgSQL function create_temp_tab() line 6 at SQL statement -ROLLBACK TO s1; -FETCH ok; -- should work - q1 | q2 -------------------+----- - 4567890123456789 | 123 -(1 row) - -FETCH ctt; -- must be rejected -ERROR: portal "ctt" cannot be run -COMMIT; -DROP FUNCTION create_temp_tab(); -DROP FUNCTION invert(x float8); --- Tests for AND CHAIN -CREATE TABLE abc (a int); --- set nondefault value so we have something to override below -SET default_transaction_read_only = on; -START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -INSERT INTO abc VALUES (1); -INSERT INTO abc VALUES (2); -COMMIT AND CHAIN; -- TBLOCK_END -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -INSERT INTO abc VALUES ('error'); -ERROR: invalid input syntax for type integer: "error" -LINE 1: INSERT INTO abc VALUES ('error'); - ^ -INSERT INTO abc VALUES (3); -- check it's really aborted -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT AND CHAIN; -- TBLOCK_ABORT_END -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -INSERT INTO abc VALUES (4); -COMMIT; -START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -SAVEPOINT x; -INSERT INTO abc VALUES ('error'); -ERROR: invalid input syntax for type integer: "error" -LINE 1: INSERT INTO abc VALUES ('error'); - ^ -COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -INSERT INTO abc VALUES (5); -COMMIT; -START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -SAVEPOINT x; -COMMIT AND CHAIN; -- TBLOCK_SUBCOMMIT -SHOW transaction_isolation; - transaction_isolation ------------------------ - repeatable read -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - on -(1 row) - -COMMIT; --- different mix of options just for fun -START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE; -SHOW transaction_isolation; - transaction_isolation ------------------------ - serializable -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - off -(1 row) - -INSERT INTO abc VALUES (6); -ROLLBACK AND CHAIN; -- TBLOCK_ABORT_PENDING -SHOW transaction_isolation; - transaction_isolation ------------------------ - serializable -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - off -(1 row) - -INSERT INTO abc VALUES ('error'); -ERROR: invalid input syntax for type integer: "error" -LINE 1: INSERT INTO abc VALUES ('error'); - ^ -ROLLBACK AND CHAIN; -- TBLOCK_ABORT_END -SHOW transaction_isolation; - transaction_isolation ------------------------ - serializable -(1 row) - -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SHOW transaction_deferrable; - transaction_deferrable ------------------------- - off -(1 row) - -ROLLBACK; --- not allowed outside a transaction block -COMMIT AND CHAIN; -- error -ERROR: COMMIT AND CHAIN can only be used in transaction blocks -ROLLBACK AND CHAIN; -- error -ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks -SELECT * FROM abc ORDER BY 1; - a ---- - 1 - 2 - 4 - 5 -(4 rows) - -RESET default_transaction_read_only; -DROP TABLE abc; --- Test assorted behaviors around the implicit transaction block created --- when multiple SQL commands are sent in a single Query message. These --- tests rely on the fact that psql will not break SQL commands apart at a --- backslash-quoted semicolon, but will send them as one Query. -create temp table i_table (f1 int); --- psql will show only the last result in a multi-statement Query -SELECT 1\; SELECT 2\; SELECT 3; - ?column? ----------- - 3 -(1 row) - --- this implicitly commits: -insert into i_table values(1)\; select * from i_table; - f1 ----- - 1 -(1 row) - --- 1/0 error will cause rolling back the whole implicit transaction -insert into i_table values(2)\; select * from i_table\; select 1/0; -ERROR: division by zero -select * from i_table; - f1 ----- - 1 -(1 row) - -rollback; -- we are not in a transaction at this point -WARNING: there is no transaction in progress --- can use regular begin/commit/rollback within a single Query -begin\; insert into i_table values(3)\; commit; -rollback; -- we are not in a transaction at this point -WARNING: there is no transaction in progress -begin\; insert into i_table values(4)\; rollback; -rollback; -- we are not in a transaction at this point -WARNING: there is no transaction in progress --- begin converts implicit transaction into a regular one that --- can extend past the end of the Query -select 1\; begin\; insert into i_table values(5); -commit; -select 1\; begin\; insert into i_table values(6); -rollback; --- commit in implicit-transaction state commits but issues a warning. -insert into i_table values(7)\; commit\; insert into i_table values(8)\; select 1/0; -WARNING: there is no transaction in progress -ERROR: division by zero --- similarly, rollback aborts but issues a warning. -insert into i_table values(9)\; rollback\; select 2; -WARNING: there is no transaction in progress - ?column? ----------- - 2 -(1 row) - -select * from i_table; - f1 ----- - 1 - 3 - 5 - 7 -(4 rows) - -rollback; -- we are not in a transaction at this point -WARNING: there is no transaction in progress --- implicit transaction block is still a transaction block, for e.g. VACUUM -SELECT 1\; VACUUM; -ERROR: VACUUM cannot run inside a transaction block -SELECT 1\; COMMIT\; VACUUM; -WARNING: there is no transaction in progress -ERROR: VACUUM cannot run inside a transaction block --- we disallow savepoint-related commands in implicit-transaction state -SELECT 1\; SAVEPOINT sp; -ERROR: SAVEPOINT can only be used in transaction blocks -SELECT 1\; COMMIT\; SAVEPOINT sp; -WARNING: there is no transaction in progress -ERROR: SAVEPOINT can only be used in transaction blocks -ROLLBACK TO SAVEPOINT sp\; SELECT 2; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -SELECT 2\; RELEASE SAVEPOINT sp\; SELECT 3; -ERROR: RELEASE SAVEPOINT can only be used in transaction blocks --- but this is OK, because the BEGIN converts it to a regular xact -SELECT 1\; BEGIN\; SAVEPOINT sp\; ROLLBACK TO SAVEPOINT sp\; COMMIT; --- Tests for AND CHAIN in implicit transaction blocks -SET TRANSACTION READ ONLY\; COMMIT AND CHAIN; -- error -ERROR: COMMIT AND CHAIN can only be used in transaction blocks -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -SET TRANSACTION READ ONLY\; ROLLBACK AND CHAIN; -- error -ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks -SHOW transaction_read_only; - transaction_read_only ------------------------ - off -(1 row) - -CREATE TABLE abc (a int); --- COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN -INSERT INTO abc VALUES (7)\; COMMIT\; INSERT INTO abc VALUES (8)\; COMMIT AND CHAIN; -- 7 commit, 8 error -WARNING: there is no transaction in progress -ERROR: COMMIT AND CHAIN can only be used in transaction blocks -INSERT INTO abc VALUES (9)\; ROLLBACK\; INSERT INTO abc VALUES (10)\; ROLLBACK AND CHAIN; -- 9 rollback, 10 error -WARNING: there is no transaction in progress -ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks --- COMMIT/ROLLBACK AND CHAIN + COMMIT/ROLLBACK -INSERT INTO abc VALUES (11)\; COMMIT AND CHAIN\; INSERT INTO abc VALUES (12)\; COMMIT; -- 11 error, 12 not reached -ERROR: COMMIT AND CHAIN can only be used in transaction blocks -INSERT INTO abc VALUES (13)\; ROLLBACK AND CHAIN\; INSERT INTO abc VALUES (14)\; ROLLBACK; -- 13 error, 14 not reached -ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks --- START TRANSACTION + COMMIT/ROLLBACK AND CHAIN -START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (15)\; COMMIT AND CHAIN; -- 15 ok -SHOW transaction_isolation; -- transaction is active at this point - transaction_isolation ------------------------ - repeatable read -(1 row) - -COMMIT; -START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (16)\; ROLLBACK AND CHAIN; -- 16 ok -SHOW transaction_isolation; -- transaction is active at this point - transaction_isolation ------------------------ - repeatable read -(1 row) - -ROLLBACK; -SET default_transaction_isolation = 'read committed'; --- START TRANSACTION + COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN -START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (17)\; COMMIT\; INSERT INTO abc VALUES (18)\; COMMIT AND CHAIN; -- 17 commit, 18 error -ERROR: COMMIT AND CHAIN can only be used in transaction blocks -SHOW transaction_isolation; -- out of transaction block - transaction_isolation ------------------------ - read committed -(1 row) - -START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (19)\; ROLLBACK\; INSERT INTO abc VALUES (20)\; ROLLBACK AND CHAIN; -- 19 rollback, 20 error -ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks -SHOW transaction_isolation; -- out of transaction block - transaction_isolation ------------------------ - read committed -(1 row) - -RESET default_transaction_isolation; -SELECT * FROM abc ORDER BY 1; - a ----- - 7 - 15 - 17 -(3 rows) - -DROP TABLE abc; --- Test for successful cleanup of an aborted transaction at session exit. --- THIS MUST BE THE LAST TEST IN THIS FILE. -begin; -select 1/0; -ERROR: division by zero -rollback to X; -ERROR: savepoint "x" does not exist --- DO NOT ADD ANYTHING HERE. +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/random.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/random.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/random.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/random.out 2021-10-30 22:04:11 -0600 @@ -1,53 +1,2 @@ --- --- RANDOM --- Test the random function --- --- count the number of tuples originally, should be 1000 -SELECT count(*) FROM onek; - count -------- - 1000 -(1 row) - --- pick three random rows, they shouldn't match -(SELECT unique1 AS random - FROM onek ORDER BY random() LIMIT 1) -INTERSECT -(SELECT unique1 AS random - FROM onek ORDER BY random() LIMIT 1) -INTERSECT -(SELECT unique1 AS random - FROM onek ORDER BY random() LIMIT 1); - random --------- -(0 rows) - --- count roughly 1/10 of the tuples -CREATE TABLE RANDOM_TBL AS - SELECT count(*) AS random - FROM onek WHERE random() < 1.0/10; --- select again, the count should be different -INSERT INTO RANDOM_TBL (random) - SELECT count(*) - FROM onek WHERE random() < 1.0/10; --- select again, the count should be different -INSERT INTO RANDOM_TBL (random) - SELECT count(*) - FROM onek WHERE random() < 1.0/10; --- select again, the count should be different -INSERT INTO RANDOM_TBL (random) - SELECT count(*) - FROM onek WHERE random() < 1.0/10; --- now test that they are different counts -SELECT random, count(random) FROM RANDOM_TBL - GROUP BY random HAVING count(random) > 3; - random | count ---------+------- -(0 rows) - -SELECT AVG(random) FROM RANDOM_TBL - HAVING AVG(random) NOT BETWEEN 80 AND 120; - avg ------ -(0 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/portals.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/portals.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/portals.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/portals.out 2021-10-30 22:04:12 -0600 @@ -1,1538 +1,2 @@ --- --- Cursor regression tests --- -BEGIN; -DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2; -DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -FETCH 1 in foo1; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH 2 in foo2; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(2 rows) - -FETCH 3 in foo3; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx -(3 rows) - -FETCH 4 in foo4; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx -(4 rows) - -FETCH 5 in foo5; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx -(5 rows) - -FETCH 6 in foo6; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx -(6 rows) - -FETCH 7 in foo7; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx -(7 rows) - -FETCH 8 in foo8; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx -(8 rows) - -FETCH 9 in foo9; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx -(9 rows) - -FETCH 10 in foo10; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx -(10 rows) - -FETCH 11 in foo11; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx -(11 rows) - -FETCH 12 in foo12; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx -(12 rows) - -FETCH 13 in foo13; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx -(13 rows) - -FETCH 14 in foo14; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx -(14 rows) - -FETCH 15 in foo15; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx -(15 rows) - -FETCH 16 in foo16; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx -(16 rows) - -FETCH 17 in foo17; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx -(17 rows) - -FETCH 18 in foo18; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx -(18 rows) - -FETCH 19 in foo19; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx -(19 rows) - -FETCH 20 in foo20; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx -(20 rows) - -FETCH 21 in foo21; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx - 9460 | 20 | 0 | 0 | 0 | 0 | 60 | 460 | 1460 | 4460 | 9460 | 120 | 121 | WZAAAA | UAAAAA | AAAAxx -(21 rows) - -FETCH 22 in foo22; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx - 9460 | 20 | 0 | 0 | 0 | 0 | 60 | 460 | 1460 | 4460 | 9460 | 120 | 121 | WZAAAA | UAAAAA | AAAAxx - 59 | 21 | 1 | 3 | 9 | 19 | 59 | 59 | 59 | 59 | 59 | 118 | 119 | HCAAAA | VAAAAA | HHHHxx -(22 rows) - -FETCH 23 in foo23; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx - 9460 | 20 | 0 | 0 | 0 | 0 | 60 | 460 | 1460 | 4460 | 9460 | 120 | 121 | WZAAAA | UAAAAA | AAAAxx - 59 | 21 | 1 | 3 | 9 | 19 | 59 | 59 | 59 | 59 | 59 | 118 | 119 | HCAAAA | VAAAAA | HHHHxx - 8020 | 22 | 0 | 0 | 0 | 0 | 20 | 20 | 20 | 3020 | 8020 | 40 | 41 | MWAAAA | WAAAAA | OOOOxx -(23 rows) - -FETCH backward 1 in foo23; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 59 | 21 | 1 | 3 | 9 | 19 | 59 | 59 | 59 | 59 | 59 | 118 | 119 | HCAAAA | VAAAAA | HHHHxx -(1 row) - -FETCH backward 2 in foo22; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 9460 | 20 | 0 | 0 | 0 | 0 | 60 | 460 | 1460 | 4460 | 9460 | 120 | 121 | WZAAAA | UAAAAA | AAAAxx - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx -(2 rows) - -FETCH backward 3 in foo21; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 6969 | 19 | 1 | 1 | 9 | 9 | 69 | 969 | 969 | 1969 | 6969 | 138 | 139 | BIAAAA | TAAAAA | VVVVxx - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx -(3 rows) - -FETCH backward 4 in foo20; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 6621 | 18 | 1 | 1 | 1 | 1 | 21 | 621 | 621 | 1621 | 6621 | 42 | 43 | RUAAAA | SAAAAA | OOOOxx - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx -(4 rows) - -FETCH backward 5 in foo19; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5785 | 17 | 1 | 1 | 5 | 5 | 85 | 785 | 1785 | 785 | 5785 | 170 | 171 | NOAAAA | RAAAAA | HHHHxx - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx -(5 rows) - -FETCH backward 6 in foo18; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5387 | 16 | 1 | 3 | 7 | 7 | 87 | 387 | 1387 | 387 | 5387 | 174 | 175 | FZAAAA | QAAAAA | AAAAxx - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx -(6 rows) - -FETCH backward 7 in foo17; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5006 | 15 | 0 | 2 | 6 | 6 | 6 | 6 | 1006 | 6 | 5006 | 12 | 13 | OKAAAA | PAAAAA | VVVVxx - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx -(7 rows) - -FETCH backward 8 in foo16; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5471 | 14 | 1 | 3 | 1 | 11 | 71 | 471 | 1471 | 471 | 5471 | 142 | 143 | LCAAAA | OAAAAA | OOOOxx - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx -(8 rows) - -FETCH backward 9 in foo15; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 6243 | 13 | 1 | 3 | 3 | 3 | 43 | 243 | 243 | 1243 | 6243 | 86 | 87 | DGAAAA | NAAAAA | HHHHxx - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx -(9 rows) - -FETCH backward 10 in foo14; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5222 | 12 | 0 | 2 | 2 | 2 | 22 | 222 | 1222 | 222 | 5222 | 44 | 45 | WSAAAA | MAAAAA | AAAAxx - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx -(10 rows) - -FETCH backward 11 in foo13; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1504 | 11 | 0 | 0 | 4 | 4 | 4 | 504 | 1504 | 1504 | 1504 | 8 | 9 | WFAAAA | LAAAAA | VVVVxx - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(11 rows) - -FETCH backward 12 in foo12; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1314 | 10 | 0 | 2 | 4 | 14 | 14 | 314 | 1314 | 1314 | 1314 | 28 | 29 | OYAAAA | KAAAAA | OOOOxx - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(11 rows) - -FETCH backward 13 in foo11; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 3043 | 9 | 1 | 3 | 3 | 3 | 43 | 43 | 1043 | 3043 | 3043 | 86 | 87 | BNAAAA | JAAAAA | HHHHxx - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(10 rows) - -FETCH backward 14 in foo10; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 4321 | 8 | 1 | 1 | 1 | 1 | 21 | 321 | 321 | 4321 | 4321 | 42 | 43 | FKAAAA | IAAAAA | AAAAxx - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(9 rows) - -FETCH backward 15 in foo9; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 6701 | 7 | 1 | 1 | 1 | 1 | 1 | 701 | 701 | 1701 | 6701 | 2 | 3 | TXAAAA | HAAAAA | VVVVxx - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(8 rows) - -FETCH backward 16 in foo8; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 5057 | 6 | 1 | 1 | 7 | 17 | 57 | 57 | 1057 | 57 | 5057 | 114 | 115 | NMAAAA | GAAAAA | OOOOxx - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(7 rows) - -FETCH backward 17 in foo7; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8009 | 5 | 1 | 1 | 9 | 9 | 9 | 9 | 9 | 3009 | 8009 | 18 | 19 | BWAAAA | FAAAAA | HHHHxx - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(6 rows) - -FETCH backward 18 in foo6; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 7164 | 4 | 0 | 0 | 4 | 4 | 64 | 164 | 1164 | 2164 | 7164 | 128 | 129 | OPAAAA | EAAAAA | AAAAxx - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(5 rows) - -FETCH backward 19 in foo5; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(4 rows) - -FETCH backward 20 in foo4; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(3 rows) - -FETCH backward 21 in foo3; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(2 rows) - -FETCH backward 22 in foo2; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH backward 23 in foo1; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- -(0 rows) - -CLOSE foo1; -CLOSE foo2; -CLOSE foo3; -CLOSE foo4; -CLOSE foo5; -CLOSE foo6; -CLOSE foo7; -CLOSE foo8; -CLOSE foo9; -CLOSE foo10; -CLOSE foo11; -CLOSE foo12; --- leave some cursors open, to test that auto-close works. --- record this in the system view as well (don't query the time field there --- however) -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY 1; - name | statement | is_holdable | is_binary | is_scrollable --------+-----------------------------------------------------------------------+-------------+-----------+--------------- - foo13 | DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t - foo14 | DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t - foo15 | DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t - foo16 | DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t - foo17 | DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t - foo18 | DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t - foo19 | DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t - foo20 | DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t - foo21 | DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t - foo22 | DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t - foo23 | DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; | f | f | t -(11 rows) - -END; -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable -------+-----------+-------------+-----------+--------------- -(0 rows) - --- --- NO SCROLL disallows backward fetching --- -BEGIN; -DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -FETCH 1 FROM foo24; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH BACKWARD 1 FROM foo24; -- should fail -ERROR: cursor can only scan forward -HINT: Declare it with SCROLL option to enable backward scan. -END; -BEGIN; -DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; -FETCH 1 FROM foo24; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH ABSOLUTE 2 FROM foo24; -- allowed - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(1 row) - -FETCH ABSOLUTE 1 FROM foo24; -- should fail -ERROR: cursor can only scan forward -HINT: Declare it with SCROLL option to enable backward scan. -END; --- --- Cursors outside transaction blocks --- -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable -------+-----------+-------------+-----------+--------------- -(0 rows) - -BEGIN; -DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; -FETCH FROM foo25; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH FROM foo25; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(1 row) - -COMMIT; -FETCH FROM foo25; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx -(1 row) - -FETCH BACKWARD FROM foo25; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(1 row) - -FETCH ABSOLUTE -1 FROM foo25; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 2968 | 9999 | 0 | 0 | 8 | 8 | 68 | 968 | 968 | 2968 | 2968 | 136 | 137 | EKAAAA | PUOAAA | VVVVxx -(1 row) - -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable --------+----------------------------------------------------------------+-------------+-----------+--------------- - foo25 | DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; | t | f | t -(1 row) - -CLOSE foo25; -BEGIN; -DECLARE foo25ns NO SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; -FETCH FROM foo25ns; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx -(1 row) - -FETCH FROM foo25ns; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx -(1 row) - -COMMIT; -FETCH FROM foo25ns; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx -(1 row) - -FETCH ABSOLUTE 4 FROM foo25ns; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 9850 | 3 | 0 | 2 | 0 | 10 | 50 | 850 | 1850 | 4850 | 9850 | 100 | 101 | WOAAAA | DAAAAA | VVVVxx -(1 row) - -FETCH ABSOLUTE 4 FROM foo25ns; -- fail -ERROR: cursor can only scan forward -HINT: Declare it with SCROLL option to enable backward scan. -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable ----------+---------------------------------------------------------------------+-------------+-----------+--------------- - foo25ns | DECLARE foo25ns NO SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; | t | f | f -(1 row) - -CLOSE foo25ns; --- --- ROLLBACK should close holdable cursors --- -BEGIN; -DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1 ORDER BY unique2; -ROLLBACK; --- should fail -FETCH FROM foo26; -ERROR: cursor "foo26" does not exist --- --- Parameterized DECLARE needs to insert param values into the cursor portal --- -BEGIN; -CREATE FUNCTION declares_cursor(text) - RETURNS void - AS 'DECLARE c CURSOR FOR SELECT stringu1 FROM tenk1 WHERE stringu1 LIKE $1;' - LANGUAGE SQL; -SELECT declares_cursor('AB%'); - declares_cursor ------------------ - -(1 row) - -FETCH ALL FROM c; - stringu1 ----------- - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA - ABAAAA -(15 rows) - -ROLLBACK; --- --- Test behavior of both volatile and stable functions inside a cursor; --- in particular we want to see what happens during commit of a holdable --- cursor --- -create temp table tt1(f1 int); -create function count_tt1_v() returns int8 as -'select count(*) from tt1' language sql volatile; -create function count_tt1_s() returns int8 as -'select count(*) from tt1' language sql stable; -begin; -insert into tt1 values(1); -declare c1 cursor for select count_tt1_v(), count_tt1_s(); -insert into tt1 values(2); -fetch all from c1; - count_tt1_v | count_tt1_s --------------+------------- - 2 | 1 -(1 row) - -rollback; -begin; -insert into tt1 values(1); -declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); -insert into tt1 values(2); -commit; -delete from tt1; -fetch all from c2; - count_tt1_v | count_tt1_s --------------+------------- - 2 | 1 -(1 row) - -drop function count_tt1_v(); -drop function count_tt1_s(); --- Create a cursor with the BINARY option and check the pg_cursors view -BEGIN; -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable -------+----------------------------------------------------------------------+-------------+-----------+--------------- - c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f -(1 row) - -DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY 1; - name | statement | is_holdable | is_binary | is_scrollable -------+----------------------------------------------------------------------+-------------+-----------+--------------- - bc | DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; | f | t | t - c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f -(2 rows) - -ROLLBACK; --- We should not see the portal that is created internally to --- implement EXECUTE in pg_cursors -PREPARE cprep AS - SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; -EXECUTE cprep; - name | statement | is_holdable | is_binary | is_scrollable -------+----------------------------------------------------------------------+-------------+-----------+--------------- - c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f -(1 row) - --- test CLOSE ALL; -SELECT name FROM pg_cursors ORDER BY 1; - name ------- - c2 -(1 row) - -CLOSE ALL; -SELECT name FROM pg_cursors ORDER BY 1; - name ------- -(0 rows) - -BEGIN; -DECLARE foo1 CURSOR WITH HOLD FOR SELECT 1; -DECLARE foo2 CURSOR WITHOUT HOLD FOR SELECT 1; -SELECT name FROM pg_cursors ORDER BY 1; - name ------- - foo1 - foo2 -(2 rows) - -CLOSE ALL; -SELECT name FROM pg_cursors ORDER BY 1; - name ------- -(0 rows) - -COMMIT; --- --- Tests for updatable cursors --- -CREATE TEMP TABLE uctest(f1 int, f2 text); -INSERT INTO uctest VALUES (1, 'one'), (2, 'two'), (3, 'three'); -SELECT * FROM uctest; - f1 | f2 -----+------- - 1 | one - 2 | two - 3 | three -(3 rows) - --- Check DELETE WHERE CURRENT -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest; -FETCH 2 FROM c1; - f1 | f2 -----+----- - 1 | one - 2 | two -(2 rows) - -DELETE FROM uctest WHERE CURRENT OF c1; --- should show deletion -SELECT * FROM uctest; - f1 | f2 -----+------- - 1 | one - 3 | three -(2 rows) - --- cursor did not move -FETCH ALL FROM c1; - f1 | f2 -----+------- - 3 | three -(1 row) - --- cursor is insensitive -MOVE BACKWARD ALL IN c1; -FETCH ALL FROM c1; - f1 | f2 -----+------- - 1 | one - 2 | two - 3 | three -(3 rows) - -COMMIT; --- should still see deletion -SELECT * FROM uctest; - f1 | f2 -----+------- - 1 | one - 3 | three -(2 rows) - --- Check UPDATE WHERE CURRENT; this time use FOR UPDATE -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; -FETCH c1; - f1 | f2 -----+----- - 1 | one -(1 row) - -UPDATE uctest SET f1 = 8 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+------- - 3 | three - 8 | one -(2 rows) - -COMMIT; -SELECT * FROM uctest; - f1 | f2 -----+------- - 3 | three - 8 | one -(2 rows) - --- Check repeated-update and update-then-delete cases -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest; -FETCH c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+------- - 8 | one - 13 | three -(2 rows) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+------- - 8 | one - 23 | three -(2 rows) - --- insensitive cursor should not show effects of updates or deletes -FETCH RELATIVE 0 FROM c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -DELETE FROM uctest WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - -DELETE FROM uctest WHERE CURRENT OF c1; -- no-op -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - -FETCH RELATIVE 0 FROM c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -ROLLBACK; -SELECT * FROM uctest; - f1 | f2 -----+------- - 3 | three - 8 | one -(2 rows) - -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; -FETCH c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+------- - 8 | one - 13 | three -(2 rows) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+------- - 8 | one - 23 | three -(2 rows) - -DELETE FROM uctest WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - -DELETE FROM uctest WHERE CURRENT OF c1; -- no-op -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op -SELECT * FROM uctest; - f1 | f2 -----+----- - 8 | one -(1 row) - ---- FOR UPDATE cursors can't currently scroll back, so this is an error: -FETCH RELATIVE 0 FROM c1; -ERROR: cursor can only scan forward -HINT: Declare it with SCROLL option to enable backward scan. -ROLLBACK; -SELECT * FROM uctest; - f1 | f2 -----+------- - 3 | three - 8 | one -(2 rows) - --- Check insensitive cursor with INSERT --- (The above tests don't test the SQL notion of an insensitive cursor --- correctly, because per SQL standard, changes from WHERE CURRENT OF --- commands should be visible in the cursor. So here we make the --- changes with a command that is independent of the cursor.) -BEGIN; -DECLARE c1 INSENSITIVE CURSOR FOR SELECT * FROM uctest; -INSERT INTO uctest VALUES (10, 'ten'); -FETCH NEXT FROM c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -FETCH NEXT FROM c1; - f1 | f2 -----+----- - 8 | one -(1 row) - -FETCH NEXT FROM c1; -- insert not visible - f1 | f2 -----+---- -(0 rows) - -COMMIT; -SELECT * FROM uctest; - f1 | f2 -----+------- - 3 | three - 8 | one - 10 | ten -(3 rows) - -DELETE FROM uctest WHERE f1 = 10; -- restore test table state --- Check inheritance cases -CREATE TEMP TABLE ucchild () inherits (uctest); -INSERT INTO ucchild values(100, 'hundred'); -SELECT * FROM uctest; - f1 | f2 ------+--------- - 3 | three - 8 | one - 100 | hundred -(3 rows) - -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; -FETCH 1 FROM c1; - f1 | f2 -----+------- - 3 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -FETCH 1 FROM c1; - f1 | f2 -----+----- - 8 | one -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -FETCH 1 FROM c1; - f1 | f2 ------+--------- - 100 | hundred -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -FETCH 1 FROM c1; - f1 | f2 -----+---- -(0 rows) - -COMMIT; -SELECT * FROM uctest; - f1 | f2 ------+--------- - 13 | three - 18 | one - 110 | hundred -(3 rows) - --- Can update from a self-join, but only if FOR UPDATE says which to use -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5; -FETCH 1 FROM c1; - f1 | f2 | f1 | f2 -----+-----+----+------- - 18 | one | 13 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail -ERROR: cursor "c1" is not a simply updatable scan of table "uctest" -ROLLBACK; -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5 FOR UPDATE; -FETCH 1 FROM c1; - f1 | f2 | f1 | f2 -----+-----+----+------- - 18 | one | 13 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail -ERROR: cursor "c1" has multiple FOR UPDATE/SHARE references to table "uctest" -ROLLBACK; -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5 FOR SHARE OF a; -FETCH 1 FROM c1; - f1 | f2 | f1 | f2 -----+-----+----+------- - 18 | one | 13 | three -(1 row) - -UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -SELECT * FROM uctest; - f1 | f2 ------+--------- - 13 | three - 28 | one - 110 | hundred -(3 rows) - -ROLLBACK; --- Check various error cases -DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no such cursor -ERROR: cursor "c1" does not exist -DECLARE cx CURSOR WITH HOLD FOR SELECT * FROM uctest; -DELETE FROM uctest WHERE CURRENT OF cx; -- fail, can't use held cursor -ERROR: cursor "cx" is held from a previous transaction -BEGIN; -DECLARE c CURSOR FOR SELECT * FROM tenk2; -DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor on wrong table -ERROR: cursor "c" is not a simply updatable scan of table "uctest" -ROLLBACK; -BEGIN; -DECLARE c CURSOR FOR SELECT * FROM tenk2 FOR SHARE; -DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor on wrong table -ERROR: cursor "c" does not have a FOR UPDATE/SHARE reference to table "uctest" -ROLLBACK; -BEGIN; -DECLARE c CURSOR FOR SELECT * FROM tenk1 JOIN tenk2 USING (unique1); -DELETE FROM tenk1 WHERE CURRENT OF c; -- fail, cursor is on a join -ERROR: cursor "c" is not a simply updatable scan of table "tenk1" -ROLLBACK; -BEGIN; -DECLARE c CURSOR FOR SELECT f1,count(*) FROM uctest GROUP BY f1; -DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor is on aggregation -ERROR: cursor "c" is not a simply updatable scan of table "uctest" -ROLLBACK; -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM uctest; -DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no current row -ERROR: cursor "c1" is not positioned on a row -ROLLBACK; -BEGIN; -DECLARE c1 CURSOR FOR SELECT MIN(f1) FROM uctest FOR UPDATE; -ERROR: FOR UPDATE is not allowed with aggregate functions -ROLLBACK; --- WHERE CURRENT OF may someday work with views, but today is not that day. --- For now, just make sure it errors out cleanly. -CREATE TEMP VIEW ucview AS SELECT * FROM uctest; -CREATE RULE ucrule AS ON DELETE TO ucview DO INSTEAD - DELETE FROM uctest WHERE f1 = OLD.f1; -BEGIN; -DECLARE c1 CURSOR FOR SELECT * FROM ucview; -FETCH FROM c1; - f1 | f2 -----+------- - 13 | three -(1 row) - -DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported -ERROR: WHERE CURRENT OF on a view is not implemented -ROLLBACK; --- Check WHERE CURRENT OF with an index-only scan -BEGIN; -EXPLAIN (costs off) -DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; - QUERY PLAN ---------------------------------------------- - Index Only Scan using onek_stringu1 on onek - Index Cond: (stringu1 = 'DZAAAA'::name) -(2 rows) - -DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; -FETCH FROM c1; - stringu1 ----------- - DZAAAA -(1 row) - -DELETE FROM onek WHERE CURRENT OF c1; -SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; - stringu1 ----------- -(0 rows) - -ROLLBACK; --- Check behavior with rewinding to a previous child scan node, --- as per bug #15395 -BEGIN; -CREATE TABLE current_check (currentid int, payload text); -CREATE TABLE current_check_1 () INHERITS (current_check); -CREATE TABLE current_check_2 () INHERITS (current_check); -INSERT INTO current_check_1 SELECT i, 'p' || i FROM generate_series(1,9) i; -INSERT INTO current_check_2 SELECT i, 'P' || i FROM generate_series(10,19) i; -DECLARE c1 SCROLL CURSOR FOR SELECT * FROM current_check; --- This tests the fetch-backwards code path -FETCH ABSOLUTE 12 FROM c1; - currentid | payload ------------+--------- - 12 | P12 -(1 row) - -FETCH ABSOLUTE 8 FROM c1; - currentid | payload ------------+--------- - 8 | p8 -(1 row) - -DELETE FROM current_check WHERE CURRENT OF c1 RETURNING *; - currentid | payload ------------+--------- - 8 | p8 -(1 row) - --- This tests the ExecutorRewind code path -FETCH ABSOLUTE 13 FROM c1; - currentid | payload ------------+--------- - 13 | P13 -(1 row) - -FETCH ABSOLUTE 1 FROM c1; - currentid | payload ------------+--------- - 1 | p1 -(1 row) - -DELETE FROM current_check WHERE CURRENT OF c1 RETURNING *; - currentid | payload ------------+--------- - 1 | p1 -(1 row) - -SELECT * FROM current_check; - currentid | payload ------------+--------- - 2 | p2 - 3 | p3 - 4 | p4 - 5 | p5 - 6 | p6 - 7 | p7 - 9 | p9 - 10 | P10 - 11 | P11 - 12 | P12 - 13 | P13 - 14 | P14 - 15 | P15 - 16 | P16 - 17 | P17 - 18 | P18 - 19 | P19 -(17 rows) - -ROLLBACK; --- Make sure snapshot management works okay, per bug report in --- 235395b90909301035v7228ce63q392931f15aa74b31@mail.gmail.com -BEGIN; -SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -CREATE TABLE cursor (a int); -INSERT INTO cursor VALUES (1); -DECLARE c1 NO SCROLL CURSOR FOR SELECT * FROM cursor FOR UPDATE; -UPDATE cursor SET a = 2; -FETCH ALL FROM c1; - a ---- -(0 rows) - -COMMIT; -DROP TABLE cursor; --- Check rewinding a cursor containing a stable function in LIMIT, --- per bug report in 8336843.9833.1399385291498.JavaMail.root@quick -begin; -create function nochange(int) returns int - as 'select $1 limit 1' language sql stable; -declare c cursor for select * from int8_tbl limit nochange(3); -fetch all from c; - q1 | q2 -------------------+------------------ - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 -(3 rows) - -move backward all in c; -fetch all from c; - q1 | q2 -------------------+------------------ - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 -(3 rows) - -rollback; --- Check handling of non-backwards-scan-capable plans with scroll cursors -begin; -explain (costs off) declare c1 cursor for select (select 42) as x; - QUERY PLAN ---------------------------- - Result - InitPlan 1 (returns $0) - -> Result -(3 rows) - -explain (costs off) declare c1 scroll cursor for select (select 42) as x; - QUERY PLAN ---------------------------- - Materialize - InitPlan 1 (returns $0) - -> Result - -> Result -(4 rows) - -declare c1 scroll cursor for select (select 42) as x; -fetch all in c1; - x ----- - 42 -(1 row) - -fetch backward all in c1; - x ----- - 42 -(1 row) - -rollback; -begin; -explain (costs off) declare c2 cursor for select generate_series(1,3) as g; - QUERY PLAN --------------- - ProjectSet - -> Result -(2 rows) - -explain (costs off) declare c2 scroll cursor for select generate_series(1,3) as g; - QUERY PLAN --------------------- - Materialize - -> ProjectSet - -> Result -(3 rows) - -declare c2 scroll cursor for select generate_series(1,3) as g; -fetch all in c2; - g ---- - 1 - 2 - 3 -(3 rows) - -fetch backward all in c2; - g ---- - 3 - 2 - 1 -(3 rows) - -rollback; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/arrays.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/arrays.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/arrays.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/arrays.out 2021-10-30 22:04:12 -0600 @@ -1,2422 +1,2 @@ --- --- ARRAYS --- -CREATE TABLE arrtest ( - a int2[], - b int4[][][], - c name[], - d text[][], - e float8[], - f char(5)[], - g varchar(5)[] -); --- --- only the 'e' array is 0-based, the others are 1-based. --- -INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g) - VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}'); -UPDATE arrtest SET e[0] = '1.1'; -UPDATE arrtest SET e[1] = '2.2'; -INSERT INTO arrtest (f) - VALUES ('{"too long"}'); -ERROR: value too long for type character(5) -INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g) - VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', - '{{"elt1", "elt2"}}', '{"3.4", "6.7"}', - '{"abc","abcde"}', '{"abc","abcde"}'); -INSERT INTO arrtest (a, b[1:2], c, d[1:2]) - VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}'); -INSERT INTO arrtest (b[2]) VALUES(now()); -- error, type mismatch -ERROR: subscripted assignment to "b" requires type integer but expression is of type timestamp with time zone -LINE 1: INSERT INTO arrtest (b[2]) VALUES(now()); - ^ -HINT: You will need to rewrite or cast the expression. -INSERT INTO arrtest (b[1:2]) VALUES(now()); -- error, type mismatch -ERROR: subscripted assignment to "b" requires type integer[] but expression is of type timestamp with time zone -LINE 1: INSERT INTO arrtest (b[1:2]) VALUES(now()); - ^ -HINT: You will need to rewrite or cast the expression. -SELECT * FROM arrtest; - a | b | c | d | e | f | g --------------+-----------------+-----------+---------------+-----------------+-----------------+------------- - {1,2,3,4,5} | {{{0,0},{1,2}}} | {} | {} | [0:1]={1.1,2.2} | {} | {} - {11,12,23} | {{3,4},{4,5}} | {foobar} | {{elt1,elt2}} | {3.4,6.7} | {"abc ",abcde} | {abc,abcde} - {} | {3,4} | {foo,bar} | {bar,foo} | | | -(3 rows) - -SELECT arrtest.a[1], - arrtest.b[1][1][1], - arrtest.c[1], - arrtest.d[1][1], - arrtest.e[0] - FROM arrtest; - a | b | c | d | e -----+---+--------+------+----- - 1 | 0 | | | 1.1 - 11 | | foobar | elt1 | - | | foo | | -(3 rows) - -SELECT a[1], b[1][1][1], c[1], d[1][1], e[0] - FROM arrtest; - a | b | c | d | e -----+---+--------+------+----- - 1 | 0 | | | 1.1 - 11 | | foobar | elt1 | - | | foo | | -(3 rows) - -SELECT a[1:3], - b[1:1][1:2][1:2], - c[1:2], - d[1:1][1:2] - FROM arrtest; - a | b | c | d -------------+-----------------+-----------+--------------- - {1,2,3} | {{{0,0},{1,2}}} | {} | {} - {11,12,23} | {} | {foobar} | {{elt1,elt2}} - {} | {} | {foo,bar} | {} -(3 rows) - -SELECT array_ndims(a) AS a,array_ndims(b) AS b,array_ndims(c) AS c - FROM arrtest; - a | b | c ----+---+--- - 1 | 3 | - 1 | 2 | 1 - | 1 | 1 -(3 rows) - -SELECT array_dims(a) AS a,array_dims(b) AS b,array_dims(c) AS c - FROM arrtest; - a | b | c --------+-----------------+------- - [1:5] | [1:1][1:2][1:2] | - [1:3] | [1:2][1:2] | [1:1] - | [1:2] | [1:2] -(3 rows) - --- returns nothing -SELECT * - FROM arrtest - WHERE a[1] < 5 and - c = '{"foobar"}'::_name; - a | b | c | d | e | f | g ----+---+---+---+---+---+--- -(0 rows) - -UPDATE arrtest - SET a[1:2] = '{16,25}' - WHERE NOT a = '{}'::_int2; -UPDATE arrtest - SET b[1:1][1:1][1:2] = '{113, 117}', - b[1:1][1:2][2:2] = '{142, 147}' - WHERE array_dims(b) = '[1:1][1:2][1:2]'; -UPDATE arrtest - SET c[2:2] = '{"new_word"}' - WHERE array_dims(c) is not null; -SELECT a,b,c FROM arrtest; - a | b | c ----------------+-----------------------+------------------- - {16,25,3,4,5} | {{{113,142},{1,147}}} | {} - {} | {3,4} | {foo,new_word} - {16,25,23} | {{3,4},{4,5}} | {foobar,new_word} -(3 rows) - -SELECT a[1:3], - b[1:1][1:2][1:2], - c[1:2], - d[1:1][2:2] - FROM arrtest; - a | b | c | d -------------+-----------------------+-------------------+---------- - {16,25,3} | {{{113,142},{1,147}}} | {} | {} - {} | {} | {foo,new_word} | {} - {16,25,23} | {} | {foobar,new_word} | {{elt2}} -(3 rows) - -SELECT b[1:1][2][2], - d[1:1][2] - FROM arrtest; - b | d ------------------------+--------------- - {{{113,142},{1,147}}} | {} - {} | {} - {} | {{elt1,elt2}} -(3 rows) - -INSERT INTO arrtest(a) VALUES('{1,null,3}'); -SELECT a FROM arrtest; - a ---------------- - {16,25,3,4,5} - {} - {16,25,23} - {1,NULL,3} -(4 rows) - -UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL; -SELECT a FROM arrtest WHERE a[2] IS NULL; - a ------------------ - [4:4]={NULL} - {1,NULL,3,NULL} -(2 rows) - -DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL; -SELECT a,b,c FROM arrtest; - a | b | c ----------------+-----------------------+------------------- - {16,25,3,4,5} | {{{113,142},{1,147}}} | {} - {16,25,23} | {{3,4},{4,5}} | {foobar,new_word} - [4:4]={NULL} | {3,4} | {foo,new_word} -(3 rows) - --- test mixed slice/scalar subscripting -select '{{1,2,3},{4,5,6},{7,8,9}}'::int[]; - int4 ---------------------------- - {{1,2,3},{4,5,6},{7,8,9}} -(1 row) - -select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; - int4 ---------------- - {{1,2},{4,5}} -(1 row) - -select '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[]; - int4 --------------------------------------- - [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}} -(1 row) - -select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; - int4 ---------------- - {{5,6},{8,9}} -(1 row) - --- --- check subscription corner cases --- --- More subscripts than MAXDIM (6) -SELECT ('{}'::int[])[1][2][3][4][5][6][7]; -ERROR: number of array dimensions (7) exceeds the maximum allowed (6) --- NULL index yields NULL when selecting -SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL][1]; - int4 ------- - -(1 row) - -SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL:1][1]; - int4 ------- - -(1 row) - -SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][1:NULL][1]; - int4 ------- - -(1 row) - --- NULL index in assignment is an error -UPDATE arrtest - SET c[NULL] = '{"can''t assign"}' - WHERE array_dims(c) is not null; -ERROR: array subscript in assignment must not be null -UPDATE arrtest - SET c[NULL:1] = '{"can''t assign"}' - WHERE array_dims(c) is not null; -ERROR: array subscript in assignment must not be null -UPDATE arrtest - SET c[1:NULL] = '{"can''t assign"}' - WHERE array_dims(c) is not null; -ERROR: array subscript in assignment must not be null --- Un-subscriptable type -SELECT (now())[1]; -ERROR: cannot subscript type timestamp with time zone because it does not support subscripting -LINE 1: SELECT (now())[1]; - ^ --- test slices with empty lower and/or upper index -CREATE TEMP TABLE arrtest_s ( - a int2[], - b int2[][] -); -INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}'); -INSERT INTO arrtest_s VALUES ('[0:4]={1,2,3,4,5}', '[0:2][0:2]={{1,2,3}, {4,5,6}, {7,8,9}}'); -SELECT * FROM arrtest_s; - a | b --------------------+-------------------------------------- - {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}} - [0:4]={1,2,3,4,5} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}} -(2 rows) - -SELECT a[:3], b[:2][:2] FROM arrtest_s; - a | b ------------+--------------------------- - {1,2,3} | {{1,2},{4,5}} - {1,2,3,4} | {{1,2,3},{4,5,6},{7,8,9}} -(2 rows) - -SELECT a[2:], b[2:][2:] FROM arrtest_s; - a | b ------------+--------------- - {2,3,4,5} | {{5,6},{8,9}} - {3,4,5} | {{9}} -(2 rows) - -SELECT a[:], b[:] FROM arrtest_s; - a | b --------------+--------------------------- - {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}} - {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}} -(2 rows) - --- updates -UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14,15}}' - WHERE array_lower(a,1) = 1; -SELECT * FROM arrtest_s; - a | b --------------------+-------------------------------------- - [0:4]={1,2,3,4,5} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}} - {11,12,13,4,5} | {{11,12,3},{14,15,6},{7,8,9}} -(2 rows) - -UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28,29}}'; -SELECT * FROM arrtest_s; - a | b ----------------------+--------------------------------------- - [0:4]={1,2,3,23,24} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,25}} - {11,12,23,24,25} | {{11,12,3},{14,25,26},{7,28,29}} -(2 rows) - -UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; -SELECT * FROM arrtest_s; - a | b -------------------------+--------------------------------------- - [0:4]={11,12,13,14,15} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,25}} - {11,12,13,14,15} | {{11,12,3},{14,25,26},{7,28,29}} -(2 rows) - -UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; -- fail, too small -ERROR: source array too small -INSERT INTO arrtest_s VALUES(NULL, NULL); -UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; -- fail, no good with null -ERROR: array slice subscript must provide both boundaries -DETAIL: When assigning to a slice of an empty array value, slice boundaries must be fully specified. --- check with fixed-length-array type, such as point -SELECT f1[0:1] FROM POINT_TBL; -ERROR: slices of fixed-length arrays not implemented -SELECT f1[0:] FROM POINT_TBL; -ERROR: slices of fixed-length arrays not implemented -SELECT f1[:1] FROM POINT_TBL; -ERROR: slices of fixed-length arrays not implemented -SELECT f1[:] FROM POINT_TBL; -ERROR: slices of fixed-length arrays not implemented --- subscript assignments to fixed-width result in NULL if previous value is NULL -UPDATE point_tbl SET f1[0] = 10 WHERE f1 IS NULL RETURNING *; - f1 ----- - -(1 row) - -INSERT INTO point_tbl(f1[0]) VALUES(0) RETURNING *; - f1 ----- - -(1 row) - --- NULL assignments get ignored -UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *; - f1 ---------- - (10,10) -(1 row) - --- but non-NULL subscript assignments work -UPDATE point_tbl SET f1[0] = -10, f1[1] = -10 WHERE f1::text = '(10,10)'::point::text RETURNING *; - f1 ------------ - (-10,-10) -(1 row) - --- but not to expand the range -UPDATE point_tbl SET f1[3] = 10 WHERE f1::text = '(-10,-10)'::point::text RETURNING *; -ERROR: array subscript out of range --- --- test array extension --- -CREATE TEMP TABLE arrtest1 (i int[], t text[]); -insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']); -select * from arrtest1; - i | t ---------------+--------------------- - {1,2,NULL,4} | {one,two,NULL,four} -(1 row) - -update arrtest1 set i[2] = 22, t[2] = 'twenty-two'; -select * from arrtest1; - i | t ----------------+---------------------------- - {1,22,NULL,4} | {one,twenty-two,NULL,four} -(1 row) - -update arrtest1 set i[5] = 5, t[5] = 'five'; -select * from arrtest1; - i | t ------------------+--------------------------------- - {1,22,NULL,4,5} | {one,twenty-two,NULL,four,five} -(1 row) - -update arrtest1 set i[8] = 8, t[8] = 'eight'; -select * from arrtest1; - i | t ------------------------------+------------------------------------------------- - {1,22,NULL,4,5,NULL,NULL,8} | {one,twenty-two,NULL,four,five,NULL,NULL,eight} -(1 row) - -update arrtest1 set i[0] = 0, t[0] = 'zero'; -select * from arrtest1; - i | t --------------------------------------+------------------------------------------------------------ - [0:8]={0,1,22,NULL,4,5,NULL,NULL,8} | [0:8]={zero,one,twenty-two,NULL,four,five,NULL,NULL,eight} -(1 row) - -update arrtest1 set i[-3] = -3, t[-3] = 'minus-three'; -select * from arrtest1; - i | t ----------------------------------------------------+----------------------------------------------------------------------------------- - [-3:8]={-3,NULL,NULL,0,1,22,NULL,4,5,NULL,NULL,8} | [-3:8]={minus-three,NULL,NULL,zero,one,twenty-two,NULL,four,five,NULL,NULL,eight} -(1 row) - -update arrtest1 set i[0:2] = array[10,11,12], t[0:2] = array['ten','eleven','twelve']; -select * from arrtest1; - i | t ------------------------------------------------------+--------------------------------------------------------------------------------- - [-3:8]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,8} | [-3:8]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,eight} -(1 row) - -update arrtest1 set i[8:10] = array[18,null,20], t[8:10] = array['p18',null,'p20']; -select * from arrtest1; - i | t ----------------------------------------------------------------+----------------------------------------------------------------------------------------- - [-3:10]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20} | [-3:10]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20} -(1 row) - -update arrtest1 set i[11:12] = array[null,22], t[11:12] = array[null,'p22']; -select * from arrtest1; - i | t ------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------- - [-3:12]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22} | [-3:12]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22} -(1 row) - -update arrtest1 set i[15:16] = array[null,26], t[15:16] = array[null,'p26']; -select * from arrtest1; - i | t ------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------- - [-3:16]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-3:16]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26} -(1 row) - -update arrtest1 set i[-5:-3] = array[-15,-14,-13], t[-5:-3] = array['m15','m14','m13']; -select * from arrtest1; - i | t ---------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------- - [-5:16]={-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-5:16]={m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26} -(1 row) - -update arrtest1 set i[-7:-6] = array[-17,null], t[-7:-6] = array['m17',null]; -select * from arrtest1; - i | t ------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------ - [-7:16]={-17,NULL,-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-7:16]={m17,NULL,m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26} -(1 row) - -update arrtest1 set i[-12:-10] = array[-22,null,-20], t[-12:-10] = array['m22',null,'m20']; -select * from arrtest1; - i | t ------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------ - [-12:16]={-22,NULL,-20,NULL,NULL,-17,NULL,-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-12:16]={m22,NULL,m20,NULL,NULL,m17,NULL,m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26} -(1 row) - -delete from arrtest1; -insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']); -select * from arrtest1; - i | t ---------------+--------------------- - {1,2,NULL,4} | {one,two,NULL,four} -(1 row) - -update arrtest1 set i[0:5] = array[0,1,2,null,4,5], t[0:5] = array['z','p1','p2',null,'p4','p5']; -select * from arrtest1; - i | t -------------------------+---------------------------- - [0:5]={0,1,2,NULL,4,5} | [0:5]={z,p1,p2,NULL,p4,p5} -(1 row) - --- --- array expressions and operators --- --- table creation and INSERTs -CREATE TEMP TABLE arrtest2 (i integer ARRAY[4], f float8[], n numeric[], t text[], d timestamp[]); -INSERT INTO arrtest2 VALUES( - ARRAY[[[113,142],[1,147]]], - ARRAY[1.1,1.2,1.3]::float8[], - ARRAY[1.1,1.2,1.3], - ARRAY[[['aaa','aab'],['aba','abb'],['aca','acb']],[['baa','bab'],['bba','bbb'],['bca','bcb']]], - ARRAY['19620326','19931223','19970117']::timestamp[] -); --- some more test data -CREATE TEMP TABLE arrtest_f (f0 int, f1 text, f2 float8); -insert into arrtest_f values(1,'cat1',1.21); -insert into arrtest_f values(2,'cat1',1.24); -insert into arrtest_f values(3,'cat1',1.18); -insert into arrtest_f values(4,'cat1',1.26); -insert into arrtest_f values(5,'cat1',1.15); -insert into arrtest_f values(6,'cat2',1.15); -insert into arrtest_f values(7,'cat2',1.26); -insert into arrtest_f values(8,'cat2',1.32); -insert into arrtest_f values(9,'cat2',1.30); -CREATE TEMP TABLE arrtest_i (f0 int, f1 text, f2 int); -insert into arrtest_i values(1,'cat1',21); -insert into arrtest_i values(2,'cat1',24); -insert into arrtest_i values(3,'cat1',18); -insert into arrtest_i values(4,'cat1',26); -insert into arrtest_i values(5,'cat1',15); -insert into arrtest_i values(6,'cat2',15); -insert into arrtest_i values(7,'cat2',26); -insert into arrtest_i values(8,'cat2',32); -insert into arrtest_i values(9,'cat2',30); --- expressions -SELECT t.f[1][3][1] AS "131", t.f[2][2][1] AS "221" FROM ( - SELECT ARRAY[[[111,112],[121,122],[131,132]],[[211,212],[221,122],[231,232]]] AS f -) AS t; - 131 | 221 ------+----- - 131 | 221 -(1 row) - -SELECT ARRAY[[[[[['hello'],['world']]]]]]; - array ---------------------------- - {{{{{{hello},{world}}}}}} -(1 row) - -SELECT ARRAY[ARRAY['hello'],ARRAY['world']]; - array -------------------- - {{hello},{world}} -(1 row) - -SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; - ARRAY ------------------------------------------------ - {1.15,1.15,1.18,1.21,1.24,1.26,1.26,1.3,1.32} -(1 row) - --- with nulls -SELECT '{1,null,3}'::int[]; - int4 ------------- - {1,NULL,3} -(1 row) - -SELECT ARRAY[1,NULL,3]; - array ------------- - {1,NULL,3} -(1 row) - --- functions -SELECT array_append(array[42], 6) AS "{42,6}"; - {42,6} --------- - {42,6} -(1 row) - -SELECT array_prepend(6, array[42]) AS "{6,42}"; - {6,42} --------- - {6,42} -(1 row) - -SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{1,2,3,4}"; - {1,2,3,4} ------------ - {1,2,3,4} -(1 row) - -SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}"; - {{1,2},{3,4},{5,6}} ---------------------- - {{1,2},{3,4},{5,6}} -(1 row) - -SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; - {{3,4},{5,6},{1,2}} ---------------------- - {{3,4},{5,6},{1,2}} -(1 row) - -SELECT array_position(ARRAY[1,2,3,4,5], 4); - array_position ----------------- - 4 -(1 row) - -SELECT array_position(ARRAY[5,3,4,2,1], 4); - array_position ----------------- - 3 -(1 row) - -SELECT array_position(ARRAY[[1,2],[3,4]], 3); -ERROR: searching for elements in multidimensional arrays is not supported -SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon'); - array_position ----------------- - 2 -(1 row) - -SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'sat'); - array_position ----------------- - 7 -(1 row) - -SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], NULL); - array_position ----------------- - -(1 row) - -SELECT array_position(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], NULL); - array_position ----------------- - 6 -(1 row) - -SELECT array_position(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], 'sat'); - array_position ----------------- - 8 -(1 row) - -SELECT array_positions(NULL, 10); - array_positions ------------------ - -(1 row) - -SELECT array_positions(NULL, NULL::int); - array_positions ------------------ - -(1 row) - -SELECT array_positions(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], 4); - array_positions ------------------ - {4,10} -(1 row) - -SELECT array_positions(ARRAY[[1,2],[3,4]], 4); -ERROR: searching for elements in multidimensional arrays is not supported -SELECT array_positions(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], NULL); - array_positions ------------------ - {} -(1 row) - -SELECT array_positions(ARRAY[1,2,3,NULL,5,6,1,2,3,NULL,5,6], NULL); - array_positions ------------------ - {4,10} -(1 row) - -SELECT array_length(array_positions(ARRAY(SELECT 'AAAAAAAAAAAAAAAAAAAAAAAAA'::text || i % 10 - FROM generate_series(1,100) g(i)), - 'AAAAAAAAAAAAAAAAAAAAAAAAA5'), 1); - array_length --------------- - 10 -(1 row) - -DO $$ -DECLARE - o int; - a int[] := ARRAY[1,2,3,2,3,1,2]; -BEGIN - o := array_position(a, 2); - WHILE o IS NOT NULL - LOOP - RAISE NOTICE '%', o; - o := array_position(a, 2, o + 1); - END LOOP; -END -$$ LANGUAGE plpgsql; -NOTICE: 2 -NOTICE: 4 -NOTICE: 7 -SELECT array_position('[2:4]={1,2,3}'::int[], 1); - array_position ----------------- - 2 -(1 row) - -SELECT array_positions('[2:4]={1,2,3}'::int[], 1); - array_positions ------------------ - {2} -(1 row) - -SELECT - array_position(ids, (1, 1)), - array_positions(ids, (1, 1)) - FROM -(VALUES - (ARRAY[(0, 0), (1, 1)]), - (ARRAY[(1, 1)]) -) AS f (ids); - array_position | array_positions -----------------+----------------- - 2 | {2} - 1 | {1} -(2 rows) - --- operators -SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]]; - a ---------------- - {16,25,3,4,5} -(1 row) - -SELECT NOT ARRAY[1.1,1.2,1.3] = ARRAY[1.1,1.2,1.3] AS "FALSE"; - FALSE -------- - f -(1 row) - -SELECT ARRAY[1,2] || 3 AS "{1,2,3}"; - {1,2,3} ---------- - {1,2,3} -(1 row) - -SELECT 0 || ARRAY[1,2] AS "{0,1,2}"; - {0,1,2} ---------- - {0,1,2} -(1 row) - -SELECT ARRAY[1,2] || ARRAY[3,4] AS "{1,2,3,4}"; - {1,2,3,4} ------------ - {1,2,3,4} -(1 row) - -SELECT ARRAY[[['hello','world']]] || ARRAY[[['happy','birthday']]] AS "ARRAY"; - ARRAY --------------------------------------- - {{{hello,world}},{{happy,birthday}}} -(1 row) - -SELECT ARRAY[[1,2],[3,4]] || ARRAY[5,6] AS "{{1,2},{3,4},{5,6}}"; - {{1,2},{3,4},{5,6}} ---------------------- - {{1,2},{3,4},{5,6}} -(1 row) - -SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}"; - {0,0,1,1,2,2} ---------------- - {0,0,1,1,2,2} -(1 row) - -SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}"; - {0,1,2,3} ------------ - {0,1,2,3} -(1 row) - -SELECT ARRAY[1.1] || ARRAY[2,3,4]; - ?column? -------------- - {1.1,2,3,4} -(1 row) - -SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523} -(6 rows) - -SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523} -(6 rows) - -SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576} - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938} - 53 | {38,17} | {AAAAAAAAAAA21658} - 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} -(8 rows) - -SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576} - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938} - 53 | {38,17} | {AAAAAAAAAAA21658} - 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} -(8 rows) - -SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} -(3 rows) - -SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------ - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576} - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938} - 53 | {38,17} | {AAAAAAAAAAA21658} - 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523} -(11 rows) - -SELECT * FROM array_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno; - seqno | i | t --------+---------------+---------------------------------------------------------------------------------------------------------------------------- - 40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 101 | {} | {} -(4 rows) - -SELECT * FROM array_op_test WHERE i = '{}' ORDER BY seqno; - seqno | i | t --------+----+---- - 101 | {} | {} -(1 row) - -SELECT * FROM array_op_test WHERE i @> '{}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038} - 2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793} - 3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246} - 4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557} - 5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104} - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946} - 8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407} - 9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000} - 10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249} - 11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557} - 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576} - 13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658} - 14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909} - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 16 | {14,63,85,11} | {AAAAAA66777} - 17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356} - 18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374} - 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938} - 20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494} - 21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420} - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562} - 24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219} - 25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449} - 26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009} - 27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254} - 28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601} - 29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194} - 30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240} - 31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938} - 32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533} - 33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796} - 34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242} - 35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084} - 36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598} - 37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611} - 38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387} - 39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620} - 40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623} - 41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666} - 42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587} - 43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946} - 44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621} - 47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466} - 48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037} - 49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587} - 50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955} - 51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452} - 52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862} - 53 | {38,17} | {AAAAAAAAAAA21658} - 54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322} - 55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737} - 56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406} - 57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415} - 58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119} - 59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955} - 60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875} - 61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804} - 62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617} - 63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938} - 64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240} - 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012} - 66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836} - 67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946} - 68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643} - 69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955} - 70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242} - 71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557} - 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407} - 73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052} - 76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} - 80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007} - 81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121} - 82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104} - 83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119} - 84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183} - 85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154} - 86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176} - 87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505} - 88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} - 90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526} - 91 | {78} | {AAAAAAAAAAAAA62007,AAA99043} - 92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089} - 93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383} - 94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587} - 95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483} - 96 | {23,97,43} | {AAAAAAAAAA646,A87088} - 97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356} - 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523} - 101 | {} | {} - 102 | {NULL} | {NULL} -(102 rows) - -SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno; - seqno | i | t --------+---+--- -(0 rows) - -SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno; - seqno | i | t --------+----+---- - 101 | {} | {} -(1 row) - -SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; - seqno | i | t --------+--------+-------- - 102 | {NULL} | {NULL} -(1 row) - -SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno; - seqno | i | t --------+---+--- -(0 rows) - -SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno; - seqno | i | t --------+---+--- -(0 rows) - -SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; - seqno | i | t --------+----+---- - 101 | {} | {} -(1 row) - -SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; - seqno | i | t --------+-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------- - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} -(4 rows) - -SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno; - seqno | i | t --------+-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------- - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} -(4 rows) - -SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno; - seqno | i | t --------+------------------+-------------------------------------------------------------------- - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} - 96 | {23,97,43} | {AAAAAAAAAA646,A87088} -(3 rows) - -SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno; - seqno | i | t --------+------------------+-------------------------------------------------------------------- - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} - 96 | {23,97,43} | {AAAAAAAAAA646,A87088} -(3 rows) - -SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; - seqno | i | t --------+------+-------------------------------------------------------------------- - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} -(1 row) - -SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; - seqno | i | t --------+-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------- - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} - 96 | {23,97,43} | {AAAAAAAAAA646,A87088} -(6 rows) - -SELECT * FROM array_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno; - seqno | i | t --------+--------------------+----------------------------------------------------------------------------------------------------------- - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 101 | {} | {} -(3 rows) - -SELECT * FROM array_op_test WHERE t = '{}' ORDER BY seqno; - seqno | i | t --------+----+---- - 101 | {} | {} -(1 row) - -SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno; - seqno | i | t --------+---------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038} - 2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793} - 3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246} - 4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557} - 5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104} - 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657} - 7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946} - 8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407} - 9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000} - 10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249} - 11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557} - 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576} - 13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658} - 14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909} - 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309} - 16 | {14,63,85,11} | {AAAAAA66777} - 17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356} - 18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374} - 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938} - 20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494} - 21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420} - 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} - 23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562} - 24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219} - 25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449} - 26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009} - 27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254} - 28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601} - 29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194} - 30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240} - 31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938} - 32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533} - 33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796} - 34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242} - 35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084} - 36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598} - 37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611} - 38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387} - 39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620} - 40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623} - 41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666} - 42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587} - 43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946} - 44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232} - 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611} - 46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621} - 47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466} - 48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037} - 49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587} - 50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955} - 51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452} - 52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862} - 53 | {38,17} | {AAAAAAAAAAA21658} - 54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322} - 55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737} - 56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406} - 57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415} - 58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119} - 59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955} - 60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875} - 61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804} - 62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617} - 63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938} - 64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240} - 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012} - 66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836} - 67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946} - 68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643} - 69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955} - 70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242} - 71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557} - 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407} - 73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598} - 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956} - 75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052} - 76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062} - 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066} - 78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620} - 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908} - 80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007} - 81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121} - 82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104} - 83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119} - 84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183} - 85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154} - 86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176} - 87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505} - 88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433} - 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673} - 90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526} - 91 | {78} | {AAAAAAAAAAAAA62007,AAA99043} - 92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089} - 93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383} - 94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587} - 95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483} - 96 | {23,97,43} | {AAAAAAAAAA646,A87088} - 97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643} - 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845} - 99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356} - 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523} - 101 | {} | {} - 102 | {NULL} | {NULL} -(102 rows) - -SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno; - seqno | i | t --------+---+--- -(0 rows) - -SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; - seqno | i | t --------+----+---- - 101 | {} | {} -(1 row) - --- array casts -SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; - {1,2,3} ---------- - {1,2,3} -(1 row) - -SELECT pg_typeof(ARRAY[1,2,3]::text[]::int[]::float8[]) AS "double precision[]"; - double precision[] --------------------- - double precision[] -(1 row) - -SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}"; - {{a,bc},{def,hijk}} ---------------------- - {{a,bc},{def,hijk}} -(1 row) - -SELECT pg_typeof(ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[]) AS "character varying[]"; - character varying[] ---------------------- - character varying[] -(1 row) - -SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"; - {{{{{{a,bb,ccc}}}}}} ----------------------- - {{{{{{a,bb,ccc}}}}}} -(1 row) - -SELECT NULL::text[]::int[] AS "NULL"; - NULL ------- - -(1 row) - --- scalar op any/all (array) -select 33 = any ('{1,2,3}'); - ?column? ----------- - f -(1 row) - -select 33 = any ('{1,2,33}'); - ?column? ----------- - t -(1 row) - -select 33 = all ('{1,2,33}'); - ?column? ----------- - f -(1 row) - -select 33 >= all ('{1,2,33}'); - ?column? ----------- - t -(1 row) - --- boundary cases -select null::int >= all ('{1,2,33}'); - ?column? ----------- - -(1 row) - -select null::int >= all ('{}'); - ?column? ----------- - t -(1 row) - -select null::int >= any ('{}'); - ?column? ----------- - f -(1 row) - --- cross-datatype -select 33.4 = any (array[1,2,3]); - ?column? ----------- - f -(1 row) - -select 33.4 > all (array[1,2,3]); - ?column? ----------- - t -(1 row) - --- errors -select 33 * any ('{1,2,3}'); -ERROR: op ANY/ALL (array) requires operator to yield boolean -LINE 1: select 33 * any ('{1,2,3}'); - ^ -select 33 * any (44); -ERROR: op ANY/ALL (array) requires array on right side -LINE 1: select 33 * any (44); - ^ --- nulls -select 33 = any (null::int[]); - ?column? ----------- - -(1 row) - -select null::int = any ('{1,2,3}'); - ?column? ----------- - -(1 row) - -select 33 = any ('{1,null,3}'); - ?column? ----------- - -(1 row) - -select 33 = any ('{1,null,33}'); - ?column? ----------- - t -(1 row) - -select 33 = all (null::int[]); - ?column? ----------- - -(1 row) - -select null::int = all ('{1,2,3}'); - ?column? ----------- - -(1 row) - -select 33 = all ('{1,null,3}'); - ?column? ----------- - f -(1 row) - -select 33 = all ('{33,null,33}'); - ?column? ----------- - -(1 row) - --- nulls later in the bitmap -SELECT -1 != ALL(ARRAY(SELECT NULLIF(g.i, 900) FROM generate_series(1,1000) g(i))); - ?column? ----------- - -(1 row) - --- test indexes on arrays -create temp table arr_tbl (f1 int[] unique); -insert into arr_tbl values ('{1,2,3}'); -insert into arr_tbl values ('{1,2}'); --- failure expected: -insert into arr_tbl values ('{1,2,3}'); -ERROR: duplicate key value violates unique constraint "arr_tbl_f1_key" -DETAIL: Key (f1)=({1,2,3}) already exists. -insert into arr_tbl values ('{2,3,4}'); -insert into arr_tbl values ('{1,5,3}'); -insert into arr_tbl values ('{1,2,10}'); -set enable_seqscan to off; -set enable_bitmapscan to off; -select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}'; - f1 ----------- - {1,2,10} - {1,5,3} -(2 rows) - -select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}'; - f1 ----------- - {1,2,3} - {1,2,10} -(2 rows) - --- test ON CONFLICT DO UPDATE with arrays -create temp table arr_pk_tbl (pk int4 primary key, f1 int[]); -insert into arr_pk_tbl values (1, '{1,2,3}'); -insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk) - do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3] - returning pk, f1; - pk | f1 -----+--------- - 1 | {3,2,5} -(1 row) - -insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk) - do update set f1[1] = excluded.f1[1], - f1[2] = excluded.f1[2], - f1[3] = excluded.f1[3] - returning pk, f1; - pk | f1 -----+------------ - 1 | {6,7,NULL} -(1 row) - --- note: if above selects don't produce the expected tuple order, --- then you didn't get an indexscan plan, and something is busted. -reset enable_seqscan; -reset enable_bitmapscan; --- test [not] (like|ilike) (any|all) (...) -select 'foo' like any (array['%a', '%o']); -- t - ?column? ----------- - t -(1 row) - -select 'foo' like any (array['%a', '%b']); -- f - ?column? ----------- - f -(1 row) - -select 'foo' like all (array['f%', '%o']); -- t - ?column? ----------- - t -(1 row) - -select 'foo' like all (array['f%', '%b']); -- f - ?column? ----------- - f -(1 row) - -select 'foo' not like any (array['%a', '%b']); -- t - ?column? ----------- - t -(1 row) - -select 'foo' not like all (array['%a', '%o']); -- f - ?column? ----------- - f -(1 row) - -select 'foo' ilike any (array['%A', '%O']); -- t - ?column? ----------- - t -(1 row) - -select 'foo' ilike all (array['F%', '%O']); -- t - ?column? ----------- - t -(1 row) - --- --- General array parser tests --- --- none of the following should be accepted -select '{{1,{2}},{2,3}}'::text[]; -ERROR: malformed array literal: "{{1,{2}},{2,3}}" -LINE 1: select '{{1,{2}},{2,3}}'::text[]; - ^ -DETAIL: Unexpected "{" character. -select '{{},{}}'::text[]; -ERROR: malformed array literal: "{{},{}}" -LINE 1: select '{{},{}}'::text[]; - ^ -DETAIL: Unexpected "}" character. -select E'{{1,2},\\{2,3}}'::text[]; -ERROR: malformed array literal: "{{1,2},\{2,3}}" -LINE 1: select E'{{1,2},\\{2,3}}'::text[]; - ^ -DETAIL: Unexpected "\" character. -select '{{"1 2" x},{3}}'::text[]; -ERROR: malformed array literal: "{{"1 2" x},{3}}" -LINE 1: select '{{"1 2" x},{3}}'::text[]; - ^ -DETAIL: Unexpected array element. -select '{}}'::text[]; -ERROR: malformed array literal: "{}}" -LINE 1: select '{}}'::text[]; - ^ -DETAIL: Junk after closing right brace. -select '{ }}'::text[]; -ERROR: malformed array literal: "{ }}" -LINE 1: select '{ }}'::text[]; - ^ -DETAIL: Junk after closing right brace. -select array[]; -ERROR: cannot determine type of empty array -LINE 1: select array[]; - ^ -HINT: Explicitly cast to the desired type, for example ARRAY[]::integer[]. --- none of the above should be accepted --- all of the following should be accepted -select '{}'::text[]; - text ------- - {} -(1 row) - -select '{{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}'::text[]; - text ------------------------------------------------ - {{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}} -(1 row) - -select '{0 second ,0 second}'::interval[]; - interval ---------------- - {"@ 0","@ 0"} -(1 row) - -select '{ { "," } , { 3 } }'::text[]; - text -------------- - {{","},{3}} -(1 row) - -select ' { { " 0 second " , 0 second } }'::text[]; - text -------------------------------- - {{" 0 second ","0 second"}} -(1 row) - -select '{ - 0 second, - @ 1 hour @ 42 minutes @ 20 seconds - }'::interval[]; - interval ------------------------------------- - {"@ 0","@ 1 hour 42 mins 20 secs"} -(1 row) - -select array[]::text[]; - array -------- - {} -(1 row) - -select '[0:1]={1.1,2.2}'::float8[]; - float8 ------------------ - [0:1]={1.1,2.2} -(1 row) - --- all of the above should be accepted --- tests for array aggregates -CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]); -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{1,2,3,4}','{{grey,red},{blue,blue}}','{1.6, 0.0}'); -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{1,2,3}','{{grey,red},{grey,blue}}','{1.6}'); -SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; - max | min | max | min | max | min ------------+---------+--------------------------+--------------------------+---------+------- - {1,2,3,4} | {1,2,3} | {{grey,red},{grey,blue}} | {{grey,red},{blue,blue}} | {1.6,0} | {1.6} -(1 row) - -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{3,3,2,4,5,6}','{{white,yellow},{pink,orange}}','{2.1,3.3,1.8,1.7,1.6}'); -SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; - max | min | max | min | max | min ----------------+---------+--------------------------------+--------------------------+-----------------------+------- - {3,3,2,4,5,6} | {1,2,3} | {{white,yellow},{pink,orange}} | {{grey,red},{blue,blue}} | {2.1,3.3,1.8,1.7,1.6} | {1.6} -(1 row) - -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{2}','{{black,red},{green,orange}}','{1.6,2.2,2.6,0.4}'); -SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; - max | min | max | min | max | min ----------------+---------+--------------------------------+------------------------------+-----------------------+------- - {3,3,2,4,5,6} | {1,2,3} | {{white,yellow},{pink,orange}} | {{black,red},{green,orange}} | {2.1,3.3,1.8,1.7,1.6} | {1.6} -(1 row) - -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{4,2,6,7,8,1}','{{red},{black},{purple},{blue},{blue}}',NULL); -SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; - max | min | max | min | max | min ----------------+---------+--------------------------------+------------------------------+-----------------------+------- - {4,2,6,7,8,1} | {1,2,3} | {{white,yellow},{pink,orange}} | {{black,red},{green,orange}} | {2.1,3.3,1.8,1.7,1.6} | {1.6} -(1 row) - -INSERT INTO arraggtest (f1, f2, f3) VALUES -('{}','{{pink,white,blue,red,grey,orange}}','{2.1,1.87,1.4,2.2}'); -SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; - max | min | max | min | max | min ----------------+-----+--------------------------------+------------------------------+-----------------------+------- - {4,2,6,7,8,1} | {} | {{white,yellow},{pink,orange}} | {{black,red},{green,orange}} | {2.1,3.3,1.8,1.7,1.6} | {1.6} -(1 row) - --- A few simple tests for arrays of composite types -create type comptype as (f1 int, f2 text); -create table comptable (c1 comptype, c2 comptype[]); --- XXX would like to not have to specify row() construct types here ... -insert into comptable - values (row(1,'foo'), array[row(2,'bar')::comptype, row(3,'baz')::comptype]); --- check that implicitly named array type _comptype isn't a problem -create type _comptype as enum('fooey'); -select * from comptable; - c1 | c2 ----------+----------------------- - (1,foo) | {"(2,bar)","(3,baz)"} -(1 row) - -select c2[2].f2 from comptable; - f2 ------ - baz -(1 row) - -drop type _comptype; -drop table comptable; -drop type comptype; -create or replace function unnest1(anyarray) -returns setof anyelement as $$ -select $1[s] from generate_subscripts($1,1) g(s); -$$ language sql immutable; -create or replace function unnest2(anyarray) -returns setof anyelement as $$ -select $1[s1][s2] from generate_subscripts($1,1) g1(s1), - generate_subscripts($1,2) g2(s2); -$$ language sql immutable; -select * from unnest1(array[1,2,3]); - unnest1 ---------- - 1 - 2 - 3 -(3 rows) - -select * from unnest2(array[[1,2,3],[4,5,6]]); - unnest2 ---------- - 1 - 2 - 3 - 4 - 5 - 6 -(6 rows) - -drop function unnest1(anyarray); -drop function unnest2(anyarray); -select array_fill(null::integer, array[3,3],array[2,2]); - array_fill ------------------------------------------------------------------ - [2:4][2:4]={{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} -(1 row) - -select array_fill(null::integer, array[3,3]); - array_fill ------------------------------------------------------- - {{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} -(1 row) - -select array_fill(null::text, array[3,3],array[2,2]); - array_fill ------------------------------------------------------------------ - [2:4][2:4]={{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} -(1 row) - -select array_fill(null::text, array[3,3]); - array_fill ------------------------------------------------------- - {{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} -(1 row) - -select array_fill(7, array[3,3],array[2,2]); - array_fill --------------------------------------- - [2:4][2:4]={{7,7,7},{7,7,7},{7,7,7}} -(1 row) - -select array_fill(7, array[3,3]); - array_fill ---------------------------- - {{7,7,7},{7,7,7},{7,7,7}} -(1 row) - -select array_fill('juhu'::text, array[3,3],array[2,2]); - array_fill ------------------------------------------------------------------ - [2:4][2:4]={{juhu,juhu,juhu},{juhu,juhu,juhu},{juhu,juhu,juhu}} -(1 row) - -select array_fill('juhu'::text, array[3,3]); - array_fill ------------------------------------------------------- - {{juhu,juhu,juhu},{juhu,juhu,juhu},{juhu,juhu,juhu}} -(1 row) - -select a, a = '{}' as is_eq, array_dims(a) - from (select array_fill(42, array[0]) as a) ss; - a | is_eq | array_dims -----+-------+------------ - {} | t | -(1 row) - -select a, a = '{}' as is_eq, array_dims(a) - from (select array_fill(42, '{}') as a) ss; - a | is_eq | array_dims -----+-------+------------ - {} | t | -(1 row) - -select a, a = '{}' as is_eq, array_dims(a) - from (select array_fill(42, '{}', '{}') as a) ss; - a | is_eq | array_dims -----+-------+------------ - {} | t | -(1 row) - --- raise exception -select array_fill(1, null, array[2,2]); -ERROR: dimension array or low bound array cannot be null -select array_fill(1, array[2,2], null); -ERROR: dimension array or low bound array cannot be null -select array_fill(1, array[2,2], '{}'); -ERROR: wrong number of array subscripts -DETAIL: Low bound array has different size than dimensions array. -select array_fill(1, array[3,3], array[1,1,1]); -ERROR: wrong number of array subscripts -DETAIL: Low bound array has different size than dimensions array. -select array_fill(1, array[1,2,null]); -ERROR: dimension values cannot be null -select array_fill(1, array[[1,2],[3,4]]); -ERROR: wrong number of array subscripts -DETAIL: Dimension array must be one dimensional. -select string_to_array('1|2|3', '|'); - string_to_array ------------------ - {1,2,3} -(1 row) - -select string_to_array('1|2|3|', '|'); - string_to_array ------------------ - {1,2,3,""} -(1 row) - -select string_to_array('1||2|3||', '||'); - string_to_array ------------------ - {1,2|3,""} -(1 row) - -select string_to_array('1|2|3', ''); - string_to_array ------------------ - {1|2|3} -(1 row) - -select string_to_array('', '|'); - string_to_array ------------------ - {} -(1 row) - -select string_to_array('1|2|3', NULL); - string_to_array ------------------ - {1,|,2,|,3} -(1 row) - -select string_to_array(NULL, '|') IS NULL; - ?column? ----------- - t -(1 row) - -select string_to_array('abc', ''); - string_to_array ------------------ - {abc} -(1 row) - -select string_to_array('abc', '', 'abc'); - string_to_array ------------------ - {NULL} -(1 row) - -select string_to_array('abc', ','); - string_to_array ------------------ - {abc} -(1 row) - -select string_to_array('abc', ',', 'abc'); - string_to_array ------------------ - {NULL} -(1 row) - -select string_to_array('1,2,3,4,,6', ','); - string_to_array ------------------ - {1,2,3,4,"",6} -(1 row) - -select string_to_array('1,2,3,4,,6', ',', ''); - string_to_array ------------------- - {1,2,3,4,NULL,6} -(1 row) - -select string_to_array('1,2,3,4,*,6', ',', '*'); - string_to_array ------------------- - {1,2,3,4,NULL,6} -(1 row) - -select v, v is null as "is null" from string_to_table('1|2|3', '|') g(v); - v | is null ----+--------- - 1 | f - 2 | f - 3 | f -(3 rows) - -select v, v is null as "is null" from string_to_table('1|2|3|', '|') g(v); - v | is null ----+--------- - 1 | f - 2 | f - 3 | f - | f -(4 rows) - -select v, v is null as "is null" from string_to_table('1||2|3||', '||') g(v); - v | is null ------+--------- - 1 | f - 2|3 | f - | f -(3 rows) - -select v, v is null as "is null" from string_to_table('1|2|3', '') g(v); - v | is null --------+--------- - 1|2|3 | f -(1 row) - -select v, v is null as "is null" from string_to_table('', '|') g(v); - v | is null ----+--------- -(0 rows) - -select v, v is null as "is null" from string_to_table('1|2|3', NULL) g(v); - v | is null ----+--------- - 1 | f - | | f - 2 | f - | | f - 3 | f -(5 rows) - -select v, v is null as "is null" from string_to_table(NULL, '|') g(v); - v | is null ----+--------- -(0 rows) - -select v, v is null as "is null" from string_to_table('abc', '') g(v); - v | is null ------+--------- - abc | f -(1 row) - -select v, v is null as "is null" from string_to_table('abc', '', 'abc') g(v); - v | is null ----+--------- - | t -(1 row) - -select v, v is null as "is null" from string_to_table('abc', ',') g(v); - v | is null ------+--------- - abc | f -(1 row) - -select v, v is null as "is null" from string_to_table('abc', ',', 'abc') g(v); - v | is null ----+--------- - | t -(1 row) - -select v, v is null as "is null" from string_to_table('1,2,3,4,,6', ',') g(v); - v | is null ----+--------- - 1 | f - 2 | f - 3 | f - 4 | f - | f - 6 | f -(6 rows) - -select v, v is null as "is null" from string_to_table('1,2,3,4,,6', ',', '') g(v); - v | is null ----+--------- - 1 | f - 2 | f - 3 | f - 4 | f - | t - 6 | f -(6 rows) - -select v, v is null as "is null" from string_to_table('1,2,3,4,*,6', ',', '*') g(v); - v | is null ----+--------- - 1 | f - 2 | f - 3 | f - 4 | f - | t - 6 | f -(6 rows) - -select array_to_string(NULL::int4[], ',') IS NULL; - ?column? ----------- - t -(1 row) - -select array_to_string('{}'::int4[], ','); - array_to_string ------------------ - -(1 row) - -select array_to_string(array[1,2,3,4,NULL,6], ','); - array_to_string ------------------ - 1,2,3,4,6 -(1 row) - -select array_to_string(array[1,2,3,4,NULL,6], ',', '*'); - array_to_string ------------------ - 1,2,3,4,*,6 -(1 row) - -select array_to_string(array[1,2,3,4,NULL,6], NULL); - array_to_string ------------------ - -(1 row) - -select array_to_string(array[1,2,3,4,NULL,6], ',', NULL); - array_to_string ------------------ - 1,2,3,4,6 -(1 row) - -select array_to_string(string_to_array('1|2|3', '|'), '|'); - array_to_string ------------------ - 1|2|3 -(1 row) - -select array_length(array[1,2,3], 1); - array_length --------------- - 3 -(1 row) - -select array_length(array[[1,2,3], [4,5,6]], 0); - array_length --------------- - -(1 row) - -select array_length(array[[1,2,3], [4,5,6]], 1); - array_length --------------- - 2 -(1 row) - -select array_length(array[[1,2,3], [4,5,6]], 2); - array_length --------------- - 3 -(1 row) - -select array_length(array[[1,2,3], [4,5,6]], 3); - array_length --------------- - -(1 row) - -select cardinality(NULL::int[]); - cardinality -------------- - -(1 row) - -select cardinality('{}'::int[]); - cardinality -------------- - 0 -(1 row) - -select cardinality(array[1,2,3]); - cardinality -------------- - 3 -(1 row) - -select cardinality('[2:4]={5,6,7}'::int[]); - cardinality -------------- - 3 -(1 row) - -select cardinality('{{1,2}}'::int[]); - cardinality -------------- - 2 -(1 row) - -select cardinality('{{1,2},{3,4},{5,6}}'::int[]); - cardinality -------------- - 6 -(1 row) - -select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]); - cardinality -------------- - 8 -(1 row) - --- array_agg(anynonarray) -select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss; - array_agg --------------------------------------- - {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14} -(1 row) - -select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; - array_agg ---------------------------------- - {0,1,2,3,4,5,6,7,8,9,0,1,2,3,4} -(1 row) - -select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; - array_agg ---------------------------------------- - {0,1,2,3,NULL,5,6,7,8,9,0,1,2,3,NULL} -(1 row) - -select array_agg(unique1) from tenk1 where unique1 < -15; - array_agg ------------ - -(1 row) - --- array_agg(anyarray) -select array_agg(ar) - from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar); - array_agg ---------------- - {{1,2},{3,4}} -(1 row) - -select array_agg(distinct ar order by ar desc) - from (select array[i / 2] from generate_series(1,10) a(i)) b(ar); - array_agg ---------------------------- - {{5},{4},{3},{2},{1},{0}} -(1 row) - -select array_agg(ar) - from (select array_agg(array[i, i+1, i-1]) - from generate_series(1,2) a(i)) b(ar); - array_agg ---------------------- - {{{1,2,0},{2,3,1}}} -(1 row) - -select array_agg(array[i+1.2, i+1.3, i+1.4]) from generate_series(1,3) g(i); - array_agg ---------------------------------------------- - {{2.2,2.3,2.4},{3.2,3.3,3.4},{4.2,4.3,4.4}} -(1 row) - -select array_agg(array['Hello', i::text]) from generate_series(9,11) g(i); - array_agg ------------------------------------ - {{Hello,9},{Hello,10},{Hello,11}} -(1 row) - -select array_agg(array[i, nullif(i, 3), i+1]) from generate_series(1,4) g(i); - array_agg --------------------------------------- - {{1,1,2},{2,2,3},{3,NULL,4},{4,4,5}} -(1 row) - --- errors -select array_agg('{}'::int[]) from generate_series(1,2); -ERROR: cannot accumulate empty arrays -select array_agg(null::int[]) from generate_series(1,2); -ERROR: cannot accumulate null arrays -select array_agg(ar) - from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar); -ERROR: cannot accumulate arrays of different dimensionality -select unnest(array[1,2,3]); - unnest --------- - 1 - 2 - 3 -(3 rows) - -select * from unnest(array[1,2,3]); - unnest --------- - 1 - 2 - 3 -(3 rows) - -select unnest(array[1,2,3,4.5]::float8[]); - unnest --------- - 1 - 2 - 3 - 4.5 -(4 rows) - -select unnest(array[1,2,3,4.5]::numeric[]); - unnest --------- - 1 - 2 - 3 - 4.5 -(4 rows) - -select unnest(array[1,2,3,null,4,null,null,5,6]); - unnest --------- - 1 - 2 - 3 - - 4 - - - 5 - 6 -(9 rows) - -select unnest(array[1,2,3,null,4,null,null,5,6]::text[]); - unnest --------- - 1 - 2 - 3 - - 4 - - - 5 - 6 -(9 rows) - -select abs(unnest(array[1,2,null,-3])); - abs ------ - 1 - 2 - - 3 -(4 rows) - -select array_remove(array[1,2,2,3], 2); - array_remove --------------- - {1,3} -(1 row) - -select array_remove(array[1,2,2,3], 5); - array_remove --------------- - {1,2,2,3} -(1 row) - -select array_remove(array[1,NULL,NULL,3], NULL); - array_remove --------------- - {1,3} -(1 row) - -select array_remove(array['A','CC','D','C','RR'], 'RR'); - array_remove --------------- - {A,CC,D,C} -(1 row) - -select array_remove(array[1.0, 2.1, 3.3], 1); - array_remove --------------- - {2.1,3.3} -(1 row) - -select array_remove('{{1,2,2},{1,4,3}}', 2); -- not allowed -ERROR: removing elements from multidimensional arrays is not supported -select array_remove(array['X','X','X'], 'X') = '{}'; - ?column? ----------- - t -(1 row) - -select array_replace(array[1,2,5,4],5,3); - array_replace ---------------- - {1,2,3,4} -(1 row) - -select array_replace(array[1,2,5,4],5,NULL); - array_replace ---------------- - {1,2,NULL,4} -(1 row) - -select array_replace(array[1,2,NULL,4,NULL],NULL,5); - array_replace ---------------- - {1,2,5,4,5} -(1 row) - -select array_replace(array['A','B','DD','B'],'B','CC'); - array_replace ---------------- - {A,CC,DD,CC} -(1 row) - -select array_replace(array[1,NULL,3],NULL,NULL); - array_replace ---------------- - {1,NULL,3} -(1 row) - -select array_replace(array['AB',NULL,'CDE'],NULL,'12'); - array_replace ---------------- - {AB,12,CDE} -(1 row) - --- array(select array-value ...) -select array(select array[i,i/2] from generate_series(1,5) i); - array ---------------------------------- - {{1,0},{2,1},{3,1},{4,2},{5,2}} -(1 row) - -select array(select array['Hello', i::text] from generate_series(9,11) i); - array ------------------------------------ - {{Hello,9},{Hello,10},{Hello,11}} -(1 row) - --- Insert/update on a column that is array of composite -create temp table t1 (f1 int8_tbl[]); -insert into t1 (f1[5].q1) values(42); -select * from t1; - f1 ------------------ - [5:5]={"(42,)"} -(1 row) - -update t1 set f1[5].q2 = 43; -select * from t1; - f1 -------------------- - [5:5]={"(42,43)"} -(1 row) - --- Check that arrays of composites are safely detoasted when needed -create temp table src (f1 text); -insert into src - select string_agg(random()::text,'') from generate_series(1,10000); -create type textandtext as (c1 text, c2 text); -create temp table dest (f1 textandtext[]); -insert into dest select array[row(f1,f1)::textandtext] from src; -select length(md5((f1[1]).c2)) from dest; - length --------- - 32 -(1 row) - -delete from src; -select length(md5((f1[1]).c2)) from dest; - length --------- - 32 -(1 row) - -truncate table src; -drop table src; -select length(md5((f1[1]).c2)) from dest; - length --------- - 32 -(1 row) - -drop table dest; -drop type textandtext; --- Tests for polymorphic-array form of width_bucket() --- this exercises the varwidth and float8 code paths -SELECT - op, - width_bucket(op::numeric, ARRAY[1, 3, 5, 10.0]::numeric[]) AS wb_n1, - width_bucket(op::numeric, ARRAY[0, 5.5, 9.99]::numeric[]) AS wb_n2, - width_bucket(op::numeric, ARRAY[-6, -5, 2.0]::numeric[]) AS wb_n3, - width_bucket(op::float8, ARRAY[1, 3, 5, 10.0]::float8[]) AS wb_f1, - width_bucket(op::float8, ARRAY[0, 5.5, 9.99]::float8[]) AS wb_f2, - width_bucket(op::float8, ARRAY[-6, -5, 2.0]::float8[]) AS wb_f3 -FROM (VALUES - (-5.2), - (-0.0000000001), - (0.000000000001), - (1), - (1.99999999999999), - (2), - (2.00000000000001), - (3), - (4), - (4.5), - (5), - (5.5), - (6), - (7), - (8), - (9), - (9.99999999999999), - (10), - (10.0000000000001) -) v(op); - op | wb_n1 | wb_n2 | wb_n3 | wb_f1 | wb_f2 | wb_f3 -------------------+-------+-------+-------+-------+-------+------- - -5.2 | 0 | 0 | 1 | 0 | 0 | 1 - -0.0000000001 | 0 | 0 | 2 | 0 | 0 | 2 - 0.000000000001 | 0 | 1 | 2 | 0 | 1 | 2 - 1 | 1 | 1 | 2 | 1 | 1 | 2 - 1.99999999999999 | 1 | 1 | 2 | 1 | 1 | 2 - 2 | 1 | 1 | 3 | 1 | 1 | 3 - 2.00000000000001 | 1 | 1 | 3 | 1 | 1 | 3 - 3 | 2 | 1 | 3 | 2 | 1 | 3 - 4 | 2 | 1 | 3 | 2 | 1 | 3 - 4.5 | 2 | 1 | 3 | 2 | 1 | 3 - 5 | 3 | 1 | 3 | 3 | 1 | 3 - 5.5 | 3 | 2 | 3 | 3 | 2 | 3 - 6 | 3 | 2 | 3 | 3 | 2 | 3 - 7 | 3 | 2 | 3 | 3 | 2 | 3 - 8 | 3 | 2 | 3 | 3 | 2 | 3 - 9 | 3 | 2 | 3 | 3 | 2 | 3 - 9.99999999999999 | 3 | 3 | 3 | 3 | 3 | 3 - 10 | 4 | 3 | 3 | 4 | 3 | 3 - 10.0000000000001 | 4 | 3 | 3 | 4 | 3 | 3 -(19 rows) - --- ensure float8 path handles NaN properly -SELECT - op, - width_bucket(op, ARRAY[1, 3, 9, 'NaN', 'NaN']::float8[]) AS wb -FROM (VALUES - (-5.2::float8), - (4::float8), - (77::float8), - ('NaN'::float8) -) v(op); - op | wb -------+---- - -5.2 | 0 - 4 | 2 - 77 | 3 - NaN | 5 -(4 rows) - --- these exercise the generic fixed-width code path -SELECT - op, - width_bucket(op, ARRAY[1, 3, 5, 10]) AS wb_1 -FROM generate_series(0,11) as op; - op | wb_1 -----+------ - 0 | 0 - 1 | 1 - 2 | 1 - 3 | 2 - 4 | 2 - 5 | 3 - 6 | 3 - 7 | 3 - 8 | 3 - 9 | 3 - 10 | 4 - 11 | 4 -(12 rows) - -SELECT width_bucket(now(), - array['yesterday', 'today', 'tomorrow']::timestamptz[]); - width_bucket --------------- - 2 -(1 row) - --- corner cases -SELECT width_bucket(5, ARRAY[3]); - width_bucket --------------- - 1 -(1 row) - -SELECT width_bucket(5, '{}'); - width_bucket --------------- - 0 -(1 row) - --- error cases -SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); -ERROR: function width_bucket(text, integer[]) does not exist -LINE 1: SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT width_bucket(5, ARRAY[3, 4, NULL]); -ERROR: thresholds array must not contain NULLs -SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); -ERROR: thresholds must be one-dimensional array --- trim_array -SELECT arr, trim_array(arr, 2) -FROM -(VALUES ('{1,2,3,4,5,6}'::bigint[]), - ('{1,2}'), - ('[10:16]={1,2,3,4,5,6,7}'), - ('[-15:-10]={1,2,3,4,5,6}'), - ('{{1,10},{2,20},{3,30},{4,40}}')) v(arr); - arr | trim_array --------------------------------+----------------- - {1,2,3,4,5,6} | {1,2,3,4} - {1,2} | {} - [10:16]={1,2,3,4,5,6,7} | {1,2,3,4,5} - [-15:-10]={1,2,3,4,5,6} | {1,2,3,4} - {{1,10},{2,20},{3,30},{4,40}} | {{1,10},{2,20}} -(5 rows) - -SELECT trim_array(ARRAY[1, 2, 3], -1); -- fail -ERROR: number of elements to trim must be between 0 and 3 -SELECT trim_array(ARRAY[1, 2, 3], 10); -- fail -ERROR: number of elements to trim must be between 0 and 3 +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/btree_index.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/btree_index.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/btree_index.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/btree_index.out 2021-10-30 22:04:11 -0600 @@ -1,345 +1,2 @@ --- --- BTREE_INDEX --- test retrieval of min/max keys for each index --- -SELECT b.* - FROM bt_i4_heap b - WHERE b.seqno < 1; - seqno | random --------+------------ - 0 | 1935401906 -(1 row) - -SELECT b.* - FROM bt_i4_heap b - WHERE b.seqno >= 9999; - seqno | random --------+------------ - 9999 | 1227676208 -(1 row) - -SELECT b.* - FROM bt_i4_heap b - WHERE b.seqno = 4500; - seqno | random --------+------------ - 4500 | 2080851358 -(1 row) - -SELECT b.* - FROM bt_name_heap b - WHERE b.seqno < '1'::name; - seqno | random --------+------------ - 0 | 1935401906 -(1 row) - -SELECT b.* - FROM bt_name_heap b - WHERE b.seqno >= '9999'::name; - seqno | random --------+------------ - 9999 | 1227676208 -(1 row) - -SELECT b.* - FROM bt_name_heap b - WHERE b.seqno = '4500'::name; - seqno | random --------+------------ - 4500 | 2080851358 -(1 row) - -SELECT b.* - FROM bt_txt_heap b - WHERE b.seqno < '1'::text; - seqno | random --------+------------ - 0 | 1935401906 -(1 row) - -SELECT b.* - FROM bt_txt_heap b - WHERE b.seqno >= '9999'::text; - seqno | random --------+------------ - 9999 | 1227676208 -(1 row) - -SELECT b.* - FROM bt_txt_heap b - WHERE b.seqno = '4500'::text; - seqno | random --------+------------ - 4500 | 2080851358 -(1 row) - -SELECT b.* - FROM bt_f8_heap b - WHERE b.seqno < '1'::float8; - seqno | random --------+------------ - 0 | 1935401906 -(1 row) - -SELECT b.* - FROM bt_f8_heap b - WHERE b.seqno >= '9999'::float8; - seqno | random --------+------------ - 9999 | 1227676208 -(1 row) - -SELECT b.* - FROM bt_f8_heap b - WHERE b.seqno = '4500'::float8; - seqno | random --------+------------ - 4500 | 2080851358 -(1 row) - --- --- Check correct optimization of LIKE (special index operator support) --- for both indexscan and bitmapscan cases --- -set enable_seqscan to false; -set enable_indexscan to true; -set enable_bitmapscan to false; -explain (costs off) -select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; - QUERY PLAN ------------------------------------------------------------------------------- - Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc - Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text)) - Filter: (proname ~~ 'RI\_FKey%del'::text) -(3 rows) - -select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; - proname ------------------------- - RI_FKey_cascade_del - RI_FKey_noaction_del - RI_FKey_restrict_del - RI_FKey_setdefault_del - RI_FKey_setnull_del -(5 rows) - -explain (costs off) -select proname from pg_proc where proname ilike '00%foo' order by 1; - QUERY PLAN --------------------------------------------------------------------- - Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc - Index Cond: ((proname >= '00'::text) AND (proname < '01'::text)) - Filter: (proname ~~* '00%foo'::text) -(3 rows) - -select proname from pg_proc where proname ilike '00%foo' order by 1; - proname ---------- -(0 rows) - -explain (costs off) -select proname from pg_proc where proname ilike 'ri%foo' order by 1; - QUERY PLAN ------------------------------------------------------------------ - Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc - Filter: (proname ~~* 'ri%foo'::text) -(2 rows) - -set enable_indexscan to false; -set enable_bitmapscan to true; -explain (costs off) -select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; - QUERY PLAN ------------------------------------------------------------------------------------------- - Sort - Sort Key: proname - -> Bitmap Heap Scan on pg_proc - Filter: (proname ~~ 'RI\_FKey%del'::text) - -> Bitmap Index Scan on pg_proc_proname_args_nsp_index - Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text)) -(6 rows) - -select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; - proname ------------------------- - RI_FKey_cascade_del - RI_FKey_noaction_del - RI_FKey_restrict_del - RI_FKey_setdefault_del - RI_FKey_setnull_del -(5 rows) - -explain (costs off) -select proname from pg_proc where proname ilike '00%foo' order by 1; - QUERY PLAN --------------------------------------------------------------------------------- - Sort - Sort Key: proname - -> Bitmap Heap Scan on pg_proc - Filter: (proname ~~* '00%foo'::text) - -> Bitmap Index Scan on pg_proc_proname_args_nsp_index - Index Cond: ((proname >= '00'::text) AND (proname < '01'::text)) -(6 rows) - -select proname from pg_proc where proname ilike '00%foo' order by 1; - proname ---------- -(0 rows) - -explain (costs off) -select proname from pg_proc where proname ilike 'ri%foo' order by 1; - QUERY PLAN ------------------------------------------------------------------ - Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc - Filter: (proname ~~* 'ri%foo'::text) -(2 rows) - -reset enable_seqscan; -reset enable_indexscan; -reset enable_bitmapscan; --- Also check LIKE optimization with binary-compatible cases -create temp table btree_bpchar (f1 text collate "C"); -create index on btree_bpchar(f1 bpchar_ops) WITH (deduplicate_items=on); -insert into btree_bpchar values ('foo'), ('fool'), ('bar'), ('quux'); --- doesn't match index: -explain (costs off) -select * from btree_bpchar where f1 like 'foo'; - QUERY PLAN -------------------------------- - Seq Scan on btree_bpchar - Filter: (f1 ~~ 'foo'::text) -(2 rows) - -select * from btree_bpchar where f1 like 'foo'; - f1 ------ - foo -(1 row) - -explain (costs off) -select * from btree_bpchar where f1 like 'foo%'; - QUERY PLAN --------------------------------- - Seq Scan on btree_bpchar - Filter: (f1 ~~ 'foo%'::text) -(2 rows) - -select * from btree_bpchar where f1 like 'foo%'; - f1 ------- - foo - fool -(2 rows) - --- these do match the index: -explain (costs off) -select * from btree_bpchar where f1::bpchar like 'foo'; - QUERY PLAN ----------------------------------------------------- - Bitmap Heap Scan on btree_bpchar - Filter: ((f1)::bpchar ~~ 'foo'::text) - -> Bitmap Index Scan on btree_bpchar_f1_idx - Index Cond: ((f1)::bpchar = 'foo'::bpchar) -(4 rows) - -select * from btree_bpchar where f1::bpchar like 'foo'; - f1 ------ - foo -(1 row) - -explain (costs off) -select * from btree_bpchar where f1::bpchar like 'foo%'; - QUERY PLAN ------------------------------------------------------------------------------------------- - Bitmap Heap Scan on btree_bpchar - Filter: ((f1)::bpchar ~~ 'foo%'::text) - -> Bitmap Index Scan on btree_bpchar_f1_idx - Index Cond: (((f1)::bpchar >= 'foo'::bpchar) AND ((f1)::bpchar < 'fop'::bpchar)) -(4 rows) - -select * from btree_bpchar where f1::bpchar like 'foo%'; - f1 ------- - foo - fool -(2 rows) - --- get test coverage for "single value" deduplication strategy: -insert into btree_bpchar select 'foo' from generate_series(1,1500); --- --- Perform unique checking, with and without the use of deduplication --- -CREATE TABLE dedup_unique_test_table (a int) WITH (autovacuum_enabled=false); -CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=on); -CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off); --- Generate enough garbage tuples in index to ensure that even the unique index --- with deduplication enabled has to check multiple leaf pages during unique --- checking (at least with a BLCKSZ of 8192 or less) -DO $$ -BEGIN - FOR r IN 1..1350 LOOP - DELETE FROM dedup_unique_test_table; - INSERT INTO dedup_unique_test_table SELECT 1; - END LOOP; -END$$; --- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple. --- The implementation prefers deleting existing items to merging any duplicate --- tuples into a posting list, so we need an explicit test to make sure we get --- coverage (note that this test also assumes BLCKSZ is 8192 or less): -DROP INDEX plain_unique; -DELETE FROM dedup_unique_test_table WHERE a = 1; -INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i; --- --- Test B-tree fast path (cache rightmost leaf page) optimization. --- --- First create a tree that's at least three levels deep (i.e. has one level --- between the root and leaf levels). The text inserted is long. It won't be --- TOAST compressed because we use plain storage in the table. Only a few --- index tuples fit on each internal page, allowing us to get a tall tree with --- few pages. (A tall tree is required to trigger caching.) --- --- The text column must be the leading column in the index, since suffix --- truncation would otherwise truncate tuples on internal pages, leaving us --- with a short tree. -create table btree_tall_tbl(id int4, t text); -alter table btree_tall_tbl alter COLUMN t set storage plain; -create index btree_tall_idx on btree_tall_tbl (t, id) with (fillfactor = 10); -insert into btree_tall_tbl select g, repeat('x', 250) -from generate_series(1, 130) g; --- --- Test for multilevel page deletion --- -CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; -ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); --- Delete most entries, and vacuum, deleting internal pages and creating "fast --- root" -DELETE FROM delete_test_table WHERE a < 79990; -VACUUM delete_test_table; --- --- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META --- WAL record type). This happens when a "fast root" page is split. This --- also creates coverage for nbtree FSM page recycling. --- --- The vacuum above should've turned the leaf page into a fast root. We just --- need to insert some rows to cause the fast root page to split. -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; --- Test unsupported btree opclass parameters -create index on btree_tall_tbl (id int4_ops(foo=1)); -ERROR: operator class int4_ops has no options --- Test case of ALTER INDEX with abuse of column names for indexes. --- This grammar is not officially supported, but the parser allows it. -CREATE INDEX btree_tall_idx2 ON btree_tall_tbl (id); -ALTER INDEX btree_tall_idx2 ALTER COLUMN id SET (n_distinct=100); -ERROR: ALTER action ALTER COLUMN ... SET cannot be performed on relation "btree_tall_idx2" -DETAIL: This operation is not supported for indexes. -DROP INDEX btree_tall_idx2; --- Partitioned index -CREATE TABLE btree_part (id int4) PARTITION BY RANGE (id); -CREATE INDEX btree_part_idx ON btree_part(id); -ALTER INDEX btree_part_idx ALTER COLUMN id SET (n_distinct=100); -ERROR: ALTER action ALTER COLUMN ... SET cannot be performed on relation "btree_part_idx" -DETAIL: This operation is not supported for partitioned indexes. -DROP TABLE btree_part; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/hash_index.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/hash_index.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/hash_index.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/hash_index.out 2021-10-30 22:04:12 -0600 @@ -1,243 +1,2 @@ --- --- HASH_INDEX --- grep 843938989 hash.data --- -SELECT * FROM hash_i4_heap - WHERE hash_i4_heap.random = 843938989; - seqno | random --------+----------- - 15 | 843938989 -(1 row) - --- --- hash index --- grep 66766766 hash.data --- -SELECT * FROM hash_i4_heap - WHERE hash_i4_heap.random = 66766766; - seqno | random --------+-------- -(0 rows) - --- --- hash index --- grep 1505703298 hash.data --- -SELECT * FROM hash_name_heap - WHERE hash_name_heap.random = '1505703298'::name; - seqno | random --------+------------ - 9838 | 1505703298 -(1 row) - --- --- hash index --- grep 7777777 hash.data --- -SELECT * FROM hash_name_heap - WHERE hash_name_heap.random = '7777777'::name; - seqno | random --------+-------- -(0 rows) - --- --- hash index --- grep 1351610853 hash.data --- -SELECT * FROM hash_txt_heap - WHERE hash_txt_heap.random = '1351610853'::text; - seqno | random --------+------------ - 5677 | 1351610853 -(1 row) - --- --- hash index --- grep 111111112222222233333333 hash.data --- -SELECT * FROM hash_txt_heap - WHERE hash_txt_heap.random = '111111112222222233333333'::text; - seqno | random --------+-------- -(0 rows) - --- --- hash index --- grep 444705537 hash.data --- -SELECT * FROM hash_f8_heap - WHERE hash_f8_heap.random = '444705537'::float8; - seqno | random --------+----------- - 7853 | 444705537 -(1 row) - --- --- hash index --- grep 88888888 hash.data --- -SELECT * FROM hash_f8_heap - WHERE hash_f8_heap.random = '88888888'::float8; - seqno | random --------+-------- -(0 rows) - --- --- hash index --- grep '^90[^0-9]' hashovfl.data --- --- SELECT count(*) AS i988 FROM hash_ovfl_heap --- WHERE x = 90; --- --- hash index --- grep '^1000[^0-9]' hashovfl.data --- --- SELECT count(*) AS i0 FROM hash_ovfl_heap --- WHERE x = 1000; --- --- HASH --- -UPDATE hash_i4_heap - SET random = 1 - WHERE hash_i4_heap.seqno = 1492; -SELECT h.seqno AS i1492, h.random AS i1 - FROM hash_i4_heap h - WHERE h.random = 1; - i1492 | i1 --------+---- - 1492 | 1 -(1 row) - -UPDATE hash_i4_heap - SET seqno = 20000 - WHERE hash_i4_heap.random = 1492795354; -SELECT h.seqno AS i20000 - FROM hash_i4_heap h - WHERE h.random = 1492795354; - i20000 --------- - 20000 -(1 row) - -UPDATE hash_name_heap - SET random = '0123456789abcdef'::name - WHERE hash_name_heap.seqno = 6543; -SELECT h.seqno AS i6543, h.random AS c0_to_f - FROM hash_name_heap h - WHERE h.random = '0123456789abcdef'::name; - i6543 | c0_to_f --------+------------------ - 6543 | 0123456789abcdef -(1 row) - -UPDATE hash_name_heap - SET seqno = 20000 - WHERE hash_name_heap.random = '76652222'::name; --- --- this is the row we just replaced; index scan should return zero rows --- -SELECT h.seqno AS emptyset - FROM hash_name_heap h - WHERE h.random = '76652222'::name; - emptyset ----------- -(0 rows) - -UPDATE hash_txt_heap - SET random = '0123456789abcdefghijklmnop'::text - WHERE hash_txt_heap.seqno = 4002; -SELECT h.seqno AS i4002, h.random AS c0_to_p - FROM hash_txt_heap h - WHERE h.random = '0123456789abcdefghijklmnop'::text; - i4002 | c0_to_p --------+---------------------------- - 4002 | 0123456789abcdefghijklmnop -(1 row) - -UPDATE hash_txt_heap - SET seqno = 20000 - WHERE hash_txt_heap.random = '959363399'::text; -SELECT h.seqno AS t20000 - FROM hash_txt_heap h - WHERE h.random = '959363399'::text; - t20000 --------- - 20000 -(1 row) - -UPDATE hash_f8_heap - SET random = '-1234.1234'::float8 - WHERE hash_f8_heap.seqno = 8906; -SELECT h.seqno AS i8096, h.random AS f1234_1234 - FROM hash_f8_heap h - WHERE h.random = '-1234.1234'::float8; - i8096 | f1234_1234 --------+------------ - 8906 | -1234.1234 -(1 row) - -UPDATE hash_f8_heap - SET seqno = 20000 - WHERE hash_f8_heap.random = '488912369'::float8; -SELECT h.seqno AS f20000 - FROM hash_f8_heap h - WHERE h.random = '488912369'::float8; - f20000 --------- - 20000 -(1 row) - --- UPDATE hash_ovfl_heap --- SET x = 1000 --- WHERE x = 90; --- this vacuums the index as well --- VACUUM hash_ovfl_heap; --- SELECT count(*) AS i0 FROM hash_ovfl_heap --- WHERE x = 90; --- SELECT count(*) AS i988 FROM hash_ovfl_heap --- WHERE x = 1000; --- --- Cause some overflow insert and splits. --- -CREATE TABLE hash_split_heap (keycol INT); -INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 500) a; -CREATE INDEX hash_split_index on hash_split_heap USING HASH (keycol); -INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 5000) a; --- Let's do a backward scan. -BEGIN; -SET enable_seqscan = OFF; -SET enable_bitmapscan = OFF; -DECLARE c CURSOR FOR SELECT * from hash_split_heap WHERE keycol = 1; -MOVE FORWARD ALL FROM c; -MOVE BACKWARD 10000 FROM c; -MOVE BACKWARD ALL FROM c; -CLOSE c; -END; --- DELETE, INSERT, VACUUM. -DELETE FROM hash_split_heap WHERE keycol = 1; -INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a; -VACUUM hash_split_heap; --- Rebuild the index using a different fillfactor -ALTER INDEX hash_split_index SET (fillfactor = 10); -REINDEX INDEX hash_split_index; --- Clean up. -DROP TABLE hash_split_heap; --- Index on temp table. -CREATE TEMP TABLE hash_temp_heap (x int, y int); -INSERT INTO hash_temp_heap VALUES (1,1); -CREATE INDEX hash_idx ON hash_temp_heap USING hash (x); -DROP TABLE hash_temp_heap CASCADE; --- Float4 type. -CREATE TABLE hash_heap_float4 (x float4, y int); -INSERT INTO hash_heap_float4 VALUES (1.1,1); -CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); -DROP TABLE hash_heap_float4 CASCADE; --- Test out-of-range fillfactor values -CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) - WITH (fillfactor=9); -ERROR: value 9 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) - WITH (fillfactor=101); -ERROR: value 101 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/update.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/update.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/update.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/update.out 2021-10-30 22:04:12 -0600 @@ -1,1028 +1,2 @@ --- --- UPDATE syntax tests --- -CREATE TABLE update_test ( - a INT DEFAULT 10, - b INT, - c TEXT -); -CREATE TABLE upsert_test ( - a INT PRIMARY KEY, - b TEXT -); -INSERT INTO update_test VALUES (5, 10, 'foo'); -INSERT INTO update_test(b, a) VALUES (15, 10); -SELECT * FROM update_test; - a | b | c -----+----+----- - 5 | 10 | foo - 10 | 15 | -(2 rows) - -UPDATE update_test SET a = DEFAULT, b = DEFAULT; -SELECT * FROM update_test; - a | b | c -----+---+----- - 10 | | foo - 10 | | -(2 rows) - --- aliases for the UPDATE target table -UPDATE update_test AS t SET b = 10 WHERE t.a = 10; -SELECT * FROM update_test; - a | b | c -----+----+----- - 10 | 10 | foo - 10 | 10 | -(2 rows) - -UPDATE update_test t SET b = t.b + 10 WHERE t.a = 10; -SELECT * FROM update_test; - a | b | c -----+----+----- - 10 | 20 | foo - 10 | 20 | -(2 rows) - --- --- Test VALUES in FROM --- -UPDATE update_test SET a=v.i FROM (VALUES(100, 20)) AS v(i, j) - WHERE update_test.b = v.j; -SELECT * FROM update_test; - a | b | c ------+----+----- - 100 | 20 | foo - 100 | 20 | -(2 rows) - --- fail, wrong data type: -UPDATE update_test SET a = v.* FROM (VALUES(100, 20)) AS v(i, j) - WHERE update_test.b = v.j; -ERROR: column "a" is of type integer but expression is of type record -LINE 1: UPDATE update_test SET a = v.* FROM (VALUES(100, 20)) AS v(i... - ^ -HINT: You will need to rewrite or cast the expression. --- --- Test multiple-set-clause syntax --- -INSERT INTO update_test SELECT a,b+1,c FROM update_test; -SELECT * FROM update_test; - a | b | c ------+----+----- - 100 | 20 | foo - 100 | 20 | - 100 | 21 | foo - 100 | 21 | -(4 rows) - -UPDATE update_test SET (c,b,a) = ('bugle', b+11, DEFAULT) WHERE c = 'foo'; -SELECT * FROM update_test; - a | b | c ------+----+------- - 100 | 20 | - 100 | 21 | - 10 | 31 | bugle - 10 | 32 | bugle -(4 rows) - -UPDATE update_test SET (c,b) = ('car', a+b), a = a + 1 WHERE a = 10; -SELECT * FROM update_test; - a | b | c ------+----+----- - 100 | 20 | - 100 | 21 | - 11 | 41 | car - 11 | 42 | car -(4 rows) - --- fail, multi assignment to same column: -UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10; -ERROR: multiple assignments to same column "b" --- uncorrelated sub-select: -UPDATE update_test - SET (b,a) = (select a,b from update_test where b = 41 and c = 'car') - WHERE a = 100 AND b = 20; -SELECT * FROM update_test; - a | b | c ------+----+----- - 100 | 21 | - 11 | 41 | car - 11 | 42 | car - 41 | 11 | -(4 rows) - --- correlated sub-select: -UPDATE update_test o - SET (b,a) = (select a+1,b from update_test i - where i.a=o.a and i.b=o.b and i.c is not distinct from o.c); -SELECT * FROM update_test; - a | b | c -----+-----+----- - 21 | 101 | - 41 | 12 | car - 42 | 12 | car - 11 | 42 | -(4 rows) - --- fail, multiple rows supplied: -UPDATE update_test SET (b,a) = (select a+1,b from update_test); -ERROR: more than one row returned by a subquery used as an expression --- set to null if no rows supplied: -UPDATE update_test SET (b,a) = (select a+1,b from update_test where a = 1000) - WHERE a = 11; -SELECT * FROM update_test; - a | b | c -----+-----+----- - 21 | 101 | - 41 | 12 | car - 42 | 12 | car - | | -(4 rows) - --- *-expansion should work in this context: -UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 100)) AS v(i, j) - WHERE update_test.a = v.i; --- you might expect this to work, but syntactically it's not a RowExpr: -UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) AS v(i, j) - WHERE update_test.a = v.i; -ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression -LINE 1: UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) ... - ^ --- if an alias for the target table is specified, don't allow references --- to the original table name -UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10; -ERROR: invalid reference to FROM-clause entry for table "update_test" -LINE 1: UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a... - ^ -HINT: Perhaps you meant to reference the table alias "t". --- Make sure that we can update to a TOASTed value. -UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car'; -SELECT a, b, char_length(c) FROM update_test; - a | b | char_length -----+-----+------------- - | | - 21 | 100 | - 41 | 12 | 10000 - 42 | 12 | 10000 -(4 rows) - --- Check multi-assignment with a Result node to handle a one-time filter. -EXPLAIN (VERBOSE, COSTS OFF) -UPDATE update_test t - SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a) - WHERE CURRENT_USER = SESSION_USER; - QUERY PLAN -------------------------------------------------------------- - Update on public.update_test t - -> Result - Output: $1, $2, (SubPlan 1 (returns $1,$2)), t.ctid - One-Time Filter: (CURRENT_USER = SESSION_USER) - -> Seq Scan on public.update_test t - Output: t.a, t.ctid - SubPlan 1 (returns $1,$2) - -> Seq Scan on public.update_test s - Output: s.b, s.a - Filter: (s.a = t.a) -(10 rows) - -UPDATE update_test t - SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a) - WHERE CURRENT_USER = SESSION_USER; -SELECT a, b, char_length(c) FROM update_test; - a | b | char_length ------+----+------------- - | | - 100 | 21 | - 12 | 41 | 10000 - 12 | 42 | 10000 -(4 rows) - --- Test ON CONFLICT DO UPDATE -INSERT INTO upsert_test VALUES(1, 'Boo'), (3, 'Zoo'); --- uncorrelated sub-select: -WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test - VALUES (1, 'Bar') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *; - a | b ----+----- - 1 | Foo -(1 row) - --- correlated sub-select: -INSERT INTO upsert_test VALUES (1, 'Baz'), (3, 'Zaz') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b || ', Correlated', a from upsert_test i WHERE i.a = upsert_test.a) - RETURNING *; - a | b ----+----------------- - 1 | Foo, Correlated - 3 | Zoo, Correlated -(2 rows) - --- correlated sub-select (EXCLUDED.* alias): -INSERT INTO upsert_test VALUES (1, 'Bat'), (3, 'Zot') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING *; - a | b ----+--------------------------- - 1 | Foo, Correlated, Excluded - 3 | Zoo, Correlated, Excluded -(2 rows) - --- ON CONFLICT using system attributes in RETURNING, testing both the --- inserting and updating paths. See bug report at: --- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au -INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct; - tableoid | xmin_correct | xmax_correct --------------+--------------+-------------- - upsert_test | t | t -(1 row) - --- currently xmax is set after a conflict - that's probably not good, --- but it seems worthwhile to have to be explicit if that changes. -INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct; - tableoid | xmin_correct | xmax_correct --------------+--------------+-------------- - upsert_test | t | t -(1 row) - -DROP TABLE update_test; -DROP TABLE upsert_test; --- Test ON CONFLICT DO UPDATE with partitioned table and non-identical children -CREATE TABLE upsert_test ( - a INT PRIMARY KEY, - b TEXT -) PARTITION BY LIST (a); -CREATE TABLE upsert_test_1 PARTITION OF upsert_test FOR VALUES IN (1); -CREATE TABLE upsert_test_2 (b TEXT, a INT PRIMARY KEY); -ALTER TABLE upsert_test ATTACH PARTITION upsert_test_2 FOR VALUES IN (2); -INSERT INTO upsert_test VALUES(1, 'Boo'), (2, 'Zoo'); --- uncorrelated sub-select: -WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test - VALUES (1, 'Bar') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *; - a | b ----+----- - 1 | Foo -(1 row) - --- correlated sub-select: -WITH aaa AS (SELECT 1 AS ctea, ' Foo' AS cteb) INSERT INTO upsert_test - VALUES (1, 'Bar'), (2, 'Baz') ON CONFLICT(a) - DO UPDATE SET (b, a) = (SELECT upsert_test.b||cteb, upsert_test.a FROM aaa) RETURNING *; - a | b ----+--------- - 1 | Foo Foo - 2 | Zoo Foo -(2 rows) - -DROP TABLE upsert_test; ---------------------------- --- UPDATE with row movement ---------------------------- --- When a partitioned table receives an UPDATE to the partitioned key and the --- new values no longer meet the partition's bound, the row must be moved to --- the correct partition for the new partition key (if one exists). We must --- also ensure that updatable views on partitioned tables properly enforce any --- WITH CHECK OPTION that is defined. The situation with triggers in this case --- also requires thorough testing as partition key updates causing row --- movement convert UPDATEs into DELETE+INSERT. -CREATE TABLE range_parted ( - a text, - b bigint, - c numeric, - d int, - e varchar -) PARTITION BY RANGE (a, b); --- Create partitions intentionally in descending bound order, so as to test --- that update-row-movement works with the leaf partitions not in bound order. -CREATE TABLE part_b_20_b_30 (e varchar, c numeric, a text, b bigint, d int); -ALTER TABLE range_parted ATTACH PARTITION part_b_20_b_30 FOR VALUES FROM ('b', 20) TO ('b', 30); -CREATE TABLE part_b_10_b_20 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY RANGE (c); -CREATE TABLE part_b_1_b_10 PARTITION OF range_parted FOR VALUES FROM ('b', 1) TO ('b', 10); -ALTER TABLE range_parted ATTACH PARTITION part_b_10_b_20 FOR VALUES FROM ('b', 10) TO ('b', 20); -CREATE TABLE part_a_10_a_20 PARTITION OF range_parted FOR VALUES FROM ('a', 10) TO ('a', 20); -CREATE TABLE part_a_1_a_10 PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('a', 10); --- Check that partition-key UPDATE works sanely on a partitioned table that --- does not have any child partitions. -UPDATE part_b_10_b_20 set b = b - 6; --- Create some more partitions following the above pattern of descending bound --- order, but let's make the situation a bit more complex by having the --- attribute numbers of the columns vary from their parent partition. -CREATE TABLE part_c_100_200 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY range (abs(d)); -ALTER TABLE part_c_100_200 DROP COLUMN e, DROP COLUMN c, DROP COLUMN a; -ALTER TABLE part_c_100_200 ADD COLUMN c numeric, ADD COLUMN e varchar, ADD COLUMN a text; -ALTER TABLE part_c_100_200 DROP COLUMN b; -ALTER TABLE part_c_100_200 ADD COLUMN b bigint; -CREATE TABLE part_d_1_15 PARTITION OF part_c_100_200 FOR VALUES FROM (1) TO (15); -CREATE TABLE part_d_15_20 PARTITION OF part_c_100_200 FOR VALUES FROM (15) TO (20); -ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_100_200 FOR VALUES FROM (100) TO (200); -CREATE TABLE part_c_1_100 (e varchar, d int, c numeric, b bigint, a text); -ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_1_100 FOR VALUES FROM (1) TO (100); -\set init_range_parted 'truncate range_parted; insert into range_parted VALUES (''a'', 1, 1, 1), (''a'', 10, 200, 1), (''b'', 12, 96, 1), (''b'', 13, 97, 2), (''b'', 15, 105, 16), (''b'', 17, 105, 19)' -\set show_data 'select tableoid::regclass::text COLLATE "C" partname, * from range_parted ORDER BY 1, 2, 3, 4, 5, 6' -:init_range_parted; -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_d_15_20 | b | 15 | 105 | 16 | - part_d_15_20 | b | 17 | 105 | 19 | -(6 rows) - --- The order of subplans should be in bound order -EXPLAIN (costs off) UPDATE range_parted set c = c - 50 WHERE c > 97; - QUERY PLAN -------------------------------------------------------- - Update on range_parted - Update on part_a_1_a_10 range_parted_1 - Update on part_a_10_a_20 range_parted_2 - Update on part_b_1_b_10 range_parted_3 - Update on part_c_1_100 range_parted_4 - Update on part_d_1_15 range_parted_5 - Update on part_d_15_20 range_parted_6 - Update on part_b_20_b_30 range_parted_7 - -> Append - -> Seq Scan on part_a_1_a_10 range_parted_1 - Filter: (c > '97'::numeric) - -> Seq Scan on part_a_10_a_20 range_parted_2 - Filter: (c > '97'::numeric) - -> Seq Scan on part_b_1_b_10 range_parted_3 - Filter: (c > '97'::numeric) - -> Seq Scan on part_c_1_100 range_parted_4 - Filter: (c > '97'::numeric) - -> Seq Scan on part_d_1_15 range_parted_5 - Filter: (c > '97'::numeric) - -> Seq Scan on part_d_15_20 range_parted_6 - Filter: (c > '97'::numeric) - -> Seq Scan on part_b_20_b_30 range_parted_7 - Filter: (c > '97'::numeric) -(23 rows) - --- fail, row movement happens only within the partition subtree. -UPDATE part_c_100_200 set c = c - 20, d = c WHERE c = 105; -ERROR: new row for relation "part_c_100_200" violates partition constraint -DETAIL: Failing row contains (105, 85, null, b, 15). --- fail, no partition key update, so no attempt to move tuple, --- but "a = 'a'" violates partition constraint enforced by root partition) -UPDATE part_b_10_b_20 set a = 'a'; -ERROR: new row for relation "part_b_10_b_20" violates partition constraint -DETAIL: Failing row contains (null, 96, a, 12, 1). --- ok, partition key update, no constraint violation -UPDATE range_parted set d = d - 10 WHERE d > 10; --- ok, no partition key update, no constraint violation -UPDATE range_parted set e = d; --- No row found -UPDATE part_c_1_100 set c = c + 20 WHERE c = 98; --- ok, row movement -UPDATE part_b_10_b_20 set c = c + 20 returning c, b, a; - c | b | a ------+----+--- - 116 | 12 | b - 117 | 13 | b - 125 | 15 | b - 125 | 17 | b -(4 rows) - -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+---+--- - part_a_10_a_20 | a | 10 | 200 | 1 | 1 - part_a_1_a_10 | a | 1 | 1 | 1 | 1 - part_d_1_15 | b | 12 | 116 | 1 | 1 - part_d_1_15 | b | 13 | 117 | 2 | 2 - part_d_1_15 | b | 15 | 125 | 6 | 6 - part_d_1_15 | b | 17 | 125 | 9 | 9 -(6 rows) - --- fail, row movement happens only within the partition subtree. -UPDATE part_b_10_b_20 set b = b - 6 WHERE c > 116 returning *; -ERROR: new row for relation "part_b_10_b_20" violates partition constraint -DETAIL: Failing row contains (2, 117, b, 7, 2). --- ok, row movement, with subset of rows moved into different partition. -UPDATE range_parted set b = b - 6 WHERE c > 116 returning a, b + c; - a | ?column? ----+---------- - a | 204 - b | 124 - b | 134 - b | 136 -(4 rows) - -:show_data; - partname | a | b | c | d | e ----------------+---+----+-----+---+--- - part_a_1_a_10 | a | 1 | 1 | 1 | 1 - part_a_1_a_10 | a | 4 | 200 | 1 | 1 - part_b_1_b_10 | b | 7 | 117 | 2 | 2 - part_b_1_b_10 | b | 9 | 125 | 6 | 6 - part_d_1_15 | b | 11 | 125 | 9 | 9 - part_d_1_15 | b | 12 | 116 | 1 | 1 -(6 rows) - --- Common table needed for multiple test scenarios. -CREATE TABLE mintab(c1 int); -INSERT into mintab VALUES (120); --- update partition key using updatable view. -CREATE VIEW upview AS SELECT * FROM range_parted WHERE (select c > c1 FROM mintab) WITH CHECK OPTION; --- ok -UPDATE upview set c = 199 WHERE b = 4; --- fail, check option violation -UPDATE upview set c = 120 WHERE b = 4; -ERROR: new row violates check option for view "upview" -DETAIL: Failing row contains (a, 4, 120, 1, 1). --- fail, row movement with check option violation -UPDATE upview set a = 'b', b = 15, c = 120 WHERE b = 4; -ERROR: new row violates check option for view "upview" -DETAIL: Failing row contains (b, 15, 120, 1, 1). --- ok, row movement, check option passes -UPDATE upview set a = 'b', b = 15 WHERE b = 4; -:show_data; - partname | a | b | c | d | e ----------------+---+----+-----+---+--- - part_a_1_a_10 | a | 1 | 1 | 1 | 1 - part_b_1_b_10 | b | 7 | 117 | 2 | 2 - part_b_1_b_10 | b | 9 | 125 | 6 | 6 - part_d_1_15 | b | 11 | 125 | 9 | 9 - part_d_1_15 | b | 12 | 116 | 1 | 1 - part_d_1_15 | b | 15 | 199 | 1 | 1 -(6 rows) - --- cleanup -DROP VIEW upview; --- RETURNING having whole-row vars. -:init_range_parted; -UPDATE range_parted set c = 95 WHERE a = 'b' and b > 10 and c > 100 returning (range_parted), *; - range_parted | a | b | c | d | e ----------------+---+----+----+----+--- - (b,15,95,16,) | b | 15 | 95 | 16 | - (b,17,95,19,) | b | 17 | 95 | 19 | -(2 rows) - -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_c_1_100 | b | 15 | 95 | 16 | - part_c_1_100 | b | 17 | 95 | 19 | -(6 rows) - --- Transition tables with update row movement -:init_range_parted; -CREATE FUNCTION trans_updatetrigfunc() RETURNS trigger LANGUAGE plpgsql AS -$$ - begin - raise notice 'trigger = %, old table = %, new table = %', - TG_NAME, - (select string_agg(old_table::text, ', ' ORDER BY a) FROM old_table), - (select string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); - return null; - end; -$$; -CREATE TRIGGER trans_updatetrig - AFTER UPDATE ON range_parted REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end ) WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,110,1,), (b,13,98,2,), (b,15,106,16,), (b,17,106,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 13 | 98 | 2 | - part_d_15_20 | b | 15 | 106 | 16 | - part_d_15_20 | b | 17 | 106 | 19 | - part_d_1_15 | b | 12 | 110 | 1 | -(6 rows) - -:init_range_parted; --- Enabling OLD TABLE capture for both DELETE as well as UPDATE stmt triggers --- should not cause DELETEd rows to be captured twice. Similar thing for --- INSERT triggers and inserted rows. -CREATE TRIGGER trans_deletetrig - AFTER DELETE ON range_parted REFERENCING OLD TABLE AS old_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -CREATE TRIGGER trans_inserttrig - AFTER INSERT ON range_parted REFERENCING NEW TABLE AS new_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,146,1,), (b,13,147,2,), (b,15,155,16,), (b,17,155,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_d_15_20 | b | 15 | 155 | 16 | - part_d_15_20 | b | 17 | 155 | 19 | - part_d_1_15 | b | 12 | 146 | 1 | - part_d_1_15 | b | 13 | 147 | 2 | -(6 rows) - -DROP TRIGGER trans_deletetrig ON range_parted; -DROP TRIGGER trans_inserttrig ON range_parted; --- Don't drop trans_updatetrig yet. It is required below. --- Test with transition tuple conversion happening for rows moved into the --- new partition. This requires a trigger that references transition table --- (we already have trans_updatetrig). For inserted rows, the conversion --- is not usually needed, because the original tuple is already compatible with --- the desired transition tuple format. But conversion happens when there is a --- BR trigger because the trigger can change the inserted row. So install a --- BR triggers on those child partitions where the rows will be moved. -CREATE FUNCTION func_parted_mod_b() RETURNS trigger AS $$ -BEGIN - NEW.b = NEW.b + 1; - return NEW; -END $$ language plpgsql; -CREATE TRIGGER trig_c1_100 BEFORE UPDATE OR INSERT ON part_c_1_100 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -CREATE TRIGGER trig_d1_15 BEFORE UPDATE OR INSERT ON part_d_1_15 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -CREATE TRIGGER trig_d15_20 BEFORE UPDATE OR INSERT ON part_d_15_20 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -:init_range_parted; -UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end) WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,110,1,), (b,15,98,2,), (b,17,106,16,), (b,19,106,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 15 | 98 | 2 | - part_d_15_20 | b | 17 | 106 | 16 | - part_d_15_20 | b | 19 | 106 | 19 | - part_d_1_15 | b | 15 | 110 | 1 | -(6 rows) - -:init_range_parted; -UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,146,1,), (b,16,147,2,), (b,17,155,16,), (b,19,155,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_d_15_20 | b | 17 | 155 | 16 | - part_d_15_20 | b | 19 | 155 | 19 | - part_d_1_15 | b | 15 | 146 | 1 | - part_d_1_15 | b | 16 | 147 | 2 | -(6 rows) - --- Case where per-partition tuple conversion map array is allocated, but the --- map is not required for the particular tuple that is routed, thanks to --- matching table attributes of the partition and the target table. -:init_range_parted; -UPDATE range_parted set b = 15 WHERE b = 1; -NOTICE: trigger = trans_updatetrig, old table = (a,1,1,1,), new table = (a,15,1,1,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_10_a_20 | a | 15 | 1 | 1 | - part_c_1_100 | b | 13 | 96 | 1 | - part_c_1_100 | b | 14 | 97 | 2 | - part_d_15_20 | b | 16 | 105 | 16 | - part_d_15_20 | b | 18 | 105 | 19 | -(6 rows) - -DROP TRIGGER trans_updatetrig ON range_parted; -DROP TRIGGER trig_c1_100 ON part_c_1_100; -DROP TRIGGER trig_d1_15 ON part_d_1_15; -DROP TRIGGER trig_d15_20 ON part_d_15_20; -DROP FUNCTION func_parted_mod_b(); --- RLS policies with update-row-movement ------------------------------------------ -ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY; -CREATE USER regress_range_parted_user; -GRANT ALL ON range_parted, mintab TO regress_range_parted_user; -CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true); -CREATE POLICY policy_range_parted ON range_parted for UPDATE USING (true) WITH CHECK (c % 2 = 0); -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- This should fail with RLS violation error while moving row from --- part_a_10_a_20 to part_d_1_15, because we are setting 'c' to an odd number. -UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy for table "range_parted" -RESET SESSION AUTHORIZATION; --- Create a trigger on part_d_1_15 -CREATE FUNCTION func_d_1_15() RETURNS trigger AS $$ -BEGIN - NEW.c = NEW.c + 1; -- Make even numbers odd, or vice versa - return NEW; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER trig_d_1_15 BEFORE INSERT ON part_d_1_15 - FOR EACH ROW EXECUTE PROCEDURE func_d_1_15(); -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- Here, RLS checks should succeed while moving row from part_a_10_a_20 to --- part_d_1_15. Even though the UPDATE is setting 'c' to an odd number, the --- trigger at the destination partition again makes it an even number. -UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; -RESET SESSION AUTHORIZATION; -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- This should fail with RLS violation error. Even though the UPDATE is setting --- 'c' to an even number, the trigger at the destination partition again makes --- it an odd number. -UPDATE range_parted set a = 'b', c = 150 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy for table "range_parted" --- Cleanup -RESET SESSION AUTHORIZATION; -DROP TRIGGER trig_d_1_15 ON part_d_1_15; -DROP FUNCTION func_d_1_15(); --- Policy expression contains SubPlan -RESET SESSION AUTHORIZATION; -:init_range_parted; -CREATE POLICY policy_range_parted_subplan on range_parted - AS RESTRICTIVE for UPDATE USING (true) - WITH CHECK ((SELECT range_parted.c <= c1 FROM mintab)); -SET SESSION AUTHORIZATION regress_range_parted_user; --- fail, mintab has row with c1 = 120 -UPDATE range_parted set a = 'b', c = 122 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy "policy_range_parted_subplan" for table "range_parted" --- ok -UPDATE range_parted set a = 'b', c = 120 WHERE a = 'a' and c = 200; --- RLS policy expression contains whole row. -RESET SESSION AUTHORIZATION; -:init_range_parted; -CREATE POLICY policy_range_parted_wholerow on range_parted AS RESTRICTIVE for UPDATE USING (true) - WITH CHECK (range_parted = row('b', 10, 112, 1, NULL)::range_parted); -SET SESSION AUTHORIZATION regress_range_parted_user; --- ok, should pass the RLS check -UPDATE range_parted set a = 'b', c = 112 WHERE a = 'a' and c = 200; -RESET SESSION AUTHORIZATION; -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- fail, the whole row RLS check should fail -UPDATE range_parted set a = 'b', c = 116 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy "policy_range_parted_wholerow" for table "range_parted" --- Cleanup -RESET SESSION AUTHORIZATION; -DROP POLICY policy_range_parted ON range_parted; -DROP POLICY policy_range_parted_subplan ON range_parted; -DROP POLICY policy_range_parted_wholerow ON range_parted; -REVOKE ALL ON range_parted, mintab FROM regress_range_parted_user; -DROP USER regress_range_parted_user; -DROP TABLE mintab; --- statement triggers with update row movement ---------------------------------------------------- -:init_range_parted; -CREATE FUNCTION trigfunc() returns trigger language plpgsql as -$$ - begin - raise notice 'trigger = % fired on table % during %', - TG_NAME, TG_TABLE_NAME, TG_OP; - return null; - end; -$$; --- Triggers on root partition -CREATE TRIGGER parent_delete_trig - AFTER DELETE ON range_parted for each statement execute procedure trigfunc(); -CREATE TRIGGER parent_update_trig - AFTER UPDATE ON range_parted for each statement execute procedure trigfunc(); -CREATE TRIGGER parent_insert_trig - AFTER INSERT ON range_parted for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_c_1_100 -CREATE TRIGGER c1_delete_trig - AFTER DELETE ON part_c_1_100 for each statement execute procedure trigfunc(); -CREATE TRIGGER c1_update_trig - AFTER UPDATE ON part_c_1_100 for each statement execute procedure trigfunc(); -CREATE TRIGGER c1_insert_trig - AFTER INSERT ON part_c_1_100 for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_d_1_15 -CREATE TRIGGER d1_delete_trig - AFTER DELETE ON part_d_1_15 for each statement execute procedure trigfunc(); -CREATE TRIGGER d1_update_trig - AFTER UPDATE ON part_d_1_15 for each statement execute procedure trigfunc(); -CREATE TRIGGER d1_insert_trig - AFTER INSERT ON part_d_1_15 for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_d_15_20 -CREATE TRIGGER d15_delete_trig - AFTER DELETE ON part_d_15_20 for each statement execute procedure trigfunc(); -CREATE TRIGGER d15_update_trig - AFTER UPDATE ON part_d_15_20 for each statement execute procedure trigfunc(); -CREATE TRIGGER d15_insert_trig - AFTER INSERT ON part_d_15_20 for each statement execute procedure trigfunc(); --- Move all rows from part_c_100_200 to part_c_1_100. None of the delete or --- insert statement triggers should be fired. -UPDATE range_parted set c = c - 50 WHERE c > 97; -NOTICE: trigger = parent_update_trig fired on table range_parted during UPDATE -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 150 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_c_1_100 | b | 15 | 55 | 16 | - part_c_1_100 | b | 17 | 55 | 19 | -(6 rows) - -DROP TRIGGER parent_delete_trig ON range_parted; -DROP TRIGGER parent_update_trig ON range_parted; -DROP TRIGGER parent_insert_trig ON range_parted; -DROP TRIGGER c1_delete_trig ON part_c_1_100; -DROP TRIGGER c1_update_trig ON part_c_1_100; -DROP TRIGGER c1_insert_trig ON part_c_1_100; -DROP TRIGGER d1_delete_trig ON part_d_1_15; -DROP TRIGGER d1_update_trig ON part_d_1_15; -DROP TRIGGER d1_insert_trig ON part_d_1_15; -DROP TRIGGER d15_delete_trig ON part_d_15_20; -DROP TRIGGER d15_update_trig ON part_d_15_20; -DROP TRIGGER d15_insert_trig ON part_d_15_20; --- Creating default partition for range -:init_range_parted; -create table part_def partition of range_parted default; -\d+ part_def - Table "public.part_def" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+-------------------+-----------+----------+---------+----------+--------------+------------- - a | text | | | | extended | | - b | bigint | | | | plain | | - c | numeric | | | | main | | - d | integer | | | | plain | | - e | character varying | | | | extended | | -Partition of: range_parted DEFAULT -Partition constraint: (NOT ((a IS NOT NULL) AND (b IS NOT NULL) AND (((a = 'a'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'a'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'b'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '20'::bigint) AND (b < '30'::bigint))))) - -insert into range_parted values ('c', 9); --- ok -update part_def set a = 'd' where a = 'c'; --- fail -update part_def set a = 'a' where a = 'd'; -ERROR: new row for relation "part_def" violates partition constraint -DETAIL: Failing row contains (a, 9, null, null, null). -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_d_15_20 | b | 15 | 105 | 16 | - part_d_15_20 | b | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Update row movement from non-default to default partition. --- fail, default partition is not under part_a_10_a_20; -UPDATE part_a_10_a_20 set a = 'ad' WHERE a = 'a'; -ERROR: new row for relation "part_a_10_a_20" violates partition constraint -DETAIL: Failing row contains (ad, 10, 200, 1, null). --- ok -UPDATE range_parted set a = 'ad' WHERE a = 'a'; -UPDATE range_parted set a = 'bd' WHERE a = 'b'; -:show_data; - partname | a | b | c | d | e -----------+----+----+-----+----+--- - part_def | ad | 1 | 1 | 1 | - part_def | ad | 10 | 200 | 1 | - part_def | bd | 12 | 96 | 1 | - part_def | bd | 13 | 97 | 2 | - part_def | bd | 15 | 105 | 16 | - part_def | bd | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Update row movement from default to non-default partitions. --- ok -UPDATE range_parted set a = 'a' WHERE a = 'ad'; -UPDATE range_parted set a = 'b' WHERE a = 'bd'; -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_d_15_20 | b | 15 | 105 | 16 | - part_d_15_20 | b | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Cleanup: range_parted no longer needed. -DROP TABLE range_parted; -CREATE TABLE list_parted ( - a text, - b int -) PARTITION BY list (a); -CREATE TABLE list_part1 PARTITION OF list_parted for VALUES in ('a', 'b'); -CREATE TABLE list_default PARTITION OF list_parted default; -INSERT into list_part1 VALUES ('a', 1); -INSERT into list_default VALUES ('d', 10); --- fail -UPDATE list_default set a = 'a' WHERE a = 'd'; -ERROR: new row for relation "list_default" violates partition constraint -DETAIL: Failing row contains (a, 10). --- ok -UPDATE list_default set a = 'x' WHERE a = 'd'; -DROP TABLE list_parted; --- Test retrieval of system columns with non-consistent partition row types. --- This is only partially supported, as seen in the results. -create table utrtest (a int, b text) partition by list (a); -create table utr1 (a int check (a in (1)), q text, b text); -create table utr2 (a int check (a in (2)), b text); -alter table utr1 drop column q; -alter table utrtest attach partition utr1 for values in (1); -alter table utrtest attach partition utr2 for values in (2); -insert into utrtest values (1, 'foo') - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; - a | b | tableoid | xmin_ok ----+-----+----------+--------- - 1 | foo | utr1 | t -(1 row) - -insert into utrtest values (2, 'bar') - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: cannot retrieve a system column in this context -insert into utrtest values (2, 'bar') - returning *, tableoid::regclass; - a | b | tableoid ----+-----+---------- - 2 | bar | utr2 -(1 row) - -update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; - a | b | x | tableoid | xmin_ok ----+--------+---+----------+--------- - 1 | foofoo | 1 | utr1 | t - 2 | barbar | 2 | utr2 | t -(2 rows) - -update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: cannot retrieve a system column in this context -update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass; - a | b | x | tableoid ----+--------+---+---------- - 2 | foofoo | 1 | utr2 - 1 | barbar | 2 | utr1 -(2 rows) - -delete from utrtest - returning *, tableoid::regclass, xmax = pg_current_xact_id()::xid as xmax_ok; - a | b | tableoid | xmax_ok ----+--------+----------+--------- - 1 | barbar | utr1 | t - 2 | foofoo | utr2 | t -(2 rows) - -drop table utrtest; --------------- --- Some more update-partition-key test scenarios below. This time use list --- partitions. --------------- --- Setup for list partitions -CREATE TABLE list_parted (a numeric, b int, c int8) PARTITION BY list (a); -CREATE TABLE sub_parted PARTITION OF list_parted for VALUES in (1) PARTITION BY list (b); -CREATE TABLE sub_part1(b int, c int8, a numeric); -ALTER TABLE sub_parted ATTACH PARTITION sub_part1 for VALUES in (1); -CREATE TABLE sub_part2(b int, c int8, a numeric); -ALTER TABLE sub_parted ATTACH PARTITION sub_part2 for VALUES in (2); -CREATE TABLE list_part1(a numeric, b int, c int8); -ALTER TABLE list_parted ATTACH PARTITION list_part1 for VALUES in (2,3); -INSERT into list_parted VALUES (2,5,50); -INSERT into list_parted VALUES (3,6,60); -INSERT into sub_parted VALUES (1,1,60); -INSERT into sub_parted VALUES (1,2,10); --- Test partition constraint violation when intermediate ancestor is used and --- constraint is inherited from upper root. -UPDATE sub_parted set a = 2 WHERE c = 10; -ERROR: new row for relation "sub_parted" violates partition constraint -DETAIL: Failing row contains (2, 2, 10). --- Test update-partition-key, where the unpruned partitions do not have their --- partition keys updated. -SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; - tableoid | a | b | c -------------+---+---+---- - list_part1 | 2 | 5 | 50 -(1 row) - -UPDATE list_parted set b = c + a WHERE a = 2; -SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 -(1 row) - --- Test the case where BR UPDATE triggers change the partition key. -CREATE FUNCTION func_parted_mod_b() returns trigger as $$ -BEGIN - NEW.b = 2; -- This is changing partition key column. - return NEW; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER parted_mod_b before update on sub_part1 - for each row execute procedure func_parted_mod_b(); -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part1 | 1 | 1 | 60 - sub_part2 | 1 | 2 | 10 -(4 rows) - --- This should do the tuple routing even though there is no explicit --- partition-key update, because there is a trigger on sub_part1. -UPDATE list_parted set c = 70 WHERE b = 1; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part2 | 1 | 2 | 10 - sub_part2 | 1 | 2 | 70 -(4 rows) - -DROP TRIGGER parted_mod_b ON sub_part1; --- If BR DELETE trigger prevented DELETE from happening, we should also skip --- the INSERT if that delete is part of UPDATE=>DELETE+INSERT. -CREATE OR REPLACE FUNCTION func_parted_mod_b() returns trigger as $$ -BEGIN - raise notice 'Trigger: Got OLD row %, but returning NULL', OLD; - return NULL; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER trig_skip_delete before delete on sub_part2 - for each row execute procedure func_parted_mod_b(); -UPDATE list_parted set b = 1 WHERE c = 70; -NOTICE: Trigger: Got OLD row (2,70,1), but returning NULL -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part2 | 1 | 2 | 10 - sub_part2 | 1 | 2 | 70 -(4 rows) - --- Drop the trigger. Now the row should be moved. -DROP TRIGGER trig_skip_delete ON sub_part2; -UPDATE list_parted set b = 1 WHERE c = 70; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part1 | 1 | 1 | 70 - sub_part2 | 1 | 2 | 10 -(4 rows) - -DROP FUNCTION func_parted_mod_b(); --- UPDATE partition-key with FROM clause. If join produces multiple output --- rows for the same row to be modified, we should tuple-route the row only --- once. There should not be any rows inserted. -CREATE TABLE non_parted (id int); -INSERT into non_parted VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); -UPDATE list_parted t1 set a = 2 FROM non_parted t2 WHERE t1.a = t2.id and a = 1; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 1 | 70 - list_part1 | 2 | 2 | 10 - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 -(4 rows) - -DROP TABLE non_parted; --- Cleanup: list_parted no longer needed. -DROP TABLE list_parted; --- create custom operator class and hash function, for the same reason --- explained in alter_table.sql -create or replace function dummy_hashint4(a int4, seed int8) returns int8 as -$$ begin return (a + seed); end; $$ language 'plpgsql' immutable; -create operator class custom_opclass for type int4 using hash as -operator 1 = , function 2 dummy_hashint4(int4, int8); -create table hash_parted ( - a int, - b int -) partition by hash (a custom_opclass, b custom_opclass); -create table hpart1 partition of hash_parted for values with (modulus 2, remainder 1); -create table hpart2 partition of hash_parted for values with (modulus 4, remainder 2); -create table hpart3 partition of hash_parted for values with (modulus 8, remainder 0); -create table hpart4 partition of hash_parted for values with (modulus 8, remainder 4); -insert into hpart1 values (1, 1); -insert into hpart2 values (2, 5); -insert into hpart4 values (3, 4); --- fail -update hpart1 set a = 3, b=4 where a = 1; -ERROR: new row for relation "hpart1" violates partition constraint -DETAIL: Failing row contains (3, 4). --- ok, row movement -update hash_parted set b = b - 1 where b = 1; --- ok -update hash_parted set b = b + 8 where b = 1; --- cleanup -drop table hash_parted; -drop operator class custom_opclass using hash; -drop function dummy_hashint4(a int4, seed int8); +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/delete.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/delete.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/delete.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/delete.out 2021-10-30 22:04:12 -0600 @@ -1,33 +1,2 @@ -CREATE TABLE delete_test ( - id SERIAL PRIMARY KEY, - a INT, - b text -); -INSERT INTO delete_test (a) VALUES (10); -INSERT INTO delete_test (a, b) VALUES (50, repeat('x', 10000)); -INSERT INTO delete_test (a) VALUES (100); --- allow an alias to be specified for DELETE's target table -DELETE FROM delete_test AS dt WHERE dt.a > 75; --- if an alias is specified, don't allow the original table name --- to be referenced -DELETE FROM delete_test dt WHERE delete_test.a > 25; -ERROR: invalid reference to FROM-clause entry for table "delete_test" -LINE 1: DELETE FROM delete_test dt WHERE delete_test.a > 25; - ^ -HINT: Perhaps you meant to reference the table alias "dt". -SELECT id, a, char_length(b) FROM delete_test; - id | a | char_length -----+----+------------- - 1 | 10 | - 2 | 50 | 10000 -(2 rows) - --- delete a row with a TOASTed value -DELETE FROM delete_test WHERE a > 25; -SELECT id, a, char_length(b) FROM delete_test; - id | a | char_length -----+----+------------- - 1 | 10 | -(1 row) - -DROP TABLE delete_test; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/namespace.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/namespace.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/namespace.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/namespace.out 2021-10-30 22:04:12 -0600 @@ -1,71 +1,2 @@ --- --- Regression tests for schemas (namespaces) --- -CREATE SCHEMA test_ns_schema_1 - CREATE UNIQUE INDEX abc_a_idx ON abc (a) - CREATE VIEW abc_view AS - SELECT a+1 AS a, b+1 AS b FROM abc - CREATE TABLE abc ( - a serial, - b int UNIQUE - ); --- verify that the objects were created -SELECT COUNT(*) FROM pg_class WHERE relnamespace = - (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); - count -------- - 5 -(1 row) - -INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; -INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; -INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; -SELECT * FROM test_ns_schema_1.abc; - a | b ----+--- - 1 | - 2 | - 3 | -(3 rows) - -SELECT * FROM test_ns_schema_1.abc_view; - a | b ----+--- - 2 | - 3 | - 4 | -(3 rows) - -ALTER SCHEMA test_ns_schema_1 RENAME TO test_ns_schema_renamed; -SELECT COUNT(*) FROM pg_class WHERE relnamespace = - (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); - count -------- - 0 -(1 row) - --- test IF NOT EXISTS cases -CREATE SCHEMA test_ns_schema_renamed; -- fail, already exists -ERROR: schema "test_ns_schema_renamed" already exists -CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed; -- ok with notice -NOTICE: schema "test_ns_schema_renamed" already exists, skipping -CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed -- fail, disallowed - CREATE TABLE abc ( - a serial, - b int UNIQUE - ); -ERROR: CREATE SCHEMA IF NOT EXISTS cannot include schema elements -LINE 2: CREATE TABLE abc ( - ^ -DROP SCHEMA test_ns_schema_renamed CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table test_ns_schema_renamed.abc -drop cascades to view test_ns_schema_renamed.abc_view --- verify that the objects were dropped -SELECT COUNT(*) FROM pg_class WHERE relnamespace = - (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_renamed'); - count -------- - 0 -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/prepared_xacts_1.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/prepared_xacts.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/prepared_xacts_1.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/prepared_xacts.out 2021-10-30 22:04:12 -0600 @@ -1,266 +1,2 @@ --- --- PREPARED TRANSACTIONS (two-phase commit) --- --- We can't readily test persistence of prepared xacts within the --- regression script framework, unfortunately. Note that a crash --- isn't really needed ... stopping and starting the postmaster would --- be enough, but we can't even do that here. --- create a simple table that we'll use in the tests -CREATE TABLE pxtest1 (foobar VARCHAR(10)); -INSERT INTO pxtest1 VALUES ('aaa'); --- Test PREPARE TRANSACTION -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa'; -SELECT * FROM pxtest1; - foobar --------- - bbb -(1 row) - -PREPARE TRANSACTION 'foo1'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - --- Test pg_prepared_xacts system view -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- Test ROLLBACK PREPARED -ROLLBACK PREPARED 'foo1'; -ERROR: prepared transaction with identifier "foo1" does not exist -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- Test COMMIT PREPARED -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -INSERT INTO pxtest1 VALUES ('ddd'); -SELECT * FROM pxtest1; - foobar --------- - aaa - ddd -(2 rows) - -PREPARE TRANSACTION 'foo2'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - -COMMIT PREPARED 'foo2'; -ERROR: prepared transaction with identifier "foo2" does not exist -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - --- Test duplicate gids -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - -PREPARE TRANSACTION 'foo3'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -INSERT INTO pxtest1 VALUES ('fff'); --- This should fail, because the gid foo3 is already in use -PREPARE TRANSACTION 'foo3'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - -ROLLBACK PREPARED 'foo3'; -ERROR: prepared transaction with identifier "foo3" does not exist -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - --- Test serialization failure (SSI) -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - -PREPARE TRANSACTION 'foo4'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -SELECT * FROM pxtest1; - foobar --------- - aaa -(1 row) - --- This should fail, because the two transactions have a write-skew anomaly -INSERT INTO pxtest1 VALUES ('fff'); -PREPARE TRANSACTION 'foo5'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - -ROLLBACK PREPARED 'foo4'; -ERROR: prepared transaction with identifier "foo4" does not exist -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- Clean up -DROP TABLE pxtest1; --- Test detection of session-level and xact-level locks on same object -BEGIN; -SELECT pg_advisory_lock(1); - pg_advisory_lock ------------------- - -(1 row) - -SELECT pg_advisory_xact_lock_shared(1); - pg_advisory_xact_lock_shared ------------------------------- - -(1 row) - -PREPARE TRANSACTION 'foo6'; -- fails -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. --- Test subtransactions -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; - CREATE TABLE pxtest2 (a int); - INSERT INTO pxtest2 VALUES (1); - SAVEPOINT a; - INSERT INTO pxtest2 VALUES (2); - ROLLBACK TO a; - SAVEPOINT b; - INSERT INTO pxtest2 VALUES (3); -PREPARE TRANSACTION 'regress-one'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. -CREATE TABLE pxtest3(fff int); --- Test shared invalidation -BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; - DROP TABLE pxtest3; - CREATE TABLE pxtest4 (a int); - INSERT INTO pxtest4 VALUES (1); - INSERT INTO pxtest4 VALUES (2); - DECLARE foo CURSOR FOR SELECT * FROM pxtest4; - -- Fetch 1 tuple, keeping the cursor open - FETCH 1 FROM foo; - a ---- - 1 -(1 row) - -PREPARE TRANSACTION 'regress-two'; -ERROR: prepared transactions are disabled -HINT: Set max_prepared_transactions to a nonzero value. --- No such cursor -FETCH 1 FROM foo; -ERROR: cursor "foo" does not exist --- Table doesn't exist, the creation hasn't been committed yet -SELECT * FROM pxtest2; -ERROR: relation "pxtest2" does not exist -LINE 1: SELECT * FROM pxtest2; - ^ --- There should be two prepared transactions -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- pxtest3 should be locked because of the pending DROP -begin; -lock table pxtest3 in access share mode nowait; -rollback; --- Disconnect, we will continue testing in a different backend -\c - --- There should still be two prepared transactions -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- pxtest3 should still be locked because of the pending DROP -begin; -lock table pxtest3 in access share mode nowait; -rollback; --- Commit table creation -COMMIT PREPARED 'regress-one'; -ERROR: prepared transaction with identifier "regress-one" does not exist -\d pxtest2 -SELECT * FROM pxtest2; -ERROR: relation "pxtest2" does not exist -LINE 1: SELECT * FROM pxtest2; - ^ --- There should be one prepared transaction -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- Commit table drop -COMMIT PREPARED 'regress-two'; -ERROR: prepared transaction with identifier "regress-two" does not exist -SELECT * FROM pxtest3; - fff ------ -(0 rows) - --- There should be no prepared transactions -SELECT gid FROM pg_prepared_xacts; - gid ------ -(0 rows) - --- Clean up -DROP TABLE pxtest2; -ERROR: table "pxtest2" does not exist -DROP TABLE pxtest3; -- will still be there if prepared xacts are disabled -DROP TABLE pxtest4; -ERROR: table "pxtest4" does not exist +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin.out 2021-10-30 22:04:14 -0600 @@ -1,569 +1,2 @@ -CREATE TABLE brintest (byteacol bytea, - charcol "char", - namecol name, - int8col bigint, - int2col smallint, - int4col integer, - textcol text, - oidcol oid, - tidcol tid, - float4col real, - float8col double precision, - macaddrcol macaddr, - inetcol inet, - cidrcol cidr, - bpcharcol character, - datecol date, - timecol time without time zone, - timestampcol timestamp without time zone, - timestamptzcol timestamp with time zone, - intervalcol interval, - timetzcol time with time zone, - bitcol bit(10), - varbitcol bit varying(16), - numericcol numeric, - uuidcol uuid, - int4rangecol int4range, - lsncol pg_lsn, - boxcol box -) WITH (fillfactor=10, autovacuum_enabled=off); -INSERT INTO brintest SELECT - repeat(stringu1, 8)::bytea, - substr(stringu1, 1, 1)::"char", - stringu1::name, 142857 * tenthous, - thousand, - twothousand, - repeat(stringu1, 8), - unique1::oid, - format('(%s,%s)', tenthous, twenty)::tid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - inet '10.2.3.4/24' + tenthous, - cidr '10.2.3/24' + tenthous, - substr(stringu1, 1, 1)::bpchar, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20+02' + hundred * interval '15 seconds', - thousand::bit(10), - tenthous::bit(16)::varbit, - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - int4range(thousand, twothousand), - format('%s/%s%s', odd, even, tenthous)::pg_lsn, - box(point(odd, even), point(thousand, twothousand)) -FROM tenk1 ORDER BY unique2 LIMIT 100; --- throw in some NULL's and different values -INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT - inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, - cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous, - 'empty'::int4range -FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; -CREATE INDEX brinidx ON brintest USING brin ( - byteacol, - charcol, - namecol, - int8col, - int2col, - int4col, - textcol, - oidcol, - tidcol, - float4col, - float8col, - macaddrcol, - inetcol inet_inclusion_ops, - inetcol inet_minmax_ops, - cidrcol inet_inclusion_ops, - cidrcol inet_minmax_ops, - bpcharcol, - datecol, - timecol, - timestampcol, - timestamptzcol, - intervalcol, - timetzcol, - bitcol, - varbitcol, - numericcol, - uuidcol, - int4rangecol, - lsncol, - boxcol -) with (pages_per_range = 1); -CREATE TABLE brinopers (colname name, typ text, - op text[], value text[], matches int[], - check (cardinality(op) = cardinality(value)), - check (cardinality(op) = cardinality(matches))); -INSERT INTO brinopers VALUES - ('byteacol', 'bytea', - '{>, >=, =, <=, <}', - '{AAAAAA, AAAAAA, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZZZZZ, ZZZZZZ}', - '{100, 100, 1, 100, 100}'), - ('charcol', '"char"', - '{>, >=, =, <=, <}', - '{A, A, M, Z, Z}', - '{97, 100, 6, 100, 98}'), - ('namecol', 'name', - '{>, >=, =, <=, <}', - '{AAAAAA, AAAAAA, MAAAAA, ZZAAAA, ZZAAAA}', - '{100, 100, 2, 100, 100}'), - ('int2col', 'int2', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 999}', - '{100, 100, 1, 100, 100}'), - ('int2col', 'int4', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int2col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int2', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int4', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('int8col', 'int2', - '{>, >=}', - '{0, 0}', - '{100, 100}'), - ('int8col', 'int4', - '{>, >=}', - '{0, 0}', - '{100, 100}'), - ('int8col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 1257141600, 1428427143, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('textcol', 'text', - '{>, >=, =, <=, <}', - '{ABABAB, ABABAB, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZAAAA, ZZAAAA}', - '{100, 100, 1, 100, 100}'), - ('oidcol', 'oid', - '{>, >=, =, <=, <}', - '{0, 0, 8800, 9999, 9999}', - '{100, 100, 1, 100, 100}'), - ('tidcol', 'tid', - '{>, >=, =, <=, <}', - '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', - '{100, 100, 1, 100, 100}'), - ('float4col', 'float4', - '{>, >=, =, <=, <}', - '{0.0103093, 0.0103093, 1, 1, 1}', - '{100, 100, 4, 100, 96}'), - ('float4col', 'float8', - '{>, >=, =, <=, <}', - '{0.0103093, 0.0103093, 1, 1, 1}', - '{100, 100, 4, 100, 96}'), - ('float8col', 'float4', - '{>, >=, =, <=, <}', - '{0, 0, 0, 1.98, 1.98}', - '{99, 100, 1, 100, 100}'), - ('float8col', 'float8', - '{>, >=, =, <=, <}', - '{0, 0, 0, 1.98, 1.98}', - '{99, 100, 1, 100, 100}'), - ('macaddrcol', 'macaddr', - '{>, >=, =, <=, <}', - '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', - '{99, 100, 2, 100, 100}'), - ('inetcol', 'inet', - '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', - '{10/8, 10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', - '{100, 1, 100, 100, 125, 125, 2, 2, 100, 100}'), - ('inetcol', 'inet', - '{&&, >>=, <<=, =}', - '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', - '{25, 1, 25, 1}'), - ('inetcol', 'cidr', - '{&&, <, <=, >, >=, >>=, >>, <<=, <<}', - '{10/8, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', - '{100, 100, 100, 125, 125, 2, 2, 100, 100}'), - ('inetcol', 'cidr', - '{&&, >>=, <<=, =}', - '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', - '{25, 1, 25, 1}'), - ('cidrcol', 'inet', - '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', - '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', - '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), - ('cidrcol', 'inet', - '{&&, >>=, <<=, =}', - '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', - '{25, 1, 25, 1}'), - ('cidrcol', 'cidr', - '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', - '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', - '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), - ('cidrcol', 'cidr', - '{&&, >>=, <<=, =}', - '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', - '{25, 1, 25, 1}'), - ('bpcharcol', 'bpchar', - '{>, >=, =, <=, <}', - '{A, A, W, Z, Z}', - '{97, 100, 6, 100, 98}'), - ('datecol', 'date', - '{>, >=, =, <=, <}', - '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', - '{100, 100, 1, 100, 100}'), - ('timecol', 'time', - '{>, >=, =, <=, <}', - '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', - '{100, 100, 1, 100, 100}'), - ('timestampcol', 'timestamp', - '{>, >=, =, <=, <}', - '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', - '{100, 100, 1, 100, 100}'), - ('timestampcol', 'timestamptz', - '{>, >=, =, <=, <}', - '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', - '{100, 100, 1, 100, 100}'), - ('timestamptzcol', 'timestamptz', - '{>, >=, =, <=, <}', - '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', - '{100, 100, 1, 100, 100}'), - ('intervalcol', 'interval', - '{>, >=, =, <=, <}', - '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', - '{100, 100, 1, 100, 100}'), - ('timetzcol', 'timetz', - '{>, >=, =, <=, <}', - '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', - '{99, 100, 2, 100, 100}'), - ('bitcol', 'bit(10)', - '{>, >=, =, <=, <}', - '{0000000010, 0000000010, 0011011110, 1111111000, 1111111000}', - '{100, 100, 1, 100, 100}'), - ('varbitcol', 'varbit(16)', - '{>, >=, =, <=, <}', - '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}', - '{100, 100, 1, 100, 100}'), - ('numericcol', 'numeric', - '{>, >=, =, <=, <}', - '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', - '{100, 100, 1, 100, 100}'), - ('uuidcol', 'uuid', - '{>, >=, =, <=, <}', - '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', - '{100, 100, 1, 100, 100}'), - ('int4rangecol', 'int4range', - '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', - '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}', - '{53, 53, 53, 53, 50, 22, 72, 1, 74, 75, 34, 21}'), - ('int4rangecol', 'int4range', - '{@>, <@, =, <=, >, >=}', - '{empty, empty, empty, empty, empty, empty}', - '{125, 72, 72, 72, 53, 125}'), - ('int4rangecol', 'int4', - '{@>}', - '{1500}', - '{22}'), - ('lsncol', 'pg_lsn', - '{>, >=, =, <=, <, IS, IS NOT}', - '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', - '{100, 100, 1, 100, 100, 25, 100}'), - ('boxcol', 'point', - '{@>}', - '{"(500,43)"}', - '{11}'), - ('boxcol', 'box', - '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', - '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}', - '{100, 100, 100, 99, 96, 100, 100, 99, 96, 1, 99, 1}'); -DO $x$ -DECLARE - r record; - r2 record; - cond text; - idx_ctids tid[]; - ss_ctids tid[]; - count int; - plan_ok bool; - plan_line text; -BEGIN - FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers, unnest(op) WITH ORDINALITY AS oper LOOP - - -- prepare the condition - IF r.value IS NULL THEN - cond := format('%I %s %L', r.colname, r.oper, r.value); - ELSE - cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); - END IF; - - -- run the query using the brin index - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Bitmap Heap Scan on brintest%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get bitmap indexscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) - INTO idx_ctids; - - -- run the query using a seqscan - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Seq Scan on brintest%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get seqscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) - INTO ss_ctids; - - -- make sure both return the same results - count := array_length(idx_ctids, 1); - - IF NOT (count = array_length(ss_ctids, 1) AND - idx_ctids @> ss_ctids AND - idx_ctids <@ ss_ctids) THEN - -- report the results of each scan to make the differences obvious - RAISE WARNING 'something not right in %: count %', r, count; - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP - RAISE NOTICE 'seqscan: %', r2; - END LOOP; - - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP - RAISE NOTICE 'bitmapscan: %', r2; - END LOOP; - END IF; - - -- make sure we found expected number of matches - IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; - END LOOP; -END; -$x$; -RESET enable_seqscan; -RESET enable_bitmapscan; -INSERT INTO brintest SELECT - repeat(stringu1, 42)::bytea, - substr(stringu1, 1, 1)::"char", - stringu1::name, 142857 * tenthous, - thousand, - twothousand, - repeat(stringu1, 42), - unique1::oid, - format('(%s,%s)', tenthous, twenty)::tid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - inet '10.2.3.4' + tenthous, - cidr '10.2.3/24' + tenthous, - substr(stringu1, 1, 1)::bpchar, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20' + hundred * interval '15 seconds', - thousand::bit(10), - tenthous::bit(16)::varbit, - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - int4range(thousand, twothousand), - format('%s/%s%s', odd, even, tenthous)::pg_lsn, - box(point(odd, even), point(thousand, twothousand)) -FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; -SELECT brin_desummarize_range('brinidx', 0); - brin_desummarize_range ------------------------- - -(1 row) - -VACUUM brintest; -- force a summarization cycle in brinidx -UPDATE brintest SET int8col = int8col * int4col; -UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL; --- Tests for brin_summarize_new_values -SELECT brin_summarize_new_values('brintest'); -- error, not an index -ERROR: "brintest" is not an index -SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index -ERROR: "tenk1_unique1" is not a BRIN index -SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected - brin_summarize_new_values ---------------------------- - 0 -(1 row) - --- Tests for brin_desummarize_range -SELECT brin_desummarize_range('brinidx', -1); -- error, invalid range -ERROR: block number out of range: -1 -SELECT brin_desummarize_range('brinidx', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx', 100000000); - brin_desummarize_range ------------------------- - -(1 row) - --- Test brin_summarize_range -CREATE TABLE brin_summarize ( - value int -) WITH (fillfactor=10, autovacuum_enabled=false); -CREATE INDEX brin_summarize_idx ON brin_summarize USING brin (value) WITH (pages_per_range=2); --- Fill a few pages -DO $$ -DECLARE curtid tid; -BEGIN - LOOP - INSERT INTO brin_summarize VALUES (1) RETURNING ctid INTO curtid; - EXIT WHEN curtid > tid '(2, 0)'; - END LOOP; -END; -$$; --- summarize one range -SELECT brin_summarize_range('brin_summarize_idx', 0); - brin_summarize_range ----------------------- - 0 -(1 row) - --- nothing: already summarized -SELECT brin_summarize_range('brin_summarize_idx', 1); - brin_summarize_range ----------------------- - 0 -(1 row) - --- summarize one range -SELECT brin_summarize_range('brin_summarize_idx', 2); - brin_summarize_range ----------------------- - 1 -(1 row) - --- nothing: page doesn't exist in table -SELECT brin_summarize_range('brin_summarize_idx', 4294967295); - brin_summarize_range ----------------------- - 0 -(1 row) - --- invalid block number values -SELECT brin_summarize_range('brin_summarize_idx', -1); -ERROR: block number out of range: -1 -SELECT brin_summarize_range('brin_summarize_idx', 4294967296); -ERROR: block number out of range: 4294967296 --- test value merging in add_value -CREATE TABLE brintest_2 (n numrange); -CREATE INDEX brinidx_2 ON brintest_2 USING brin (n); -INSERT INTO brintest_2 VALUES ('empty'); -INSERT INTO brintest_2 VALUES (numrange(0, 2^1000::numeric)); -INSERT INTO brintest_2 VALUES ('(-1, 0)'); -SELECT brin_desummarize_range('brinidx', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_summarize_range('brinidx', 0); - brin_summarize_range ----------------------- - 1 -(1 row) - -DROP TABLE brintest_2; --- test brin cost estimates behave sanely based on correlation of values -CREATE TABLE brin_test (a INT, b INT); -INSERT INTO brin_test SELECT x/100,x%100 FROM generate_series(1,10000) x(x); -CREATE INDEX brin_test_a_idx ON brin_test USING brin (a) WITH (pages_per_range = 2); -CREATE INDEX brin_test_b_idx ON brin_test USING brin (b) WITH (pages_per_range = 2); -VACUUM ANALYZE brin_test; --- Ensure brin index is used when columns are perfectly correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1; - QUERY PLAN --------------------------------------------- - Bitmap Heap Scan on brin_test - Recheck Cond: (a = 1) - -> Bitmap Index Scan on brin_test_a_idx - Index Cond: (a = 1) -(4 rows) - --- Ensure brin index is not used when values are not correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1; - QUERY PLAN ------------------------ - Seq Scan on brin_test - Filter: (b = 1) -(2 rows) - --- make sure data are properly de-toasted in BRIN index -CREATE TABLE brintest_3 (a text, b text, c text, d text); --- long random strings (~2000 chars each, so ~6kB for min/max on two --- columns) to trigger toasting -WITH rand_value AS (SELECT string_agg(md5(i::text),'') AS val FROM generate_series(1,60) s(i)) -INSERT INTO brintest_3 -SELECT val, val, val, val FROM rand_value; -CREATE INDEX brin_test_toast_idx ON brintest_3 USING brin (b, c); -DELETE FROM brintest_3; --- We need to wait a bit for all transactions to complete, so that the --- vacuum actually removes the TOAST rows. Creating an index concurrently --- is a one way to achieve that, because it does exactly such wait. -CREATE INDEX CONCURRENTLY brin_test_temp_idx ON brintest_3(a); -DROP INDEX brin_test_temp_idx; --- vacuum the table, to discard TOAST data -VACUUM brintest_3; --- retry insert with a different random-looking (but deterministic) value --- the value is different, and so should replace either min or max in the --- brin summary -WITH rand_value AS (SELECT string_agg(md5((-i)::text),'') AS val FROM generate_series(1,60) s(i)) -INSERT INTO brintest_3 -SELECT val, val, val, val FROM rand_value; --- now try some queries, accessing the brin index -SET enable_seqscan = off; -EXPLAIN (COSTS OFF) -SELECT * FROM brintest_3 WHERE b < '0'; - QUERY PLAN ------------------------------------------------- - Bitmap Heap Scan on brintest_3 - Recheck Cond: (b < '0'::text) - -> Bitmap Index Scan on brin_test_toast_idx - Index Cond: (b < '0'::text) -(4 rows) - -SELECT * FROM brintest_3 WHERE b < '0'; - a | b | c | d ----+---+---+--- -(0 rows) - -DROP TABLE brintest_3; -RESET enable_seqscan; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/gin.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/gin.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/gin.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/gin.out 2021-10-30 22:04:14 -0600 @@ -1,290 +1,2 @@ --- --- Test GIN indexes. --- --- There are other tests to test different GIN opclasses. This is for testing --- GIN itself. --- Create and populate a test table with a GIN index. -create table gin_test_tbl(i int4[]) with (autovacuum_enabled = off); -create index gin_test_idx on gin_test_tbl using gin (i) - with (fastupdate = on, gin_pending_list_limit = 4096); -insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 20000) g; -insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; -select gin_clean_pending_list('gin_test_idx')>10 as many; -- flush the fastupdate buffers - many ------- - t -(1 row) - -insert into gin_test_tbl select array[3, 1, g] from generate_series(1, 1000) g; -vacuum gin_test_tbl; -- flush the fastupdate buffers -select gin_clean_pending_list('gin_test_idx'); -- nothing to flush - gin_clean_pending_list ------------------------- - 0 -(1 row) - --- Test vacuuming -delete from gin_test_tbl where i @> array[2]; -vacuum gin_test_tbl; --- Disable fastupdate, and do more insertions. With fastupdate enabled, most --- insertions (by flushing the list pages) cause page splits. Without --- fastupdate, we get more churn in the GIN data leaf pages, and exercise the --- recompression codepaths. -alter index gin_test_idx set (fastupdate = off); -insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 1000) g; -insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; -delete from gin_test_tbl where i @> array[2]; -vacuum gin_test_tbl; --- Test for "rare && frequent" searches -explain (costs off) -select count(*) from gin_test_tbl where i @> array[1, 999]; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on gin_test_tbl - Recheck Cond: (i @> '{1,999}'::integer[]) - -> Bitmap Index Scan on gin_test_idx - Index Cond: (i @> '{1,999}'::integer[]) -(5 rows) - -select count(*) from gin_test_tbl where i @> array[1, 999]; - count -------- - 3 -(1 row) - --- Very weak test for gin_fuzzy_search_limit -set gin_fuzzy_search_limit = 1000; -explain (costs off) -select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; - QUERY PLAN ---------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on gin_test_tbl - Recheck Cond: (i @> '{1}'::integer[]) - -> Bitmap Index Scan on gin_test_idx - Index Cond: (i @> '{1}'::integer[]) -(5 rows) - -select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; - ok ----- - t -(1 row) - -reset gin_fuzzy_search_limit; --- Test optimization of empty queries -create temp table t_gin_test_tbl(i int4[], j int4[]); -create index on t_gin_test_tbl using gin (i, j); -insert into t_gin_test_tbl -values - (null, null), - ('{}', null), - ('{1}', null), - ('{1,2}', null), - (null, '{}'), - (null, '{10}'), - ('{1,2}', '{10}'), - ('{2}', '{10}'), - ('{1,3}', '{}'), - ('{1,1}', '{10}'); -set enable_seqscan = off; -explain (costs off) -select * from t_gin_test_tbl where array[0] <@ i; - QUERY PLAN ---------------------------------------------------- - Bitmap Heap Scan on t_gin_test_tbl - Recheck Cond: ('{0}'::integer[] <@ i) - -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx - Index Cond: (i @> '{0}'::integer[]) -(4 rows) - -select * from t_gin_test_tbl where array[0] <@ i; - i | j ----+--- -(0 rows) - -select * from t_gin_test_tbl where array[0] <@ i and '{}'::int4[] <@ j; - i | j ----+--- -(0 rows) - -explain (costs off) -select * from t_gin_test_tbl where i @> '{}'; - QUERY PLAN ---------------------------------------------------- - Bitmap Heap Scan on t_gin_test_tbl - Recheck Cond: (i @> '{}'::integer[]) - -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx - Index Cond: (i @> '{}'::integer[]) -(4 rows) - -select * from t_gin_test_tbl where i @> '{}'; - i | j --------+------ - {} | - {1} | - {1,2} | - {1,2} | {10} - {2} | {10} - {1,3} | {} - {1,1} | {10} -(7 rows) - -create function explain_query_json(query_sql text) -returns table (explain_line json) -language plpgsql as -$$ -begin - set enable_seqscan = off; - set enable_bitmapscan = on; - return query execute 'EXPLAIN (ANALYZE, FORMAT json) ' || query_sql; -end; -$$; -create function execute_text_query_index(query_sql text) -returns setof text -language plpgsql -as -$$ -begin - set enable_seqscan = off; - set enable_bitmapscan = on; - return query execute query_sql; -end; -$$; -create function execute_text_query_heap(query_sql text) -returns setof text -language plpgsql -as -$$ -begin - set enable_seqscan = on; - set enable_bitmapscan = off; - return query execute query_sql; -end; -$$; --- check number of rows returned by index and removed by recheck -select - query, - js->0->'Plan'->'Plans'->0->'Actual Rows' as "return by index", - js->0->'Plan'->'Rows Removed by Index Recheck' as "removed by recheck", - (res_index = res_heap) as "match" -from - (values - ($$ i @> '{}' $$), - ($$ j @> '{}' $$), - ($$ i @> '{}' and j @> '{}' $$), - ($$ i @> '{1}' $$), - ($$ i @> '{1}' and j @> '{}' $$), - ($$ i @> '{1}' and i @> '{}' and j @> '{}' $$), - ($$ j @> '{10}' $$), - ($$ j @> '{10}' and i @> '{}' $$), - ($$ j @> '{10}' and j @> '{}' and i @> '{}' $$), - ($$ i @> '{1}' and j @> '{10}' $$) - ) q(query), - lateral explain_query_json($$select * from t_gin_test_tbl where $$ || query) js, - lateral execute_text_query_index($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res_index, - lateral execute_text_query_heap($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res_heap; - query | return by index | removed by recheck | match --------------------------------------------+-----------------+--------------------+------- - i @> '{}' | 7 | 0 | t - j @> '{}' | 6 | 0 | t - i @> '{}' and j @> '{}' | 4 | 0 | t - i @> '{1}' | 5 | 0 | t - i @> '{1}' and j @> '{}' | 3 | 0 | t - i @> '{1}' and i @> '{}' and j @> '{}' | 3 | 0 | t - j @> '{10}' | 4 | 0 | t - j @> '{10}' and i @> '{}' | 3 | 0 | t - j @> '{10}' and j @> '{}' and i @> '{}' | 3 | 0 | t - i @> '{1}' and j @> '{10}' | 2 | 0 | t -(10 rows) - -reset enable_seqscan; -reset enable_bitmapscan; --- re-purpose t_gin_test_tbl to test scans involving posting trees -insert into t_gin_test_tbl select array[1, g, g/10], array[2, g, g/10] - from generate_series(1, 20000) g; -select gin_clean_pending_list('t_gin_test_tbl_i_j_idx') is not null; - ?column? ----------- - t -(1 row) - -analyze t_gin_test_tbl; -set enable_seqscan = off; -set enable_bitmapscan = on; -explain (costs off) -select count(*) from t_gin_test_tbl where j @> array[50]; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on t_gin_test_tbl - Recheck Cond: (j @> '{50}'::integer[]) - -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx - Index Cond: (j @> '{50}'::integer[]) -(5 rows) - -select count(*) from t_gin_test_tbl where j @> array[50]; - count -------- - 11 -(1 row) - -explain (costs off) -select count(*) from t_gin_test_tbl where j @> array[2]; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on t_gin_test_tbl - Recheck Cond: (j @> '{2}'::integer[]) - -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx - Index Cond: (j @> '{2}'::integer[]) -(5 rows) - -select count(*) from t_gin_test_tbl where j @> array[2]; - count -------- - 20000 -(1 row) - -explain (costs off) -select count(*) from t_gin_test_tbl where j @> '{}'::int[]; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on t_gin_test_tbl - Recheck Cond: (j @> '{}'::integer[]) - -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx - Index Cond: (j @> '{}'::integer[]) -(5 rows) - -select count(*) from t_gin_test_tbl where j @> '{}'::int[]; - count -------- - 20006 -(1 row) - --- test vacuuming of posting trees -delete from t_gin_test_tbl where j @> array[2]; -vacuum t_gin_test_tbl; -select count(*) from t_gin_test_tbl where j @> array[50]; - count -------- - 0 -(1 row) - -select count(*) from t_gin_test_tbl where j @> array[2]; - count -------- - 0 -(1 row) - -select count(*) from t_gin_test_tbl where j @> '{}'::int[]; - count -------- - 6 -(1 row) - -reset enable_seqscan; -reset enable_bitmapscan; -drop table t_gin_test_tbl; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/gist.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/gist.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/gist.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/gist.out 2021-10-30 22:04:14 -0600 @@ -1,319 +1,2 @@ --- --- Test GiST indexes. --- --- There are other tests to test different GiST opclasses. This is for --- testing GiST code itself. Vacuuming in particular. -create table gist_point_tbl(id int4, p point); -create index gist_pointidx on gist_point_tbl using gist(p); --- Verify the fillfactor and buffering options -create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50); -create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off); -create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto); -drop index gist_pointidx2, gist_pointidx3, gist_pointidx4; --- Make sure bad values are refused -create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value); -ERROR: invalid value for enum option "buffering": invalid_value -DETAIL: Valid values are "on", "off", and "auto". -create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9); -ERROR: value 9 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101); -ERROR: value 101 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". --- Insert enough data to create a tree that's a couple of levels deep. -insert into gist_point_tbl (id, p) -select g, point(g*10, g*10) from generate_series(1, 10000) g; -insert into gist_point_tbl (id, p) -select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g; --- To test vacuum, delete some entries from all over the index. -delete from gist_point_tbl where id % 2 = 1; --- And also delete some concentration of values. -delete from gist_point_tbl where id > 5000; -vacuum analyze gist_point_tbl; --- rebuild the index with a different fillfactor -alter index gist_pointidx SET (fillfactor = 40); -reindex index gist_pointidx; --- --- Test Index-only plans on GiST indexes --- -create table gist_tbl (b box, p point, c circle); -insert into gist_tbl -select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)), - point(0.05*i, 0.05*i), - circle(point(0.05*i, 0.05*i), 1.0) -from generate_series(0,10000) as i; -vacuum analyze gist_tbl; -set enable_seqscan=off; -set enable_bitmapscan=off; -set enable_indexonlyscan=on; --- Test index-only scan with point opclass -create index gist_tbl_point_index on gist_tbl using gist (p); --- check that the planner chooses an index-only scan -explain (costs off) -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)); - QUERY PLAN --------------------------------------------------------- - Index Only Scan using gist_tbl_point_index on gist_tbl - Index Cond: (p <@ '(0.5,0.5),(0,0)'::box) -(2 rows) - --- execute the same -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)); - p -------------- - (0,0) - (0.05,0.05) - (0.1,0.1) - (0.15,0.15) - (0.2,0.2) - (0.25,0.25) - (0.3,0.3) - (0.35,0.35) - (0.4,0.4) - (0.45,0.45) - (0.5,0.5) -(11 rows) - --- Also test an index-only knn-search -explain (costs off) -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) -order by p <-> point(0.201, 0.201); - QUERY PLAN --------------------------------------------------------- - Index Only Scan using gist_tbl_point_index on gist_tbl - Index Cond: (p <@ '(0.5,0.5),(0,0)'::box) - Order By: (p <-> '(0.201,0.201)'::point) -(3 rows) - -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) -order by p <-> point(0.201, 0.201); - p -------------- - (0.2,0.2) - (0.25,0.25) - (0.15,0.15) - (0.3,0.3) - (0.1,0.1) - (0.35,0.35) - (0.05,0.05) - (0.4,0.4) - (0,0) - (0.45,0.45) - (0.5,0.5) -(11 rows) - --- Check commuted case as well -explain (costs off) -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) -order by point(0.101, 0.101) <-> p; - QUERY PLAN --------------------------------------------------------- - Index Only Scan using gist_tbl_point_index on gist_tbl - Index Cond: (p <@ '(0.5,0.5),(0,0)'::box) - Order By: (p <-> '(0.101,0.101)'::point) -(3 rows) - -select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) -order by point(0.101, 0.101) <-> p; - p -------------- - (0.1,0.1) - (0.15,0.15) - (0.05,0.05) - (0.2,0.2) - (0,0) - (0.25,0.25) - (0.3,0.3) - (0.35,0.35) - (0.4,0.4) - (0.45,0.45) - (0.5,0.5) -(11 rows) - --- Check case with multiple rescans (bug #14641) -explain (costs off) -select p from - (values (box(point(0,0), point(0.5,0.5))), - (box(point(0.5,0.5), point(0.75,0.75))), - (box(point(0.8,0.8), point(1.0,1.0)))) as v(bb) -cross join lateral - (select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss; - QUERY PLAN --------------------------------------------------------------------- - Nested Loop - -> Values Scan on "*VALUES*" - -> Limit - -> Index Only Scan using gist_tbl_point_index on gist_tbl - Index Cond: (p <@ "*VALUES*".column1) - Order By: (p <-> ("*VALUES*".column1)[0]) -(6 rows) - -select p from - (values (box(point(0,0), point(0.5,0.5))), - (box(point(0.5,0.5), point(0.75,0.75))), - (box(point(0.8,0.8), point(1.0,1.0)))) as v(bb) -cross join lateral - (select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss; - p -------------- - (0.5,0.5) - (0.45,0.45) - (0.75,0.75) - (0.7,0.7) - (1,1) - (0.95,0.95) -(6 rows) - -drop index gist_tbl_point_index; --- Test index-only scan with box opclass -create index gist_tbl_box_index on gist_tbl using gist (b); --- check that the planner chooses an index-only scan -explain (costs off) -select b from gist_tbl where b <@ box(point(5,5), point(6,6)); - QUERY PLAN ------------------------------------------------------- - Index Only Scan using gist_tbl_box_index on gist_tbl - Index Cond: (b <@ '(6,6),(5,5)'::box) -(2 rows) - --- execute the same -select b from gist_tbl where b <@ box(point(5,5), point(6,6)); - b -------------------------- - (5,5),(5,5) - (5.05,5.05),(5.05,5.05) - (5.1,5.1),(5.1,5.1) - (5.15,5.15),(5.15,5.15) - (5.2,5.2),(5.2,5.2) - (5.25,5.25),(5.25,5.25) - (5.3,5.3),(5.3,5.3) - (5.35,5.35),(5.35,5.35) - (5.4,5.4),(5.4,5.4) - (5.45,5.45),(5.45,5.45) - (5.5,5.5),(5.5,5.5) - (5.55,5.55),(5.55,5.55) - (5.6,5.6),(5.6,5.6) - (5.65,5.65),(5.65,5.65) - (5.7,5.7),(5.7,5.7) - (5.75,5.75),(5.75,5.75) - (5.8,5.8),(5.8,5.8) - (5.85,5.85),(5.85,5.85) - (5.9,5.9),(5.9,5.9) - (5.95,5.95),(5.95,5.95) - (6,6),(6,6) -(21 rows) - --- Also test an index-only knn-search -explain (costs off) -select b from gist_tbl where b <@ box(point(5,5), point(6,6)) -order by b <-> point(5.2, 5.91); - QUERY PLAN ------------------------------------------------------- - Index Only Scan using gist_tbl_box_index on gist_tbl - Index Cond: (b <@ '(6,6),(5,5)'::box) - Order By: (b <-> '(5.2,5.91)'::point) -(3 rows) - -select b from gist_tbl where b <@ box(point(5,5), point(6,6)) -order by b <-> point(5.2, 5.91); - b -------------------------- - (5.55,5.55),(5.55,5.55) - (5.6,5.6),(5.6,5.6) - (5.5,5.5),(5.5,5.5) - (5.65,5.65),(5.65,5.65) - (5.45,5.45),(5.45,5.45) - (5.7,5.7),(5.7,5.7) - (5.4,5.4),(5.4,5.4) - (5.75,5.75),(5.75,5.75) - (5.35,5.35),(5.35,5.35) - (5.8,5.8),(5.8,5.8) - (5.3,5.3),(5.3,5.3) - (5.85,5.85),(5.85,5.85) - (5.25,5.25),(5.25,5.25) - (5.9,5.9),(5.9,5.9) - (5.2,5.2),(5.2,5.2) - (5.95,5.95),(5.95,5.95) - (5.15,5.15),(5.15,5.15) - (6,6),(6,6) - (5.1,5.1),(5.1,5.1) - (5.05,5.05),(5.05,5.05) - (5,5),(5,5) -(21 rows) - --- Check commuted case as well -explain (costs off) -select b from gist_tbl where b <@ box(point(5,5), point(6,6)) -order by point(5.2, 5.91) <-> b; - QUERY PLAN ------------------------------------------------------- - Index Only Scan using gist_tbl_box_index on gist_tbl - Index Cond: (b <@ '(6,6),(5,5)'::box) - Order By: (b <-> '(5.2,5.91)'::point) -(3 rows) - -select b from gist_tbl where b <@ box(point(5,5), point(6,6)) -order by point(5.2, 5.91) <-> b; - b -------------------------- - (5.55,5.55),(5.55,5.55) - (5.6,5.6),(5.6,5.6) - (5.5,5.5),(5.5,5.5) - (5.65,5.65),(5.65,5.65) - (5.45,5.45),(5.45,5.45) - (5.7,5.7),(5.7,5.7) - (5.4,5.4),(5.4,5.4) - (5.75,5.75),(5.75,5.75) - (5.35,5.35),(5.35,5.35) - (5.8,5.8),(5.8,5.8) - (5.3,5.3),(5.3,5.3) - (5.85,5.85),(5.85,5.85) - (5.25,5.25),(5.25,5.25) - (5.9,5.9),(5.9,5.9) - (5.2,5.2),(5.2,5.2) - (5.95,5.95),(5.95,5.95) - (5.15,5.15),(5.15,5.15) - (6,6),(6,6) - (5.1,5.1),(5.1,5.1) - (5.05,5.05),(5.05,5.05) - (5,5),(5,5) -(21 rows) - -drop index gist_tbl_box_index; --- Test that an index-only scan is not chosen, when the query involves the --- circle column (the circle opclass does not support index-only scans). -create index gist_tbl_multi_index on gist_tbl using gist (p, c); -explain (costs off) -select p, c from gist_tbl -where p <@ box(point(5,5), point(6, 6)); - QUERY PLAN ---------------------------------------------------- - Index Scan using gist_tbl_multi_index on gist_tbl - Index Cond: (p <@ '(6,6),(5,5)'::box) -(2 rows) - --- execute the same -select b, p from gist_tbl -where b <@ box(point(4.5, 4.5), point(5.5, 5.5)) -and p <@ box(point(5,5), point(6, 6)); - b | p --------------------------+------------- - (5,5),(5,5) | (5,5) - (5.05,5.05),(5.05,5.05) | (5.05,5.05) - (5.1,5.1),(5.1,5.1) | (5.1,5.1) - (5.15,5.15),(5.15,5.15) | (5.15,5.15) - (5.2,5.2),(5.2,5.2) | (5.2,5.2) - (5.25,5.25),(5.25,5.25) | (5.25,5.25) - (5.3,5.3),(5.3,5.3) | (5.3,5.3) - (5.35,5.35),(5.35,5.35) | (5.35,5.35) - (5.4,5.4),(5.4,5.4) | (5.4,5.4) - (5.45,5.45),(5.45,5.45) | (5.45,5.45) - (5.5,5.5),(5.5,5.5) | (5.5,5.5) -(11 rows) - -drop index gist_tbl_multi_index; --- Clean up -reset enable_seqscan; -reset enable_bitmapscan; -reset enable_indexonlyscan; -drop table gist_tbl; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/spgist.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/spgist.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/spgist.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/spgist.out 2021-10-30 22:04:14 -0600 @@ -1,67 +1,2 @@ --- --- Test SP-GiST indexes. --- --- There are other tests to test different SP-GiST opclasses. This is for --- testing SP-GiST code itself. -create table spgist_point_tbl(id int4, p point); -create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75); --- Test vacuum-root operation. It gets invoked when the root is also a leaf, --- i.e. the index is very small. -insert into spgist_point_tbl (id, p) -select g, point(g*10, g*10) from generate_series(1, 10) g; -delete from spgist_point_tbl where id < 5; -vacuum spgist_point_tbl; --- Insert more data, to make the index a few levels deep. -insert into spgist_point_tbl (id, p) -select g, point(g*10, g*10) from generate_series(1, 10000) g; -insert into spgist_point_tbl (id, p) -select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g; --- To test vacuum, delete some entries from all over the index. -delete from spgist_point_tbl where id % 2 = 1; --- And also delete some concentration of values. (SP-GiST doesn't currently --- attempt to delete pages even when they become empty, but if it did, this --- would exercise it) -delete from spgist_point_tbl where id < 10000; -vacuum spgist_point_tbl; --- Test rescan paths (cf. bug #15378) --- use box and && rather than point, so that rescan happens when the --- traverse stack is non-empty -create table spgist_box_tbl(id serial, b box); -insert into spgist_box_tbl(b) -select box(point(i,j),point(i+s,j+s)) - from generate_series(1,100,5) i, - generate_series(1,100,5) j, - generate_series(1,10) s; -create index spgist_box_idx on spgist_box_tbl using spgist (b); -select count(*) - from (values (point(5,5)),(point(8,8)),(point(12,12))) v(p) - where exists(select * from spgist_box_tbl b where b.b && box(v.p,v.p)); - count -------- - 3 -(1 row) - --- The point opclass's choose method only uses the spgMatchNode action, --- so the other actions are not tested by the above. Create an index using --- text opclass, which uses the others actions. -create table spgist_text_tbl(id int4, t text); -create index spgist_text_idx on spgist_text_tbl using spgist(t); -insert into spgist_text_tbl (id, t) -select g, 'f' || repeat('o', 100) || g from generate_series(1, 10000) g -union all -select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g; --- Do a lot of insertions that have to split an existing node. Hopefully --- one of these will cause the page to run out of space, causing the inner --- tuple to be moved to another page. -insert into spgist_text_tbl (id, t) -select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g; --- Test out-of-range fillfactor values -create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9); -ERROR: value 9 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101); -ERROR: value 101 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". --- Modify fillfactor in existing index -alter index spgist_point_idx set (fillfactor = 90); -reindex index spgist_point_idx; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/privileges.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/privileges.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/privileges.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/privileges.out 2021-10-30 22:04:14 -0600 @@ -1,2415 +1,2 @@ --- --- Test access privileges --- --- Clean up in case a prior regression run failed --- Suppress NOTICE messages when users/groups don't exist -SET client_min_messages TO 'warning'; -DROP ROLE IF EXISTS regress_priv_group1; -DROP ROLE IF EXISTS regress_priv_group2; -DROP ROLE IF EXISTS regress_priv_user1; -DROP ROLE IF EXISTS regress_priv_user2; -DROP ROLE IF EXISTS regress_priv_user3; -DROP ROLE IF EXISTS regress_priv_user4; -DROP ROLE IF EXISTS regress_priv_user5; -DROP ROLE IF EXISTS regress_priv_user6; -DROP ROLE IF EXISTS regress_priv_user7; -SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; - lo_unlink ------------ -(0 rows) - -RESET client_min_messages; --- test proper begins here -CREATE USER regress_priv_user1; -CREATE USER regress_priv_user2; -CREATE USER regress_priv_user3; -CREATE USER regress_priv_user4; -CREATE USER regress_priv_user5; -CREATE USER regress_priv_user5; -- duplicate -ERROR: role "regress_priv_user5" already exists -CREATE USER regress_priv_user6; -CREATE USER regress_priv_user7; -GRANT pg_read_all_data TO regress_priv_user6; -GRANT pg_write_all_data TO regress_priv_user7; -CREATE GROUP regress_priv_group1; -CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2; -ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4; -ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate -NOTICE: role "regress_priv_user2" is already a member of role "regress_priv_group2" -ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; -GRANT regress_priv_group2 TO regress_priv_user4 WITH ADMIN OPTION; --- prepare non-leakproof function for later -CREATE FUNCTION leak(integer,integer) RETURNS boolean - AS 'int4lt' - LANGUAGE internal IMMUTABLE STRICT; -- but deliberately not LEAKPROOF -ALTER FUNCTION leak(integer,integer) OWNER TO regress_priv_user1; --- test owner privileges -SET SESSION AUTHORIZATION regress_priv_user1; -SELECT session_user, current_user; - session_user | current_user ---------------------+-------------------- - regress_priv_user1 | regress_priv_user1 -(1 row) - -CREATE TABLE atest1 ( a int, b text ); -SELECT * FROM atest1; - a | b ----+--- -(0 rows) - -INSERT INTO atest1 VALUES (1, 'one'); -DELETE FROM atest1; -UPDATE atest1 SET a = 1 WHERE b = 'blech'; -TRUNCATE atest1; -BEGIN; -LOCK atest1 IN ACCESS EXCLUSIVE MODE; -COMMIT; -REVOKE ALL ON atest1 FROM PUBLIC; -SELECT * FROM atest1; - a | b ----+--- -(0 rows) - -GRANT ALL ON atest1 TO regress_priv_user2; -GRANT SELECT ON atest1 TO regress_priv_user3, regress_priv_user4; -SELECT * FROM atest1; - a | b ----+--- -(0 rows) - -CREATE TABLE atest2 (col1 varchar(10), col2 boolean); -GRANT SELECT ON atest2 TO regress_priv_user2; -GRANT UPDATE ON atest2 TO regress_priv_user3; -GRANT INSERT ON atest2 TO regress_priv_user4 GRANTED BY CURRENT_USER; -GRANT TRUNCATE ON atest2 TO regress_priv_user5 GRANTED BY CURRENT_ROLE; -GRANT TRUNCATE ON atest2 TO regress_priv_user4 GRANTED BY regress_priv_user5; -- error -ERROR: grantor must be current user -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT session_user, current_user; - session_user | current_user ---------------------+-------------------- - regress_priv_user2 | regress_priv_user2 -(1 row) - --- try various combinations of queries on atest1 and atest2 -SELECT * FROM atest1; -- ok - a | b ----+--- -(0 rows) - -SELECT * FROM atest2; -- ok - col1 | col2 -------+------ -(0 rows) - -INSERT INTO atest1 VALUES (2, 'two'); -- ok -INSERT INTO atest2 VALUES ('foo', true); -- fail -ERROR: permission denied for table atest2 -INSERT INTO atest1 SELECT 1, b FROM atest1; -- ok -UPDATE atest1 SET a = 1 WHERE a = 2; -- ok -UPDATE atest2 SET col2 = NOT col2; -- fail -ERROR: permission denied for table atest2 -SELECT * FROM atest1 FOR UPDATE; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT * FROM atest2 FOR UPDATE; -- fail -ERROR: permission denied for table atest2 -DELETE FROM atest2; -- fail -ERROR: permission denied for table atest2 -TRUNCATE atest2; -- fail -ERROR: permission denied for table atest2 -BEGIN; -LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail -ERROR: permission denied for table atest2 -COMMIT; -COPY atest2 FROM stdin; -- fail -ERROR: permission denied for table atest2 -GRANT ALL ON atest1 TO PUBLIC; -- fail -WARNING: no privileges were granted for "atest1" --- checks in subquery, both ok -SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); - a | b ----+--- -(0 rows) - -SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); - col1 | col2 -------+------ -(0 rows) - -SET SESSION AUTHORIZATION regress_priv_user6; -SELECT * FROM atest1; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT * FROM atest2; -- ok - col1 | col2 -------+------ -(0 rows) - -INSERT INTO atest2 VALUES ('foo', true); -- fail -ERROR: permission denied for table atest2 -SET SESSION AUTHORIZATION regress_priv_user7; -SELECT * FROM atest1; -- fail -ERROR: permission denied for table atest1 -SELECT * FROM atest2; -- fail -ERROR: permission denied for table atest2 -INSERT INTO atest2 VALUES ('foo', true); -- ok -UPDATE atest2 SET col2 = true; -- ok -DELETE FROM atest2; -- ok --- Make sure we are not able to modify system catalogs -UPDATE pg_catalog.pg_class SET relname = '123'; -- fail -ERROR: permission denied for table pg_class -DELETE FROM pg_catalog.pg_class; -- fail -ERROR: permission denied for table pg_class -UPDATE pg_toast.pg_toast_1213 SET chunk_id = 1; -- fail -ERROR: permission denied for table pg_toast_1213 -SET SESSION AUTHORIZATION regress_priv_user3; -SELECT session_user, current_user; - session_user | current_user ---------------------+-------------------- - regress_priv_user3 | regress_priv_user3 -(1 row) - -SELECT * FROM atest1; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT * FROM atest2; -- fail -ERROR: permission denied for table atest2 -INSERT INTO atest1 VALUES (2, 'two'); -- fail -ERROR: permission denied for table atest1 -INSERT INTO atest2 VALUES ('foo', true); -- fail -ERROR: permission denied for table atest2 -INSERT INTO atest1 SELECT 1, b FROM atest1; -- fail -ERROR: permission denied for table atest1 -UPDATE atest1 SET a = 1 WHERE a = 2; -- fail -ERROR: permission denied for table atest1 -UPDATE atest2 SET col2 = NULL; -- ok -UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2 -ERROR: permission denied for table atest2 -UPDATE atest2 SET col2 = true FROM atest1 WHERE atest1.a = 5; -- ok -SELECT * FROM atest1 FOR UPDATE; -- fail -ERROR: permission denied for table atest1 -SELECT * FROM atest2 FOR UPDATE; -- fail -ERROR: permission denied for table atest2 -DELETE FROM atest2; -- fail -ERROR: permission denied for table atest2 -TRUNCATE atest2; -- fail -ERROR: permission denied for table atest2 -BEGIN; -LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- ok -COMMIT; -COPY atest2 FROM stdin; -- fail -ERROR: permission denied for table atest2 --- checks in subquery, both fail -SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); -ERROR: permission denied for table atest2 -SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); -ERROR: permission denied for table atest2 -SET SESSION AUTHORIZATION regress_priv_user4; -COPY atest2 FROM stdin; -- ok -SELECT * FROM atest1; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - --- test leaky-function protections in selfuncs --- regress_priv_user1 will own a table and provide views for it. -SET SESSION AUTHORIZATION regress_priv_user1; -CREATE TABLE atest12 as - SELECT x AS a, 10001 - x AS b FROM generate_series(1,10000) x; -CREATE INDEX ON atest12 (a); -CREATE INDEX ON atest12 (abs(a)); --- results below depend on having quite accurate stats for atest12, so... -ALTER TABLE atest12 SET (autovacuum_enabled = off); -SET default_statistics_target = 10000; -VACUUM ANALYZE atest12; -RESET default_statistics_target; -CREATE OPERATOR <<< (procedure = leak, leftarg = integer, rightarg = integer, - restrict = scalarltsel); --- views with leaky operator -CREATE VIEW atest12v AS - SELECT * FROM atest12 WHERE b <<< 5; -CREATE VIEW atest12sbv WITH (security_barrier=true) AS - SELECT * FROM atest12 WHERE b <<< 5; -GRANT SELECT ON atest12v TO PUBLIC; -GRANT SELECT ON atest12sbv TO PUBLIC; --- This plan should use nestloop, knowing that few rows will be selected. -EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) - -> Index Scan using atest12_a_idx on atest12 - Index Cond: (a = atest12_1.b) - Filter: (b <<< 5) -(6 rows) - --- And this one. -EXPLAIN (COSTS OFF) SELECT * FROM atest12 x, atest12 y - WHERE x.a = y.b and abs(y.a) <<< 5; - QUERY PLAN ---------------------------------------------------- - Nested Loop - -> Seq Scan on atest12 y - Filter: (abs(a) <<< 5) - -> Index Scan using atest12_a_idx on atest12 x - Index Cond: (a = y.b) -(5 rows) - --- This should also be a nestloop, but the security barrier forces the inner --- scan to be materialized -EXPLAIN (COSTS OFF) SELECT * FROM atest12sbv x, atest12sbv y WHERE x.a = y.b; - QUERY PLAN -------------------------------------------- - Nested Loop - Join Filter: (atest12.a = atest12_1.b) - -> Seq Scan on atest12 - Filter: (b <<< 5) - -> Materialize - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) -(7 rows) - --- Check if regress_priv_user2 can break security. -SET SESSION AUTHORIZATION regress_priv_user2; -CREATE FUNCTION leak2(integer,integer) RETURNS boolean - AS $$begin raise notice 'leak % %', $1, $2; return $1 > $2; end$$ - LANGUAGE plpgsql immutable; -CREATE OPERATOR >>> (procedure = leak2, leftarg = integer, rightarg = integer, - restrict = scalargtsel); --- This should not show any "leak" notices before failing. -EXPLAIN (COSTS OFF) SELECT * FROM atest12 WHERE a >>> 0; -ERROR: permission denied for table atest12 --- These plans should continue to use a nestloop, since they execute with the --- privileges of the view owner. -EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) - -> Index Scan using atest12_a_idx on atest12 - Index Cond: (a = atest12_1.b) - Filter: (b <<< 5) -(6 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM atest12sbv x, atest12sbv y WHERE x.a = y.b; - QUERY PLAN -------------------------------------------- - Nested Loop - Join Filter: (atest12.a = atest12_1.b) - -> Seq Scan on atest12 - Filter: (b <<< 5) - -> Materialize - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) -(7 rows) - --- A non-security barrier view does not guard against information leakage. -EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y - WHERE x.a = y.b and abs(y.a) <<< 5; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on atest12 atest12_1 - Filter: ((b <<< 5) AND (abs(a) <<< 5)) - -> Index Scan using atest12_a_idx on atest12 - Index Cond: (a = atest12_1.b) - Filter: (b <<< 5) -(6 rows) - --- But a security barrier view isolates the leaky operator. -EXPLAIN (COSTS OFF) SELECT * FROM atest12sbv x, atest12sbv y - WHERE x.a = y.b and abs(y.a) <<< 5; - QUERY PLAN -------------------------------------- - Nested Loop - Join Filter: (atest12_1.a = y.b) - -> Subquery Scan on y - Filter: (abs(y.a) <<< 5) - -> Seq Scan on atest12 - Filter: (b <<< 5) - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) -(8 rows) - --- Now regress_priv_user1 grants sufficient access to regress_priv_user2. -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT SELECT (a, b) ON atest12 TO PUBLIC; -SET SESSION AUTHORIZATION regress_priv_user2; --- regress_priv_user2 should continue to get a good row estimate. -EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on atest12 atest12_1 - Filter: (b <<< 5) - -> Index Scan using atest12_a_idx on atest12 - Index Cond: (a = atest12_1.b) - Filter: (b <<< 5) -(6 rows) - --- But not for this, due to lack of table-wide permissions needed --- to make use of the expression index's statistics. -EXPLAIN (COSTS OFF) SELECT * FROM atest12 x, atest12 y - WHERE x.a = y.b and abs(y.a) <<< 5; - QUERY PLAN --------------------------------------- - Hash Join - Hash Cond: (x.a = y.b) - -> Seq Scan on atest12 x - -> Hash - -> Seq Scan on atest12 y - Filter: (abs(a) <<< 5) -(6 rows) - --- clean up (regress_priv_user1's objects are all dropped later) -DROP FUNCTION leak2(integer, integer) CASCADE; -NOTICE: drop cascades to operator >>>(integer,integer) --- groups -SET SESSION AUTHORIZATION regress_priv_user3; -CREATE TABLE atest3 (one int, two int, three int); -GRANT DELETE ON atest3 TO GROUP regress_priv_group2; -SET SESSION AUTHORIZATION regress_priv_user1; -SELECT * FROM atest3; -- fail -ERROR: permission denied for table atest3 -DELETE FROM atest3; -- ok -BEGIN; -RESET SESSION AUTHORIZATION; -ALTER ROLE regress_priv_user1 NOINHERIT; -SET SESSION AUTHORIZATION regress_priv_user1; -DELETE FROM atest3; -ERROR: permission denied for table atest3 -ROLLBACK; --- views -SET SESSION AUTHORIZATION regress_priv_user3; -CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok -/* The next *should* fail, but it's not implemented that way yet. */ -CREATE VIEW atestv2 AS SELECT * FROM atest2; -CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok -/* Empty view is a corner case that failed in 9.2. */ -CREATE VIEW atestv0 AS SELECT 0 as x WHERE false; -- ok -SELECT * FROM atestv1; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT * FROM atestv2; -- fail -ERROR: permission denied for table atest2 -GRANT SELECT ON atestv1, atestv3 TO regress_priv_user4; -GRANT SELECT ON atestv2 TO regress_priv_user2; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT * FROM atestv1; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT * FROM atestv2; -- fail -ERROR: permission denied for view atestv2 -SELECT * FROM atestv3; -- ok - one | two | three ------+-----+------- -(0 rows) - -SELECT * FROM atestv0; -- fail -ERROR: permission denied for view atestv0 --- Appendrels excluded by constraints failed to check permissions in 8.4-9.2. -select * from - ((select a.q1 as x from int8_tbl a offset 0) - union all - (select b.q2 as x from int8_tbl b offset 0)) ss -where false; -ERROR: permission denied for table int8_tbl -set constraint_exclusion = on; -select * from - ((select a.q1 as x, random() from int8_tbl a where q1 > 0) - union all - (select b.q2 as x, random() from int8_tbl b where q2 > 0)) ss -where x < 0; -ERROR: permission denied for table int8_tbl -reset constraint_exclusion; -CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view -SELECT * FROM atestv4; -- ok - one | two | three ------+-----+------- -(0 rows) - -GRANT SELECT ON atestv4 TO regress_priv_user2; -SET SESSION AUTHORIZATION regress_priv_user2; --- Two complex cases: -SELECT * FROM atestv3; -- fail -ERROR: permission denied for view atestv3 -SELECT * FROM atestv4; -- ok (even though regress_priv_user2 cannot access underlying atestv3) - one | two | three ------+-----+------- -(0 rows) - -SELECT * FROM atest2; -- ok - col1 | col2 -------+------ - bar | t -(1 row) - -SELECT * FROM atestv2; -- fail (even though regress_priv_user2 can access underlying atest2) -ERROR: permission denied for table atest2 --- Test column level permissions -SET SESSION AUTHORIZATION regress_priv_user1; -CREATE TABLE atest5 (one int, two int unique, three int, four int unique); -CREATE TABLE atest6 (one int, two int, blue int); -GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regress_priv_user4; -GRANT ALL (one) ON atest5 TO regress_priv_user3; -INSERT INTO atest5 VALUES (1,2,3); -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT * FROM atest5; -- fail -ERROR: permission denied for table atest5 -SELECT one FROM atest5; -- ok - one ------ - 1 -(1 row) - -COPY atest5 (one) TO stdout; -- ok -1 -SELECT two FROM atest5; -- fail -ERROR: permission denied for table atest5 -COPY atest5 (two) TO stdout; -- fail -ERROR: permission denied for table atest5 -SELECT atest5 FROM atest5; -- fail -ERROR: permission denied for table atest5 -COPY atest5 (one,two) TO stdout; -- fail -ERROR: permission denied for table atest5 -SELECT 1 FROM atest5; -- ok - ?column? ----------- - 1 -(1 row) - -SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok - ?column? ----------- - 1 -(1 row) - -SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail -ERROR: permission denied for table atest5 -SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail -ERROR: permission denied for table atest5 -SELECT * FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT j.* FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT one FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- ok - one ------ - 1 -(1 row) - -SELECT j.one FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- ok - one ------ - 1 -(1 row) - -SELECT two FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT j.two FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT y FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT j.y FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)) j; -- fail -ERROR: permission denied for table atest5 -SELECT * FROM (atest5 a JOIN atest5 b USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT a.* FROM (atest5 a JOIN atest5 b USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT (a.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT two FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT a.two FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT y FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT b.y FROM (atest5 a JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT y FROM (atest5 a LEFT JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT b.y FROM (atest5 a LEFT JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT y FROM (atest5 a FULL JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT b.y FROM (atest5 a FULL JOIN atest5 b(one,x,y,z) USING (one)); -- fail -ERROR: permission denied for table atest5 -SELECT 1 FROM atest5 WHERE two = 2; -- fail -ERROR: permission denied for table atest5 -SELECT * FROM atest1, atest5; -- fail -ERROR: permission denied for table atest5 -SELECT atest1.* FROM atest1, atest5; -- ok - a | b ----+----- - 1 | two - 1 | two -(2 rows) - -SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok - a | b | one ----+-----+----- - 1 | two | 1 - 1 | two | 1 -(2 rows) - -SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail -ERROR: permission denied for table atest5 -SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok - a | b | one ----+-----+----- - 1 | two | 1 - 1 | two | 1 -(2 rows) - -SELECT one, two FROM atest5; -- fail -ERROR: permission denied for table atest5 -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT SELECT (one,two) ON atest6 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still -ERROR: permission denied for table atest5 -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT SELECT (two) ON atest5 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now - one | two ------+----- -(0 rows) - --- test column-level privileges for INSERT and UPDATE -INSERT INTO atest5 (two) VALUES (3); -- ok -COPY atest5 FROM stdin; -- fail -ERROR: permission denied for table atest5 -COPY atest5 (two) FROM stdin; -- ok -INSERT INTO atest5 (three) VALUES (4); -- fail -ERROR: permission denied for table atest5 -INSERT INTO atest5 VALUES (5,5,5); -- fail -ERROR: permission denied for table atest5 -UPDATE atest5 SET three = 10; -- ok -UPDATE atest5 SET one = 8; -- fail -ERROR: permission denied for table atest5 -UPDATE atest5 SET three = 5, one = 2; -- fail -ERROR: permission denied for table atest5 --- Check that column level privs are enforced in RETURNING --- Ok. -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10; --- Error. No SELECT on column three. -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10 RETURNING atest5.three; -ERROR: permission denied for table atest5 --- Ok. May SELECT on column "one": -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10 RETURNING atest5.one; - one ------ - -(1 row) - --- Check that column level privileges are enforced for EXCLUDED --- Ok. we may select one -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = EXCLUDED.one; --- Error. No select rights on three -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = EXCLUDED.three; -ERROR: permission denied for table atest5 -INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set one = 8; -- fails (due to UPDATE) -ERROR: permission denied for table atest5 -INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) DO UPDATE set three = 10; -- fails (due to INSERT) -ERROR: permission denied for table atest5 --- Check that the columns in the inference require select privileges -INSERT INTO atest5(four) VALUES (4); -- fail -ERROR: permission denied for table atest5 -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT INSERT (four) ON atest5 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -INSERT INTO atest5(four) VALUES (4) ON CONFLICT (four) DO UPDATE set three = 3; -- fails (due to SELECT) -ERROR: permission denied for table atest5 -INSERT INTO atest5(four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE set three = 3; -- fails (due to SELECT) -ERROR: permission denied for table atest5 -INSERT INTO atest5(four) VALUES (4); -- ok -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT SELECT (four) ON atest5 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -INSERT INTO atest5(four) VALUES (4) ON CONFLICT (four) DO UPDATE set three = 3; -- ok -INSERT INTO atest5(four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE set three = 3; -- ok -SET SESSION AUTHORIZATION regress_priv_user1; -REVOKE ALL (one) ON atest5 FROM regress_priv_user4; -GRANT SELECT (one,two,blue) ON atest6 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT one FROM atest5; -- fail -ERROR: permission denied for table atest5 -UPDATE atest5 SET one = 1; -- fail -ERROR: permission denied for table atest5 -SELECT atest6 FROM atest6; -- ok - atest6 --------- -(0 rows) - -COPY atest6 TO stdout; -- ok --- check error reporting with column privs -SET SESSION AUTHORIZATION regress_priv_user1; -CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2)); -GRANT SELECT (c1) ON t1 TO regress_priv_user2; -GRANT INSERT (c1, c2, c3) ON t1 TO regress_priv_user2; -GRANT UPDATE (c1, c2, c3) ON t1 TO regress_priv_user2; --- seed data -INSERT INTO t1 VALUES (1, 1, 1); -INSERT INTO t1 VALUES (1, 2, 1); -INSERT INTO t1 VALUES (2, 1, 2); -INSERT INTO t1 VALUES (2, 2, 2); -INSERT INTO t1 VALUES (3, 1, 3); -SET SESSION AUTHORIZATION regress_priv_user2; -INSERT INTO t1 (c1, c2) VALUES (1, 1); -- fail, but row not shown -ERROR: duplicate key value violates unique constraint "t1_pkey" -UPDATE t1 SET c2 = 1; -- fail, but row not shown -ERROR: duplicate key value violates unique constraint "t1_pkey" -INSERT INTO t1 (c1, c2) VALUES (null, null); -- fail, but see columns being inserted -ERROR: null value in column "c1" of relation "t1" violates not-null constraint -DETAIL: Failing row contains (c1, c2) = (null, null). -INSERT INTO t1 (c3) VALUES (null); -- fail, but see columns being inserted or have SELECT -ERROR: null value in column "c1" of relation "t1" violates not-null constraint -DETAIL: Failing row contains (c1, c3) = (null, null). -INSERT INTO t1 (c1) VALUES (5); -- fail, but see columns being inserted or have SELECT -ERROR: null value in column "c2" of relation "t1" violates not-null constraint -DETAIL: Failing row contains (c1) = (5). -UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified -ERROR: new row for relation "t1" violates check constraint "t1_c3_check" -DETAIL: Failing row contains (c1, c3) = (1, 10). -SET SESSION AUTHORIZATION regress_priv_user1; -DROP TABLE t1; --- check error reporting with column privs on a partitioned table -CREATE TABLE errtst(a text, b text NOT NULL, c text, secret1 text, secret2 text) PARTITION BY LIST (a); -CREATE TABLE errtst_part_1(secret2 text, c text, a text, b text NOT NULL, secret1 text); -CREATE TABLE errtst_part_2(secret1 text, secret2 text, a text, c text, b text NOT NULL); -ALTER TABLE errtst ATTACH PARTITION errtst_part_1 FOR VALUES IN ('aaa'); -ALTER TABLE errtst ATTACH PARTITION errtst_part_2 FOR VALUES IN ('aaaa'); -GRANT SELECT (a, b, c) ON TABLE errtst TO regress_priv_user2; -GRANT UPDATE (a, b, c) ON TABLE errtst TO regress_priv_user2; -GRANT INSERT (a, b, c) ON TABLE errtst TO regress_priv_user2; -INSERT INTO errtst_part_1 (a, b, c, secret1, secret2) -VALUES ('aaa', 'bbb', 'ccc', 'the body', 'is in the attic'); -SET SESSION AUTHORIZATION regress_priv_user2; --- Perform a few updates that violate the NOT NULL constraint. Make sure --- the error messages don't leak the secret fields. --- simple insert. -INSERT INTO errtst (a, b) VALUES ('aaa', NULL); -ERROR: null value in column "b" of relation "errtst_part_1" violates not-null constraint -DETAIL: Failing row contains (a, b, c) = (aaa, null, null). --- simple update. -UPDATE errtst SET b = NULL; -ERROR: null value in column "b" of relation "errtst_part_1" violates not-null constraint -DETAIL: Failing row contains (a, b, c) = (aaa, null, ccc). --- partitioning key is updated, doesn't move the row. -UPDATE errtst SET a = 'aaa', b = NULL; -ERROR: null value in column "b" of relation "errtst_part_1" violates not-null constraint -DETAIL: Failing row contains (a, b, c) = (aaa, null, ccc). --- row is moved to another partition. -UPDATE errtst SET a = 'aaaa', b = NULL; -ERROR: null value in column "b" of relation "errtst_part_2" violates not-null constraint -DETAIL: Failing row contains (a, b, c) = (aaaa, null, ccc). --- row is moved to another partition. This differs from the previous case in --- that the new partition is excluded by constraint exclusion, so its --- ResultRelInfo is not created at ExecInitModifyTable, but needs to be --- constructed on the fly when the updated tuple is routed to it. -UPDATE errtst SET a = 'aaaa', b = NULL WHERE a = 'aaa'; -ERROR: null value in column "b" of relation "errtst_part_2" violates not-null constraint -DETAIL: Failing row contains (a, b, c) = (aaaa, null, ccc). -SET SESSION AUTHORIZATION regress_priv_user1; -DROP TABLE errtst; --- test column-level privileges when involved with DELETE -SET SESSION AUTHORIZATION regress_priv_user1; -ALTER TABLE atest6 ADD COLUMN three integer; -GRANT DELETE ON atest5 TO regress_priv_user3; -GRANT SELECT (two) ON atest5 TO regress_priv_user3; -REVOKE ALL (one) ON atest5 FROM regress_priv_user3; -GRANT SELECT (one) ON atest5 TO regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT atest6 FROM atest6; -- fail -ERROR: permission denied for table atest6 -SELECT one FROM atest5 NATURAL JOIN atest6; -- fail -ERROR: permission denied for table atest5 -SET SESSION AUTHORIZATION regress_priv_user1; -ALTER TABLE atest6 DROP COLUMN three; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT atest6 FROM atest6; -- ok - atest6 --------- -(0 rows) - -SELECT one FROM atest5 NATURAL JOIN atest6; -- ok - one ------ -(0 rows) - -SET SESSION AUTHORIZATION regress_priv_user1; -ALTER TABLE atest6 DROP COLUMN two; -REVOKE SELECT (one,blue) ON atest6 FROM regress_priv_user4; -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT * FROM atest6; -- fail -ERROR: permission denied for table atest6 -SELECT 1 FROM atest6; -- fail -ERROR: permission denied for table atest6 -SET SESSION AUTHORIZATION regress_priv_user3; -DELETE FROM atest5 WHERE one = 1; -- fail -ERROR: permission denied for table atest5 -DELETE FROM atest5 WHERE two = 2; -- ok --- check inheritance cases -SET SESSION AUTHORIZATION regress_priv_user1; -CREATE TABLE atestp1 (f1 int, f2 int); -CREATE TABLE atestp2 (fx int, fy int); -CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2); -GRANT SELECT(fx,fy,tableoid) ON atestp2 TO regress_priv_user2; -GRANT SELECT(fx) ON atestc TO regress_priv_user2; -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT fx FROM atestp2; -- ok - fx ----- -(0 rows) - -SELECT fy FROM atestp2; -- ok - fy ----- -(0 rows) - -SELECT atestp2 FROM atestp2; -- ok - atestp2 ---------- -(0 rows) - -SELECT tableoid FROM atestp2; -- ok - tableoid ----------- -(0 rows) - -SELECT fy FROM atestc; -- fail -ERROR: permission denied for table atestc -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT SELECT(fy,tableoid) ON atestc TO regress_priv_user2; -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT fx FROM atestp2; -- still ok - fx ----- -(0 rows) - -SELECT fy FROM atestp2; -- ok - fy ----- -(0 rows) - -SELECT atestp2 FROM atestp2; -- ok - atestp2 ---------- -(0 rows) - -SELECT tableoid FROM atestp2; -- ok - tableoid ----------- -(0 rows) - --- child's permissions do not apply when operating on parent -SET SESSION AUTHORIZATION regress_priv_user1; -REVOKE ALL ON atestc FROM regress_priv_user2; -GRANT ALL ON atestp1 TO regress_priv_user2; -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT f2 FROM atestp1; -- ok - f2 ----- -(0 rows) - -SELECT f2 FROM atestc; -- fail -ERROR: permission denied for table atestc -DELETE FROM atestp1; -- ok -DELETE FROM atestc; -- fail -ERROR: permission denied for table atestc -UPDATE atestp1 SET f1 = 1; -- ok -UPDATE atestc SET f1 = 1; -- fail -ERROR: permission denied for table atestc -TRUNCATE atestp1; -- ok -TRUNCATE atestc; -- fail -ERROR: permission denied for table atestc -BEGIN; -LOCK atestp1; -END; -BEGIN; -LOCK atestc; -ERROR: permission denied for table atestc -END; --- privileges on functions, languages --- switch to superuser -\c - -REVOKE ALL PRIVILEGES ON LANGUAGE sql FROM PUBLIC; -GRANT USAGE ON LANGUAGE sql TO regress_priv_user1; -- ok -GRANT USAGE ON LANGUAGE c TO PUBLIC; -- fail -ERROR: language "c" is not trusted -DETAIL: GRANT and REVOKE are not allowed on untrusted languages, because only superusers can use untrusted languages. -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT USAGE ON LANGUAGE sql TO regress_priv_user2; -- fail -WARNING: no privileges were granted for "sql" -CREATE FUNCTION priv_testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -CREATE FUNCTION priv_testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; -CREATE AGGREGATE priv_testagg1(int) (sfunc = int4pl, stype = int4); -CREATE PROCEDURE priv_testproc1(int) AS 'select $1;' LANGUAGE sql; -REVOKE ALL ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) FROM PUBLIC; -GRANT EXECUTE ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) TO regress_priv_user2; -REVOKE ALL ON FUNCTION priv_testproc1(int) FROM PUBLIC; -- fail, not a function -ERROR: priv_testproc1(integer) is not a function -REVOKE ALL ON PROCEDURE priv_testproc1(int) FROM PUBLIC; -GRANT EXECUTE ON PROCEDURE priv_testproc1(int) TO regress_priv_user2; -GRANT USAGE ON FUNCTION priv_testfunc1(int) TO regress_priv_user3; -- semantic error -ERROR: invalid privilege type USAGE for function -GRANT USAGE ON FUNCTION priv_testagg1(int) TO regress_priv_user3; -- semantic error -ERROR: invalid privilege type USAGE for function -GRANT USAGE ON PROCEDURE priv_testproc1(int) TO regress_priv_user3; -- semantic error -ERROR: invalid privilege type USAGE for procedure -GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc1(int) TO regress_priv_user4; -GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc_nosuch(int) TO regress_priv_user4; -ERROR: function priv_testfunc_nosuch(integer) does not exist -GRANT ALL PRIVILEGES ON FUNCTION priv_testagg1(int) TO regress_priv_user4; -GRANT ALL PRIVILEGES ON PROCEDURE priv_testproc1(int) TO regress_priv_user4; -CREATE FUNCTION priv_testfunc4(boolean) RETURNS text - AS 'select col1 from atest2 where col2 = $1;' - LANGUAGE sql SECURITY DEFINER; -GRANT EXECUTE ON FUNCTION priv_testfunc4(boolean) TO regress_priv_user3; -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT priv_testfunc1(5), priv_testfunc2(5); -- ok - priv_testfunc1 | priv_testfunc2 -----------------+---------------- - 10 | 15 -(1 row) - -CREATE FUNCTION priv_testfunc3(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -- fail -ERROR: permission denied for language sql -SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- ok - priv_testagg1 ---------------- - 6 -(1 row) - -CALL priv_testproc1(6); -- ok -SET SESSION AUTHORIZATION regress_priv_user3; -SELECT priv_testfunc1(5); -- fail -ERROR: permission denied for function priv_testfunc1 -SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- fail -ERROR: permission denied for aggregate priv_testagg1 -CALL priv_testproc1(6); -- fail -ERROR: permission denied for procedure priv_testproc1 -SELECT col1 FROM atest2 WHERE col2 = true; -- fail -ERROR: permission denied for table atest2 -SELECT priv_testfunc4(true); -- ok - priv_testfunc4 ----------------- - bar -(1 row) - -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT priv_testfunc1(5); -- ok - priv_testfunc1 ----------------- - 10 -(1 row) - -SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- ok - priv_testagg1 ---------------- - 6 -(1 row) - -CALL priv_testproc1(6); -- ok -DROP FUNCTION priv_testfunc1(int); -- fail -ERROR: must be owner of function priv_testfunc1 -DROP AGGREGATE priv_testagg1(int); -- fail -ERROR: must be owner of aggregate priv_testagg1 -DROP PROCEDURE priv_testproc1(int); -- fail -ERROR: must be owner of procedure priv_testproc1 -\c - -DROP FUNCTION priv_testfunc1(int); -- ok --- restore to sanity -GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC; --- verify privilege checks on array-element coercions -BEGIN; -SELECT '{1}'::int4[]::int8[]; - int8 ------- - {1} -(1 row) - -REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC; -SELECT '{1}'::int4[]::int8[]; --superuser, succeed - int8 ------- - {1} -(1 row) - -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT '{1}'::int4[]::int8[]; --other user, fail -ERROR: permission denied for function int8 -ROLLBACK; --- privileges on types --- switch to superuser -\c - -CREATE TYPE priv_testtype1 AS (a int, b text); -REVOKE USAGE ON TYPE priv_testtype1 FROM PUBLIC; -GRANT USAGE ON TYPE priv_testtype1 TO regress_priv_user2; -GRANT USAGE ON TYPE _priv_testtype1 TO regress_priv_user2; -- fail -ERROR: cannot set privileges of array types -HINT: Set the privileges of the element type instead. -GRANT USAGE ON DOMAIN priv_testtype1 TO regress_priv_user2; -- fail -ERROR: "priv_testtype1" is not a domain -CREATE DOMAIN priv_testdomain1 AS int; -REVOKE USAGE on DOMAIN priv_testdomain1 FROM PUBLIC; -GRANT USAGE ON DOMAIN priv_testdomain1 TO regress_priv_user2; -GRANT USAGE ON TYPE priv_testdomain1 TO regress_priv_user2; -- ok -SET SESSION AUTHORIZATION regress_priv_user1; --- commands that should fail -CREATE AGGREGATE priv_testagg1a(priv_testdomain1) (sfunc = int4_sum, stype = bigint); -ERROR: permission denied for type priv_testdomain1 -CREATE DOMAIN priv_testdomain2a AS priv_testdomain1; -ERROR: permission denied for type priv_testdomain1 -CREATE DOMAIN priv_testdomain3a AS int; -CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3a AS $$ SELECT $1::priv_testdomain3a $$ LANGUAGE SQL; -CREATE CAST (priv_testdomain1 AS priv_testdomain3a) WITH FUNCTION castfunc(int); -ERROR: permission denied for type priv_testdomain1 -DROP FUNCTION castfunc(int) CASCADE; -DROP DOMAIN priv_testdomain3a; -CREATE FUNCTION priv_testfunc5a(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; -ERROR: permission denied for type priv_testdomain1 -CREATE FUNCTION priv_testfunc6a(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; -ERROR: permission denied for type priv_testdomain1 -CREATE OPERATOR !+! (PROCEDURE = int4pl, LEFTARG = priv_testdomain1, RIGHTARG = priv_testdomain1); -ERROR: permission denied for type priv_testdomain1 -CREATE TABLE test5a (a int, b priv_testdomain1); -ERROR: permission denied for type priv_testdomain1 -CREATE TABLE test6a OF priv_testtype1; -ERROR: permission denied for type priv_testtype1 -CREATE TABLE test10a (a int[], b priv_testtype1[]); -ERROR: permission denied for type priv_testtype1 -CREATE TABLE test9a (a int, b int); -ALTER TABLE test9a ADD COLUMN c priv_testdomain1; -ERROR: permission denied for type priv_testdomain1 -ALTER TABLE test9a ALTER COLUMN b TYPE priv_testdomain1; -ERROR: permission denied for type priv_testdomain1 -CREATE TYPE test7a AS (a int, b priv_testdomain1); -ERROR: permission denied for type priv_testdomain1 -CREATE TYPE test8a AS (a int, b int); -ALTER TYPE test8a ADD ATTRIBUTE c priv_testdomain1; -ERROR: permission denied for type priv_testdomain1 -ALTER TYPE test8a ALTER ATTRIBUTE b TYPE priv_testdomain1; -ERROR: permission denied for type priv_testdomain1 -CREATE TABLE test11a AS (SELECT 1::priv_testdomain1 AS a); -ERROR: permission denied for type priv_testdomain1 -REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; -ERROR: permission denied for type priv_testtype1 -SET SESSION AUTHORIZATION regress_priv_user2; --- commands that should succeed -CREATE AGGREGATE priv_testagg1b(priv_testdomain1) (sfunc = int4_sum, stype = bigint); -CREATE DOMAIN priv_testdomain2b AS priv_testdomain1; -CREATE DOMAIN priv_testdomain3b AS int; -CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3b AS $$ SELECT $1::priv_testdomain3b $$ LANGUAGE SQL; -CREATE CAST (priv_testdomain1 AS priv_testdomain3b) WITH FUNCTION castfunc(int); -WARNING: cast will be ignored because the source data type is a domain -CREATE FUNCTION priv_testfunc5b(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; -CREATE FUNCTION priv_testfunc6b(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; -CREATE OPERATOR !! (PROCEDURE = priv_testfunc5b, RIGHTARG = priv_testdomain1); -CREATE TABLE test5b (a int, b priv_testdomain1); -CREATE TABLE test6b OF priv_testtype1; -CREATE TABLE test10b (a int[], b priv_testtype1[]); -CREATE TABLE test9b (a int, b int); -ALTER TABLE test9b ADD COLUMN c priv_testdomain1; -ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; -CREATE TYPE test7b AS (a int, b priv_testdomain1); -CREATE TYPE test8b AS (a int, b int); -ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; -ALTER TYPE test8b ALTER ATTRIBUTE b TYPE priv_testdomain1; -CREATE TABLE test11b AS (SELECT 1::priv_testdomain1 AS a); -REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; -WARNING: no privileges could be revoked for "priv_testtype1" -\c - -DROP AGGREGATE priv_testagg1b(priv_testdomain1); -DROP DOMAIN priv_testdomain2b; -DROP OPERATOR !! (NONE, priv_testdomain1); -DROP FUNCTION priv_testfunc5b(a priv_testdomain1); -DROP FUNCTION priv_testfunc6b(b int); -DROP TABLE test5b; -DROP TABLE test6b; -DROP TABLE test9b; -DROP TABLE test10b; -DROP TYPE test7b; -DROP TYPE test8b; -DROP CAST (priv_testdomain1 AS priv_testdomain3b); -DROP FUNCTION castfunc(int) CASCADE; -DROP DOMAIN priv_testdomain3b; -DROP TABLE test11b; -DROP TYPE priv_testtype1; -- ok -DROP DOMAIN priv_testdomain1; -- ok --- truncate -SET SESSION AUTHORIZATION regress_priv_user5; -TRUNCATE atest2; -- ok -TRUNCATE atest3; -- fail -ERROR: permission denied for table atest3 --- has_table_privilege function --- bad-input checks -select has_table_privilege(NULL,'pg_authid','select'); - has_table_privilege ---------------------- - -(1 row) - -select has_table_privilege('pg_shad','select'); -ERROR: relation "pg_shad" does not exist -select has_table_privilege('nosuchuser','pg_authid','select'); -ERROR: role "nosuchuser" does not exist -select has_table_privilege('pg_authid','sel'); -ERROR: unrecognized privilege type: "sel" -select has_table_privilege(-999999,'pg_authid','update'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(1,'select'); - has_table_privilege ---------------------- - -(1 row) - --- superuser -\c - -select has_table_privilege(current_user,'pg_authid','select'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(current_user,'pg_authid','insert'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,'pg_authid','update') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,'pg_authid','delete') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - --- 'rule' privilege no longer exists, but for backwards compatibility --- has_table_privilege still recognizes the keyword and says FALSE -select has_table_privilege(current_user,t1.oid,'rule') -from (select oid from pg_class where relname = 'pg_authid') as t1; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(current_user,t1.oid,'references') -from (select oid from pg_class where relname = 'pg_authid') as t1; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'select') -from (select oid from pg_class where relname = 'pg_authid') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'insert') -from (select oid from pg_class where relname = 'pg_authid') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege('pg_authid','update'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege('pg_authid','delete'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege('pg_authid','truncate'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t1.oid,'select') -from (select oid from pg_class where relname = 'pg_authid') as t1; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t1.oid,'trigger') -from (select oid from pg_class where relname = 'pg_authid') as t1; - has_table_privilege ---------------------- - t -(1 row) - --- non-superuser -SET SESSION AUTHORIZATION regress_priv_user3; -select has_table_privilege(current_user,'pg_class','select'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(current_user,'pg_class','insert'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,'pg_class','update') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,'pg_class','delete') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(current_user,t1.oid,'references') -from (select oid from pg_class where relname = 'pg_class') as t1; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'select') -from (select oid from pg_class where relname = 'pg_class') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'insert') -from (select oid from pg_class where relname = 'pg_class') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('pg_class','update'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('pg_class','delete'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('pg_class','truncate'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t1.oid,'select') -from (select oid from pg_class where relname = 'pg_class') as t1; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t1.oid,'trigger') -from (select oid from pg_class where relname = 'pg_class') as t1; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(current_user,'atest1','select'); - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(current_user,'atest1','insert'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,'atest1','update') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,'atest1','delete') -from (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(current_user,t1.oid,'references') -from (select oid from pg_class where relname = 'atest1') as t1; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'select') -from (select oid from pg_class where relname = 'atest1') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t2.oid,t1.oid,'insert') -from (select oid from pg_class where relname = 'atest1') as t1, - (select oid from pg_roles where rolname = current_user) as t2; - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('atest1','update'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('atest1','delete'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege('atest1','truncate'); - has_table_privilege ---------------------- - f -(1 row) - -select has_table_privilege(t1.oid,'select') -from (select oid from pg_class where relname = 'atest1') as t1; - has_table_privilege ---------------------- - t -(1 row) - -select has_table_privilege(t1.oid,'trigger') -from (select oid from pg_class where relname = 'atest1') as t1; - has_table_privilege ---------------------- - f -(1 row) - --- has_column_privilege function --- bad-input checks (as non-super-user) -select has_column_privilege('pg_authid',NULL,'select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege('pg_authid','nosuchcol','select'); -ERROR: column "nosuchcol" of relation "pg_authid" does not exist -select has_column_privilege(9999,'nosuchcol','select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege(9999,99::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege('pg_authid',99::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege(9999,99::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -create temp table mytable(f1 int, f2 int, f3 int); -alter table mytable drop column f2; -select has_column_privilege('mytable','f2','select'); -ERROR: column "f2" of relation "mytable" does not exist -select has_column_privilege('mytable','........pg.dropped.2........','select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege('mytable',2::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege('mytable',99::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -revoke select on table mytable from regress_priv_user3; -select has_column_privilege('mytable',2::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -select has_column_privilege('mytable',99::int2,'select'); - has_column_privilege ----------------------- - -(1 row) - -drop table mytable; --- Grant options -SET SESSION AUTHORIZATION regress_priv_user1; -CREATE TABLE atest4 (a int); -GRANT SELECT ON atest4 TO regress_priv_user2 WITH GRANT OPTION; -GRANT UPDATE ON atest4 TO regress_priv_user2; -GRANT SELECT ON atest4 TO GROUP regress_priv_group1 WITH GRANT OPTION; -SET SESSION AUTHORIZATION regress_priv_user2; -GRANT SELECT ON atest4 TO regress_priv_user3; -GRANT UPDATE ON atest4 TO regress_priv_user3; -- fail -WARNING: no privileges were granted for "atest4" -SET SESSION AUTHORIZATION regress_priv_user1; -REVOKE SELECT ON atest4 FROM regress_priv_user3; -- does nothing -SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- true - has_table_privilege ---------------------- - t -(1 row) - -REVOKE SELECT ON atest4 FROM regress_priv_user2; -- fail -ERROR: dependent privileges exist -HINT: Use CASCADE to revoke them too. -REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regress_priv_user2 CASCADE; -- ok -SELECT has_table_privilege('regress_priv_user2', 'atest4', 'SELECT'); -- true - has_table_privilege ---------------------- - t -(1 row) - -SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- false - has_table_privilege ---------------------- - f -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true - has_table_privilege ---------------------- - t -(1 row) - --- security-restricted operations -\c - -CREATE ROLE regress_sro_user; -SET SESSION AUTHORIZATION regress_sro_user; -CREATE FUNCTION unwanted_grant() RETURNS void LANGUAGE sql AS - 'GRANT regress_priv_group2 TO regress_sro_user'; -CREATE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS - 'DECLARE c CURSOR WITH HOLD FOR SELECT unwanted_grant(); SELECT true'; --- REFRESH of this MV will queue a GRANT at end of transaction -CREATE MATERIALIZED VIEW sro_mv AS SELECT mv_action() WITH NO DATA; -REFRESH MATERIALIZED VIEW sro_mv; -ERROR: cannot create a cursor WITH HOLD within security-restricted operation -CONTEXT: SQL function "mv_action" statement 1 -\c - -REFRESH MATERIALIZED VIEW sro_mv; -ERROR: cannot create a cursor WITH HOLD within security-restricted operation -CONTEXT: SQL function "mv_action" statement 1 -SET SESSION AUTHORIZATION regress_sro_user; --- INSERT to this table will queue a GRANT at end of transaction -CREATE TABLE sro_trojan_table (); -CREATE FUNCTION sro_trojan() RETURNS trigger LANGUAGE plpgsql AS - 'BEGIN PERFORM unwanted_grant(); RETURN NULL; END'; -CREATE CONSTRAINT TRIGGER t AFTER INSERT ON sro_trojan_table - INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE sro_trojan(); --- Now, REFRESH will issue such an INSERT, queueing the GRANT -CREATE OR REPLACE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS - 'INSERT INTO sro_trojan_table DEFAULT VALUES; SELECT true'; -REFRESH MATERIALIZED VIEW sro_mv; -ERROR: cannot fire deferred trigger within security-restricted operation -CONTEXT: SQL function "mv_action" statement 1 -\c - -REFRESH MATERIALIZED VIEW sro_mv; -ERROR: cannot fire deferred trigger within security-restricted operation -CONTEXT: SQL function "mv_action" statement 1 -BEGIN; SET CONSTRAINTS ALL IMMEDIATE; REFRESH MATERIALIZED VIEW sro_mv; COMMIT; -ERROR: must have admin option on role "regress_priv_group2" -CONTEXT: SQL function "unwanted_grant" statement 1 -SQL statement "SELECT unwanted_grant()" -PL/pgSQL function sro_trojan() line 1 at PERFORM -SQL function "mv_action" statement 1 -DROP OWNED BY regress_sro_user; -DROP ROLE regress_sro_user; --- Admin options -SET SESSION AUTHORIZATION regress_priv_user4; -CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS - 'GRANT regress_priv_group2 TO regress_priv_user5'; -GRANT regress_priv_group2 TO regress_priv_user5; -- ok: had ADMIN OPTION -SET ROLE regress_priv_group2; -GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE suspended privilege -ERROR: must have admin option on role "regress_priv_group2" -SET SESSION AUTHORIZATION regress_priv_user1; -GRANT regress_priv_group2 TO regress_priv_user5; -- fails: no ADMIN OPTION -ERROR: must have admin option on role "regress_priv_group2" -SELECT dogrant_ok(); -- ok: SECURITY DEFINER conveys ADMIN -NOTICE: role "regress_priv_user5" is already a member of role "regress_priv_group2" - dogrant_ok ------------- - -(1 row) - -SET ROLE regress_priv_group2; -GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE did not help -ERROR: must have admin option on role "regress_priv_group2" -SET SESSION AUTHORIZATION regress_priv_group2; -GRANT regress_priv_group2 TO regress_priv_user5; -- ok: a role can self-admin -NOTICE: role "regress_priv_user5" is already a member of role "regress_priv_group2" -CREATE FUNCTION dogrant_fails() RETURNS void LANGUAGE sql SECURITY DEFINER AS - 'GRANT regress_priv_group2 TO regress_priv_user5'; -SELECT dogrant_fails(); -- fails: no self-admin in SECURITY DEFINER -ERROR: must have admin option on role "regress_priv_group2" -CONTEXT: SQL function "dogrant_fails" statement 1 -DROP FUNCTION dogrant_fails(); -SET SESSION AUTHORIZATION regress_priv_user4; -DROP FUNCTION dogrant_ok(); -REVOKE regress_priv_group2 FROM regress_priv_user5; --- has_sequence_privilege tests -\c - -CREATE SEQUENCE x_seq; -GRANT USAGE on x_seq to regress_priv_user2; -SELECT has_sequence_privilege('regress_priv_user1', 'atest1', 'SELECT'); -ERROR: "atest1" is not a sequence -SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'INSERT'); -ERROR: unrecognized privilege type: "INSERT" -SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'SELECT'); - has_sequence_privilege ------------------------- - f -(1 row) - -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT has_sequence_privilege('x_seq', 'USAGE'); - has_sequence_privilege ------------------------- - t -(1 row) - --- largeobject privilege tests -\c - -SET SESSION AUTHORIZATION regress_priv_user1; -SELECT lo_create(1001); - lo_create ------------ - 1001 -(1 row) - -SELECT lo_create(1002); - lo_create ------------ - 1002 -(1 row) - -SELECT lo_create(1003); - lo_create ------------ - 1003 -(1 row) - -SELECT lo_create(1004); - lo_create ------------ - 1004 -(1 row) - -SELECT lo_create(1005); - lo_create ------------ - 1005 -(1 row) - -GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; -GRANT SELECT ON LARGE OBJECT 1003 TO regress_priv_user2; -GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regress_priv_user2; -GRANT ALL ON LARGE OBJECT 1005 TO regress_priv_user2; -GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user2 WITH GRANT OPTION; -GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed -ERROR: invalid privilege type INSERT for large object -GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed -ERROR: role "nosuchuser" does not exist -GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed -ERROR: large object 999 does not exist -\c - -SET SESSION AUTHORIZATION regress_priv_user2; -SELECT lo_create(2001); - lo_create ------------ - 2001 -(1 row) - -SELECT lo_create(2002); - lo_create ------------ - 2002 -(1 row) - -SELECT loread(lo_open(1001, x'20000'::int), 32); -- allowed, for now - loread --------- - \x -(1 row) - -SELECT lowrite(lo_open(1001, x'40000'::int), 'abcd'); -- fail, wrong mode -ERROR: large object descriptor 0 was not opened for writing -SELECT loread(lo_open(1001, x'40000'::int), 32); - loread --------- - \x -(1 row) - -SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -ERROR: permission denied for large object 1002 -SELECT loread(lo_open(1003, x'40000'::int), 32); - loread --------- - \x -(1 row) - -SELECT loread(lo_open(1004, x'40000'::int), 32); - loread --------- - \x -(1 row) - -SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); - lowrite ---------- - 4 -(1 row) - -SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -ERROR: permission denied for large object 1002 -SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied -ERROR: permission denied for large object 1003 -SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); - lowrite ---------- - 4 -(1 row) - -GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user3; -GRANT UPDATE ON LARGE OBJECT 1006 TO regress_priv_user3; -- to be denied -ERROR: large object 1006 does not exist -REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; -GRANT ALL ON LARGE OBJECT 2001 TO regress_priv_user3; -SELECT lo_unlink(1001); -- to be denied -ERROR: must be owner of large object 1001 -SELECT lo_unlink(2002); - lo_unlink ------------ - 1 -(1 row) - -\c - --- confirm ACL setting -SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; - oid | ownername | lomacl -------+--------------------+------------------------------------------------------------------------------------------------------------------------------ - 1001 | regress_priv_user1 | {regress_priv_user1=rw/regress_priv_user1,=rw/regress_priv_user1} - 1002 | regress_priv_user1 | - 1003 | regress_priv_user1 | {regress_priv_user1=rw/regress_priv_user1,regress_priv_user2=r/regress_priv_user1} - 1004 | regress_priv_user1 | {regress_priv_user1=rw/regress_priv_user1,regress_priv_user2=rw/regress_priv_user1} - 1005 | regress_priv_user1 | {regress_priv_user1=rw/regress_priv_user1,regress_priv_user2=r*w/regress_priv_user1,regress_priv_user3=r/regress_priv_user2} - 2001 | regress_priv_user2 | {regress_priv_user2=rw/regress_priv_user2,regress_priv_user3=rw/regress_priv_user2} -(6 rows) - -SET SESSION AUTHORIZATION regress_priv_user3; -SELECT loread(lo_open(1001, x'40000'::int), 32); - loread ------------- - \x61626364 -(1 row) - -SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied -ERROR: permission denied for large object 1003 -SELECT loread(lo_open(1005, x'40000'::int), 32); - loread --------- - \x -(1 row) - -SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied -ERROR: permission denied for large object 1005 -SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); - lo_truncate -------------- - 0 -(1 row) - --- compatibility mode in largeobject permission -\c - -SET lo_compat_privileges = false; -- default setting -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -ERROR: permission denied for large object 1002 -SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -ERROR: permission denied for large object 1002 -SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied -ERROR: permission denied for large object 1002 -SELECT lo_put(1002, 1, 'abcd'); -- to be denied -ERROR: permission denied for large object 1002 -SELECT lo_unlink(1002); -- to be denied -ERROR: must be owner of large object 1002 -SELECT lo_export(1001, '/dev/null'); -- to be denied -ERROR: permission denied for function lo_export -SELECT lo_import('/dev/null'); -- to be denied -ERROR: permission denied for function lo_import -SELECT lo_import('/dev/null', 2003); -- to be denied -ERROR: permission denied for function lo_import -\c - -SET lo_compat_privileges = true; -- compatibility mode -SET SESSION AUTHORIZATION regress_priv_user4; -SELECT loread(lo_open(1002, x'40000'::int), 32); - loread --------- - \x -(1 row) - -SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); - lowrite ---------- - 4 -(1 row) - -SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); - lo_truncate -------------- - 0 -(1 row) - -SELECT lo_unlink(1002); - lo_unlink ------------ - 1 -(1 row) - -SELECT lo_export(1001, '/dev/null'); -- to be denied -ERROR: permission denied for function lo_export --- don't allow unpriv users to access pg_largeobject contents -\c - -SELECT * FROM pg_largeobject LIMIT 0; - loid | pageno | data -------+--------+------ -(0 rows) - -SET SESSION AUTHORIZATION regress_priv_user1; -SELECT * FROM pg_largeobject LIMIT 0; -- to be denied -ERROR: permission denied for table pg_largeobject --- test pg_database_owner -RESET SESSION AUTHORIZATION; -GRANT pg_database_owner TO regress_priv_user1; -ERROR: role "pg_database_owner" cannot have explicit members -GRANT regress_priv_user1 TO pg_database_owner; -ERROR: role "pg_database_owner" cannot be a member of any role -CREATE TABLE datdba_only (); -ALTER TABLE datdba_only OWNER TO pg_database_owner; -REVOKE DELETE ON datdba_only FROM pg_database_owner; -SELECT - pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv, - pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem, - pg_has_role('regress_priv_user1', 'pg_database_owner', - 'MEMBER WITH ADMIN OPTION') as admin; - priv | mem | admin -------+-----+------- - f | f | f -(1 row) - -BEGIN; -DO $$BEGIN EXECUTE format( - 'ALTER DATABASE %I OWNER TO regress_priv_group2', current_catalog); END$$; -SELECT - pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv, - pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem, - pg_has_role('regress_priv_user1', 'pg_database_owner', - 'MEMBER WITH ADMIN OPTION') as admin; - priv | mem | admin -------+-----+------- - t | t | f -(1 row) - -SET SESSION AUTHORIZATION regress_priv_user1; -TABLE information_schema.enabled_roles ORDER BY role_name COLLATE "C"; - role_name ---------------------- - pg_database_owner - regress_priv_group2 - regress_priv_user1 -(3 rows) - -TABLE information_schema.applicable_roles ORDER BY role_name COLLATE "C"; - grantee | role_name | is_grantable ----------------------+---------------------+-------------- - regress_priv_group2 | pg_database_owner | NO - regress_priv_user1 | regress_priv_group2 | NO -(2 rows) - -INSERT INTO datdba_only DEFAULT VALUES; -SAVEPOINT q; DELETE FROM datdba_only; ROLLBACK TO q; -ERROR: permission denied for table datdba_only -SET SESSION AUTHORIZATION regress_priv_user2; -TABLE information_schema.enabled_roles; - role_name --------------------- - regress_priv_user2 -(1 row) - -INSERT INTO datdba_only DEFAULT VALUES; -ERROR: permission denied for table datdba_only -ROLLBACK; --- test default ACLs -\c - -CREATE SCHEMA testns; -GRANT ALL ON SCHEMA testns TO regress_priv_user1; -CREATE TABLE testns.acltest1 (x int); -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no - has_table_privilege ---------------------- - f -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no - has_table_privilege ---------------------- - f -(1 row) - --- placeholder for test with duplicated schema and role names -ALTER DEFAULT PRIVILEGES IN SCHEMA testns,testns GRANT SELECT ON TABLES TO public,public; -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no - has_table_privilege ---------------------- - f -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no - has_table_privilege ---------------------- - f -(1 row) - -DROP TABLE testns.acltest1; -CREATE TABLE testns.acltest1 (x int); -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes - has_table_privilege ---------------------- - t -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no - has_table_privilege ---------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLES TO regress_priv_user1; -DROP TABLE testns.acltest1; -CREATE TABLE testns.acltest1 (x int); -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes - has_table_privilege ---------------------- - t -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- yes - has_table_privilege ---------------------- - t -(1 row) - -ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLES FROM regress_priv_user1; -DROP TABLE testns.acltest1; -CREATE TABLE testns.acltest1 (x int); -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes - has_table_privilege ---------------------- - t -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no - has_table_privilege ---------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; -ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_priv_user2; -- error -ERROR: cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS --- --- Testing blanket default grants is very hazardous since it might change --- the privileges attached to objects created by concurrent regression tests. --- To avoid that, be sure to revoke the privileges again before committing. --- -BEGIN; -ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_priv_user2; -CREATE SCHEMA testns2; -SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'USAGE'); -- yes - has_schema_privilege ----------------------- - t -(1 row) - -SELECT has_schema_privilege('regress_priv_user6', 'testns2', 'USAGE'); -- yes - has_schema_privilege ----------------------- - t -(1 row) - -SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'CREATE'); -- no - has_schema_privilege ----------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_priv_user2; -CREATE SCHEMA testns3; -SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'USAGE'); -- no - has_schema_privilege ----------------------- - f -(1 row) - -SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'CREATE'); -- no - has_schema_privilege ----------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_priv_user2; -CREATE SCHEMA testns4; -SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'USAGE'); -- yes - has_schema_privilege ----------------------- - t -(1 row) - -SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'CREATE'); -- yes - has_schema_privilege ----------------------- - t -(1 row) - -ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_priv_user2; -COMMIT; --- Test for DROP OWNED BY with shared dependencies. This is done in a --- separate, rollbacked, transaction to avoid any trouble with other --- regression sessions. -BEGIN; -ALTER DEFAULT PRIVILEGES GRANT ALL ON FUNCTIONS TO regress_priv_user2; -ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_priv_user2; -ALTER DEFAULT PRIVILEGES GRANT ALL ON SEQUENCES TO regress_priv_user2; -ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO regress_priv_user2; -ALTER DEFAULT PRIVILEGES GRANT ALL ON TYPES TO regress_priv_user2; -SELECT count(*) FROM pg_shdepend - WHERE deptype = 'a' AND - refobjid = 'regress_priv_user2'::regrole AND - classid = 'pg_default_acl'::regclass; - count -------- - 5 -(1 row) - -DROP OWNED BY regress_priv_user2, regress_priv_user2; -SELECT count(*) FROM pg_shdepend - WHERE deptype = 'a' AND - refobjid = 'regress_priv_user2'::regrole AND - classid = 'pg_default_acl'::regclass; - count -------- - 0 -(1 row) - -ROLLBACK; -CREATE SCHEMA testns5; -SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'USAGE'); -- no - has_schema_privilege ----------------------- - f -(1 row) - -SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'CREATE'); -- no - has_schema_privilege ----------------------- - f -(1 row) - -SET ROLE regress_priv_user1; -CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; -CREATE AGGREGATE testns.agg1(int) (sfunc = int4pl, stype = int4); -CREATE PROCEDURE testns.bar() AS 'select 1' LANGUAGE sql; -SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- no - has_function_privilege ------------------------- - f -(1 row) - -SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- no - has_function_privilege ------------------------- - f -(1 row) - -SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- no - has_function_privilege ------------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON ROUTINES to public; -DROP FUNCTION testns.foo(); -CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; -DROP AGGREGATE testns.agg1(int); -CREATE AGGREGATE testns.agg1(int) (sfunc = int4pl, stype = int4); -DROP PROCEDURE testns.bar(); -CREATE PROCEDURE testns.bar() AS 'select 1' LANGUAGE sql; -SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- yes - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- yes - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- yes (counts as function here) - has_function_privilege ------------------------- - t -(1 row) - -DROP FUNCTION testns.foo(); -DROP AGGREGATE testns.agg1(int); -DROP PROCEDURE testns.bar(); -ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE USAGE ON TYPES FROM public; -CREATE DOMAIN testns.priv_testdomain1 AS int; -SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- no - has_type_privilege --------------------- - f -(1 row) - -ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON TYPES to public; -DROP DOMAIN testns.priv_testdomain1; -CREATE DOMAIN testns.priv_testdomain1 AS int; -SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- yes - has_type_privilege --------------------- - t -(1 row) - -DROP DOMAIN testns.priv_testdomain1; -RESET ROLE; -SELECT count(*) - FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid - WHERE nspname = 'testns'; - count -------- - 3 -(1 row) - -DROP SCHEMA testns CASCADE; -NOTICE: drop cascades to table testns.acltest1 -DROP SCHEMA testns2 CASCADE; -DROP SCHEMA testns3 CASCADE; -DROP SCHEMA testns4 CASCADE; -DROP SCHEMA testns5 CASCADE; -SELECT d.* -- check that entries went away - FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid - WHERE nspname IS NULL AND defaclnamespace != 0; - oid | defaclrole | defaclnamespace | defaclobjtype | defaclacl ------+------------+-----------------+---------------+----------- -(0 rows) - --- Grant on all objects of given type in a schema -\c - -CREATE SCHEMA testns; -CREATE TABLE testns.t1 (f1 int); -CREATE TABLE testns.t2 (f1 int); -SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false - has_table_privilege ---------------------- - f -(1 row) - -GRANT ALL ON ALL TABLES IN SCHEMA testns TO regress_priv_user1; -SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- true - has_table_privilege ---------------------- - t -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- true - has_table_privilege ---------------------- - t -(1 row) - -REVOKE ALL ON ALL TABLES IN SCHEMA testns FROM regress_priv_user1; -SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false - has_table_privilege ---------------------- - f -(1 row) - -SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- false - has_table_privilege ---------------------- - f -(1 row) - -CREATE FUNCTION testns.priv_testfunc(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; -CREATE AGGREGATE testns.priv_testagg(int) (sfunc = int4pl, stype = int4); -CREATE PROCEDURE testns.priv_testproc(int) AS 'select 3' LANGUAGE sql; -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true by default - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true by default - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true by default - has_function_privilege ------------------------- - t -(1 row) - -REVOKE ALL ON ALL FUNCTIONS IN SCHEMA testns FROM PUBLIC; -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- false - has_function_privilege ------------------------- - f -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- false - has_function_privilege ------------------------- - f -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- still true, not a function - has_function_privilege ------------------------- - t -(1 row) - -REVOKE ALL ON ALL PROCEDURES IN SCHEMA testns FROM PUBLIC; -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- now false - has_function_privilege ------------------------- - f -(1 row) - -GRANT ALL ON ALL ROUTINES IN SCHEMA testns TO PUBLIC; -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true - has_function_privilege ------------------------- - t -(1 row) - -SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true - has_function_privilege ------------------------- - t -(1 row) - -DROP SCHEMA testns CASCADE; -NOTICE: drop cascades to 5 other objects -DETAIL: drop cascades to table testns.t1 -drop cascades to table testns.t2 -drop cascades to function testns.priv_testfunc(integer) -drop cascades to function testns.priv_testagg(integer) -drop cascades to function testns.priv_testproc(integer) --- Change owner of the schema & and rename of new schema owner -\c - -CREATE ROLE regress_schemauser1 superuser login; -CREATE ROLE regress_schemauser2 superuser login; -SET SESSION ROLE regress_schemauser1; -CREATE SCHEMA testns; -SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; - nspname | rolname ----------+--------------------- - testns | regress_schemauser1 -(1 row) - -ALTER SCHEMA testns OWNER TO regress_schemauser2; -ALTER ROLE regress_schemauser2 RENAME TO regress_schemauser_renamed; -SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; - nspname | rolname ----------+---------------------------- - testns | regress_schemauser_renamed -(1 row) - -set session role regress_schemauser_renamed; -DROP SCHEMA testns CASCADE; --- clean up -\c - -DROP ROLE regress_schemauser1; -DROP ROLE regress_schemauser_renamed; --- test that dependent privileges are revoked (or not) properly -\c - -set session role regress_priv_user1; -create table dep_priv_test (a int); -grant select on dep_priv_test to regress_priv_user2 with grant option; -grant select on dep_priv_test to regress_priv_user3 with grant option; -set session role regress_priv_user2; -grant select on dep_priv_test to regress_priv_user4 with grant option; -set session role regress_priv_user3; -grant select on dep_priv_test to regress_priv_user4 with grant option; -set session role regress_priv_user4; -grant select on dep_priv_test to regress_priv_user5; -\dp dep_priv_test - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------+---------------+-------+-----------------------------------------------+-------------------+---------- - public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| | - | | | regress_priv_user2=r*/regress_priv_user1 +| | - | | | regress_priv_user3=r*/regress_priv_user1 +| | - | | | regress_priv_user4=r*/regress_priv_user2 +| | - | | | regress_priv_user4=r*/regress_priv_user3 +| | - | | | regress_priv_user5=r/regress_priv_user4 | | -(1 row) - -set session role regress_priv_user2; -revoke select on dep_priv_test from regress_priv_user4 cascade; -\dp dep_priv_test - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------+---------------+-------+-----------------------------------------------+-------------------+---------- - public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| | - | | | regress_priv_user2=r*/regress_priv_user1 +| | - | | | regress_priv_user3=r*/regress_priv_user1 +| | - | | | regress_priv_user4=r*/regress_priv_user3 +| | - | | | regress_priv_user5=r/regress_priv_user4 | | -(1 row) - -set session role regress_priv_user3; -revoke select on dep_priv_test from regress_priv_user4 cascade; -\dp dep_priv_test - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------+---------------+-------+-----------------------------------------------+-------------------+---------- - public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| | - | | | regress_priv_user2=r*/regress_priv_user1 +| | - | | | regress_priv_user3=r*/regress_priv_user1 | | -(1 row) - -set session role regress_priv_user1; -drop table dep_priv_test; --- clean up -\c -drop sequence x_seq; -DROP AGGREGATE priv_testagg1(int); -DROP FUNCTION priv_testfunc2(int); -DROP FUNCTION priv_testfunc4(boolean); -DROP PROCEDURE priv_testproc1(int); -DROP VIEW atestv0; -DROP VIEW atestv1; -DROP VIEW atestv2; --- this should cascade to drop atestv4 -DROP VIEW atestv3 CASCADE; -NOTICE: drop cascades to view atestv4 --- this should complain "does not exist" -DROP VIEW atestv4; -ERROR: view "atestv4" does not exist -DROP TABLE atest1; -DROP TABLE atest2; -DROP TABLE atest3; -DROP TABLE atest4; -DROP TABLE atest5; -DROP TABLE atest6; -DROP TABLE atestc; -DROP TABLE atestp1; -DROP TABLE atestp2; -SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; - lo_unlink ------------ - 1 - 1 - 1 - 1 - 1 -(5 rows) - -DROP GROUP regress_priv_group1; -DROP GROUP regress_priv_group2; --- these are needed to clean up permissions -REVOKE USAGE ON LANGUAGE sql FROM regress_priv_user1; -DROP OWNED BY regress_priv_user1; -DROP USER regress_priv_user1; -DROP USER regress_priv_user2; -DROP USER regress_priv_user3; -DROP USER regress_priv_user4; -DROP USER regress_priv_user5; -DROP USER regress_priv_user6; -DROP USER regress_priv_user7; -DROP USER regress_priv_user8; -- does not exist -ERROR: role "regress_priv_user8" does not exist --- permissions with LOCK TABLE -CREATE USER regress_locktable_user; -CREATE TABLE lock_table (a int); --- LOCK TABLE and SELECT permission -GRANT SELECT ON lock_table TO regress_locktable_user; -SET SESSION AUTHORIZATION regress_locktable_user; -BEGIN; -LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -BEGIN; -LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should pass -COMMIT; -BEGIN; -LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -\c -REVOKE SELECT ON lock_table FROM regress_locktable_user; --- LOCK TABLE and INSERT permission -GRANT INSERT ON lock_table TO regress_locktable_user; -SET SESSION AUTHORIZATION regress_locktable_user; -BEGIN; -LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass -COMMIT; -BEGIN; -LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -BEGIN; -LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -\c -REVOKE INSERT ON lock_table FROM regress_locktable_user; --- LOCK TABLE and UPDATE permission -GRANT UPDATE ON lock_table TO regress_locktable_user; -SET SESSION AUTHORIZATION regress_locktable_user; -BEGIN; -LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass -COMMIT; -BEGIN; -LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -BEGIN; -LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass -COMMIT; -\c -REVOKE UPDATE ON lock_table FROM regress_locktable_user; --- LOCK TABLE and DELETE permission -GRANT DELETE ON lock_table TO regress_locktable_user; -SET SESSION AUTHORIZATION regress_locktable_user; -BEGIN; -LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass -COMMIT; -BEGIN; -LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -BEGIN; -LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass -COMMIT; -\c -REVOKE DELETE ON lock_table FROM regress_locktable_user; --- LOCK TABLE and TRUNCATE permission -GRANT TRUNCATE ON lock_table TO regress_locktable_user; -SET SESSION AUTHORIZATION regress_locktable_user; -BEGIN; -LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass -COMMIT; -BEGIN; -LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail -ERROR: permission denied for table lock_table -ROLLBACK; -BEGIN; -LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass -COMMIT; -\c -REVOKE TRUNCATE ON lock_table FROM regress_locktable_user; --- clean up -DROP TABLE lock_table; -DROP USER regress_locktable_user; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/init_privs.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/init_privs.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/init_privs.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/init_privs.out 2021-10-30 22:04:14 -0600 @@ -1,12 +1,2 @@ --- Test initial privileges --- There should always be some initial privileges, set up by initdb -SELECT count(*) > 0 FROM pg_init_privs; - ?column? ----------- - t -(1 row) - --- Intentionally include some non-initial privs for pg_dump to dump out -GRANT SELECT ON pg_proc TO CURRENT_USER; -GRANT SELECT (prosrc) ON pg_proc TO CURRENT_USER; -GRANT SELECT (rolname, rolsuper) ON pg_authid TO CURRENT_USER; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/security_label.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/security_label.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/security_label.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/security_label.out 2021-10-30 22:04:14 -0600 @@ -1,44 +1,2 @@ --- --- Test for facilities of security label --- --- initial setups -SET client_min_messages TO 'warning'; -DROP ROLE IF EXISTS regress_seclabel_user1; -DROP ROLE IF EXISTS regress_seclabel_user2; -RESET client_min_messages; -CREATE USER regress_seclabel_user1 WITH CREATEROLE; -CREATE USER regress_seclabel_user2; -CREATE TABLE seclabel_tbl1 (a int, b text); -CREATE TABLE seclabel_tbl2 (x int, y text); -CREATE VIEW seclabel_view1 AS SELECT * FROM seclabel_tbl2; -CREATE FUNCTION seclabel_four() RETURNS integer AS $$SELECT 4$$ language sql; -CREATE DOMAIN seclabel_domain AS text; -ALTER TABLE seclabel_tbl1 OWNER TO regress_seclabel_user1; -ALTER TABLE seclabel_tbl2 OWNER TO regress_seclabel_user2; --- --- Test of SECURITY LABEL statement without a plugin --- -SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- fail -ERROR: no security label providers have been loaded -SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail -ERROR: security label provider "dummy" is not loaded -SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail -ERROR: no security label providers have been loaded -SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail -ERROR: no security label providers have been loaded -SECURITY LABEL ON ROLE regress_seclabel_user1 IS 'classified'; -- fail -ERROR: no security label providers have been loaded -SECURITY LABEL FOR 'dummy' ON ROLE regress_seclabel_user1 IS 'classified'; -- fail -ERROR: security label provider "dummy" is not loaded -SECURITY LABEL ON ROLE regress_seclabel_user1 IS '...invalid label...'; -- fail -ERROR: no security label providers have been loaded -SECURITY LABEL ON ROLE regress_seclabel_user3 IS 'unclassified'; -- fail -ERROR: no security label providers have been loaded --- clean up objects -DROP FUNCTION seclabel_four(); -DROP DOMAIN seclabel_domain; -DROP VIEW seclabel_view1; -DROP TABLE seclabel_tbl1; -DROP TABLE seclabel_tbl2; -DROP USER regress_seclabel_user1; -DROP USER regress_seclabel_user2; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.out 2021-10-30 22:04:14 -0600 @@ -1,776 +1,2 @@ -/* - * This test is intended to pass on all platforms supported by Postgres. - * We can therefore only assume that the default, C, and POSIX collations - * are available --- and since the regression tests are often run in a - * C-locale database, these may well all have the same behavior. But - * fortunately, the system doesn't know that and will treat them as - * incompatible collations. It is therefore at least possible to test - * parser behaviors such as collation conflict resolution. This test will, - * however, be more revealing when run in a database with non-C locale, - * since any departure from C sorting behavior will show as a failure. - */ -CREATE SCHEMA collate_tests; -SET search_path = collate_tests; -CREATE TABLE collate_test1 ( - a int, - b text COLLATE "C" NOT NULL -); -\d collate_test1 - Table "collate_tests.collate_test1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | C | not null | - -CREATE TABLE collate_test_fail ( - a int COLLATE "C", - b text -); -ERROR: collations are not supported by type integer -LINE 2: a int COLLATE "C", - ^ -CREATE TABLE collate_test_like ( - LIKE collate_test1 -); -\d collate_test_like - Table "collate_tests.collate_test_like" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | C | not null | - -CREATE TABLE collate_test2 ( - a int, - b text COLLATE "POSIX" -); -INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'Abc'), (3, 'bbc'), (4, 'ABD'); -INSERT INTO collate_test2 SELECT * FROM collate_test1; -SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc'; - a | b ----+----- - 1 | abc - 3 | bbc -(2 rows) - -SELECT * FROM collate_test1 WHERE b >= 'abc' COLLATE "C"; - a | b ----+----- - 1 | abc - 3 | bbc -(2 rows) - -SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc' COLLATE "C"; - a | b ----+----- - 1 | abc - 3 | bbc -(2 rows) - -SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "POSIX"; -- fail -ERROR: collation mismatch between explicit collations "C" and "POSIX" -LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P... - ^ -CREATE DOMAIN testdomain_p AS text COLLATE "POSIX"; -CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail -ERROR: collations are not supported by type integer -CREATE TABLE collate_test4 ( - a int, - b testdomain_p -); -INSERT INTO collate_test4 SELECT * FROM collate_test1; -SELECT a, b FROM collate_test4 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -CREATE TABLE collate_test5 ( - a int, - b testdomain_p COLLATE "C" -); -INSERT INTO collate_test5 SELECT * FROM collate_test1; -SELECT a, b FROM collate_test5 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b FROM collate_test1 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b FROM collate_test2 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - --- star expansion -SELECT * FROM collate_test1 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT * FROM collate_test2 ORDER BY b; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - --- constant expression folding -SELECT 'bbc' COLLATE "C" > 'Abc' COLLATE "C" AS "true"; - true ------- - t -(1 row) - -SELECT 'bbc' COLLATE "POSIX" < 'Abc' COLLATE "POSIX" AS "false"; - false -------- - f -(1 row) - --- upper/lower -CREATE TABLE collate_test10 ( - a int, - x text COLLATE "C", - y text COLLATE "POSIX" -); -INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); -SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; - a | lower | lower | upper | upper | initcap | initcap ----+-------+-------+-------+-------+---------+--------- - 1 | hij | hij | HIJ | HIJ | Hij | Hij - 2 | hij | hij | HIJ | HIJ | Hij | Hij -(2 rows) - -SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; - a | lower | lower ----+-------+------- - 1 | hij | hij - 2 | hij | hij -(2 rows) - -SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; - a | x | y ----+-----+----- - 1 | hij | hij - 2 | HIJ | HIJ -(2 rows) - --- backwards parsing -CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; -CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; -SELECT table_name, view_definition FROM information_schema.views - WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ - | FROM collate_test10; -(3 rows) - --- collation propagation in various expression types -SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; - a | coalesce ----+---------- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; - a | coalesce ----+---------- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; - a | lower | lower ----+-------+------- - 1 | hij | hij - 2 | hij | hij -(2 rows) - -SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; - a | b | greatest ----+-----+---------- - 2 | Abc | CCC - 4 | ABD | CCC - 1 | abc | abc - 3 | bbc | bbc -(4 rows) - -SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; - a | b | greatest ----+-----+---------- - 2 | Abc | CCC - 4 | ABD | CCC - 1 | abc | abc - 3 | bbc | bbc -(4 rows) - -SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; - a | x | y | lower | lower ----+-----+-----+-------+------- - 1 | hij | hij | hij | hij - 2 | HIJ | HIJ | foo | foo -(2 rows) - -SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; - a | nullif ----+-------- - 4 | ABD - 2 | Abc - 3 | bbc - 1 | -(4 rows) - -SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; - a | nullif ----+-------- - 4 | ABD - 2 | Abc - 3 | bbc - 1 | -(4 rows) - -SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; - a | lower | lower ----+-------+------- - 1 | hij | hij - 2 | hij | hij -(2 rows) - -SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; - a | b ----+------ - 4 | ABD - 2 | Abc - 1 | abcd - 3 | bbc -(4 rows) - -SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; - a | b ----+------ - 4 | ABD - 2 | Abc - 1 | abcd - 3 | bbc -(4 rows) - -CREATE DOMAIN testdomain AS text; -SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b::testdomain_p FROM collate_test2 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; - a | lower | lower ----+-------+------- - 1 | hij | hij - 2 | hij | hij -(2 rows) - -SELECT min(b), max(b) FROM collate_test1; - min | max ------+----- - ABD | bbc -(1 row) - -SELECT min(b), max(b) FROM collate_test2; - min | max ------+----- - ABD | bbc -(1 row) - -SELECT array_agg(b ORDER BY b) FROM collate_test1; - array_agg -------------------- - {ABD,Abc,abc,bbc} -(1 row) - -SELECT array_agg(b ORDER BY b) FROM collate_test2; - array_agg -------------------- - {ABD,Abc,abc,bbc} -(1 row) - --- In aggregates, ORDER BY expressions don't affect aggregate's collation -SELECT string_agg(x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; -- fail -ERROR: collation mismatch between explicit collations "C" and "POSIX" -LINE 1: SELECT string_agg(x COLLATE "C", y COLLATE "POSIX") FROM col... - ^ -SELECT array_agg(x COLLATE "C" ORDER BY y COLLATE "POSIX") FROM collate_test10; - array_agg ------------ - {HIJ,hij} -(1 row) - -SELECT array_agg(a ORDER BY x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; - array_agg ------------ - {2,1} -(1 row) - -SELECT array_agg(a ORDER BY x||y) FROM collate_test10; -- fail -ERROR: collation mismatch between implicit collations "C" and "POSIX" -LINE 1: SELECT array_agg(a ORDER BY x||y) FROM collate_test10; - ^ -HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. -SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; - a | b ----+----- - 4 | ABD - 4 | ABD - 2 | Abc - 2 | Abc - 1 | abc - 1 | abc - 3 | bbc - 3 | bbc -(8 rows) - -SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b FROM collate_test2 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test2 WHERE a > 1 ORDER BY 2; - a | b ----+----- - 2 | Abc - 3 | bbc -(2 rows) - -SELECT a, b FROM collate_test2 EXCEPT SELECT a, b FROM collate_test2 WHERE a < 2 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 3 | bbc -(3 rows) - -SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2 ORDER BY 2; -- fail -ERROR: could not determine which collation to use for string comparison -HINT: Use the COLLATE clause to set the collation explicitly. -SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- ok - a | b ----+----- - 1 | abc - 2 | Abc - 3 | bbc - 4 | ABD - 1 | abc - 2 | Abc - 3 | bbc - 4 | ABD -(8 rows) - -SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "C" and "POSIX" -LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... - ^ -HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. -SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- ok - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "C" and "POSIX" -LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... - ^ -HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. -SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "C" and "POSIX" -LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... - ^ -HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. -CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- fail -ERROR: no collation was derived for column "b" with collatable type text -HINT: Use the COLLATE clause to set the collation explicitly. --- ideally this would be a parse-time error, but for now it must be run-time: -select x < y from collate_test10; -- fail -ERROR: could not determine which collation to use for string comparison -HINT: Use the COLLATE clause to set the collation explicitly. -select x || y from collate_test10; -- ok, because || is not collation aware - ?column? ----------- - hijhij - HIJHIJ -(2 rows) - -select x, y from collate_test10 order by x || y; -- not so ok -ERROR: collation mismatch between implicit collations "C" and "POSIX" -LINE 1: select x, y from collate_test10 order by x || y; - ^ -HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. --- collation mismatch between recursive and non-recursive term -WITH RECURSIVE foo(x) AS - (SELECT x FROM (VALUES('a' COLLATE "C"),('b')) t(x) - UNION ALL - SELECT (x || 'c') COLLATE "POSIX" FROM foo WHERE length(x) < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has collation "C" in non-recursive term but collation "POSIX" overall -LINE 2: (SELECT x FROM (VALUES('a' COLLATE "C"),('b')) t(x) - ^ -HINT: Use the COLLATE clause to set the collation of the non-recursive term. -SELECT a, b, a < b as lt FROM - (VALUES ('a', 'B'), ('A', 'b' COLLATE "C")) v(a,b); - a | b | lt ----+---+---- - a | B | f - A | b | t -(2 rows) - --- collation mismatch in subselects -SELECT * FROM collate_test10 WHERE (x, y) NOT IN (SELECT y, x FROM collate_test10); -ERROR: could not determine which collation to use for string hashing -HINT: Use the COLLATE clause to set the collation explicitly. --- now it works with overrides -SELECT * FROM collate_test10 WHERE (x COLLATE "POSIX", y COLLATE "C") NOT IN (SELECT y, x FROM collate_test10); - a | x | y ----+---+--- -(0 rows) - -SELECT * FROM collate_test10 WHERE (x, y) NOT IN (SELECT y COLLATE "C", x COLLATE "POSIX" FROM collate_test10); - a | x | y ----+---+--- -(0 rows) - --- casting -SELECT CAST('42' AS text COLLATE "C"); -ERROR: syntax error at or near "COLLATE" -LINE 1: SELECT CAST('42' AS text COLLATE "C"); - ^ -SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; - a | b ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - --- result of a SQL function -CREATE FUNCTION vc (text) RETURNS text LANGUAGE sql - AS 'select $1::varchar'; -SELECT a, b FROM collate_test1 ORDER BY a, vc(b); - a | b ----+----- - 1 | abc - 2 | Abc - 3 | bbc - 4 | ABD -(4 rows) - --- polymorphism -SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; - unnest --------- - ABD - Abc - abc - bbc -(4 rows) - -SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; - unnest --------- - ABD - Abc - abc - bbc -(4 rows) - -CREATE FUNCTION dup (anyelement) RETURNS anyelement - AS 'select $1' LANGUAGE sql; -SELECT a, dup(b) FROM collate_test1 ORDER BY 2; - a | dup ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - -SELECT a, dup(b) FROM collate_test2 ORDER BY 2; - a | dup ----+----- - 4 | ABD - 2 | Abc - 1 | abc - 3 | bbc -(4 rows) - --- indexes -CREATE INDEX collate_test1_idx1 ON collate_test1 (b); -CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "POSIX"); -CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "POSIX")); -- this is different grammatically -CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); -CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "POSIX"); -- fail -ERROR: collations are not supported by type integer -CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "POSIX")); -- fail -ERROR: collations are not supported by type integer -LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "P... - ^ -SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; - relname | pg_get_indexdef ---------------------+------------------------------------------------------------------------------------------------------------------- - collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) - collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "POSIX") - collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "POSIX") - collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") -(4 rows) - --- foreign keys --- force indexes and mergejoins to be used for FK checking queries, --- else they might not exercise collation-dependent operators -SET enable_seqscan TO 0; -SET enable_hashjoin TO 0; -SET enable_nestloop TO 0; -CREATE TABLE collate_test20 (f1 text COLLATE "C" PRIMARY KEY); -INSERT INTO collate_test20 VALUES ('foo'), ('bar'); -CREATE TABLE collate_test21 (f2 text COLLATE "POSIX" REFERENCES collate_test20); -INSERT INTO collate_test21 VALUES ('foo'), ('bar'); -INSERT INTO collate_test21 VALUES ('baz'); -- fail -ERROR: insert or update on table "collate_test21" violates foreign key constraint "collate_test21_f2_fkey" -DETAIL: Key (f2)=(baz) is not present in table "collate_test20". -CREATE TABLE collate_test22 (f2 text COLLATE "POSIX"); -INSERT INTO collate_test22 VALUES ('foo'), ('bar'), ('baz'); -ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; -- fail -ERROR: insert or update on table "collate_test22" violates foreign key constraint "collate_test22_f2_fkey" -DETAIL: Key (f2)=(baz) is not present in table "collate_test20". -DELETE FROM collate_test22 WHERE f2 = 'baz'; -ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; -RESET enable_seqscan; -RESET enable_hashjoin; -RESET enable_nestloop; --- EXPLAIN -EXPLAIN (COSTS OFF) - SELECT * FROM collate_test10 ORDER BY x, y; - QUERY PLAN ----------------------------------------------- - Sort - Sort Key: x COLLATE "C", y COLLATE "POSIX" - -> Seq Scan on collate_test10 -(3 rows) - -EXPLAIN (COSTS OFF) - SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: x COLLATE "C" DESC, y COLLATE "C" NULLS FIRST - -> Seq Scan on collate_test10 -(3 rows) - --- CREATE/DROP COLLATION -CREATE COLLATION mycoll1 FROM "C"; -CREATE COLLATION mycoll2 ( LC_COLLATE = "POSIX", LC_CTYPE = "POSIX" ); -CREATE COLLATION mycoll3 FROM "default"; -- intentionally unsupported -ERROR: collation "default" cannot be copied -DROP COLLATION mycoll1; -CREATE TABLE collate_test23 (f1 text collate mycoll2); -DROP COLLATION mycoll2; -- fail -ERROR: cannot drop collation mycoll2 because other objects depend on it -DETAIL: column f1 of table collate_test23 depends on collation mycoll2 -HINT: Use DROP ... CASCADE to drop the dependent objects too. --- invalid: non-lowercase quoted identifiers -CREATE COLLATION case_coll ("Lc_Collate" = "POSIX", "Lc_Ctype" = "POSIX"); -ERROR: collation attribute "Lc_Collate" not recognized -LINE 1: CREATE COLLATION case_coll ("Lc_Collate" = "POSIX", "Lc_Ctyp... - ^ --- 9.1 bug with useless COLLATE in an expression subject to length coercion -CREATE TEMP TABLE vctable (f1 varchar(25)); -INSERT INTO vctable VALUES ('foo' COLLATE "C"); -SELECT collation for ('foo'); -- unknown type - null - pg_collation_for ------------------- - -(1 row) - -SELECT collation for ('foo'::text); - pg_collation_for ------------------- - "default" -(1 row) - -SELECT collation for ((SELECT a FROM collate_test1 LIMIT 1)); -- non-collatable type - error -ERROR: collations are not supported by type integer -SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1)); - pg_collation_for ------------------- - "C" -(1 row) - --- old bug with not dropping COLLATE when coercing to non-collatable type -CREATE VIEW collate_on_int AS -SELECT c1+1 AS c1p FROM - (SELECT ('4' COLLATE "C")::INT AS c1) ss; -\d+ collate_on_int - View "collate_tests.collate_on_int" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+---------+------------- - c1p | integer | | | | plain | -View definition: - SELECT ss.c1 + 1 AS c1p - FROM ( SELECT 4 AS c1) ss; - --- Check conflicting or redundant options in CREATE COLLATION --- LC_COLLATE -CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_COLLATE = "NONSENSE", LC_CTYPE = "POSIX"); -ERROR: conflicting or redundant options -LINE 1: ...ATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_COLLATE... - ^ --- LC_CTYPE -CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LC_CTYPE = "NONSENSE", LC_COLLATE = "POSIX"); -ERROR: conflicting or redundant options -LINE 1: ...REATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LC_CTYPE =... - ^ --- PROVIDER -CREATE COLLATION coll_dup_chk (PROVIDER = icu, PROVIDER = NONSENSE, LC_COLLATE = "POSIX", LC_CTYPE = "POSIX"); -ERROR: conflicting or redundant options -LINE 1: CREATE COLLATION coll_dup_chk (PROVIDER = icu, PROVIDER = NO... - ^ --- LOCALE -CREATE COLLATION case_sensitive (LOCALE = '', LOCALE = "NONSENSE"); -ERROR: conflicting or redundant options -LINE 1: CREATE COLLATION case_sensitive (LOCALE = '', LOCALE = "NONS... - ^ --- DETERMINISTIC -CREATE COLLATION coll_dup_chk (DETERMINISTIC = TRUE, DETERMINISTIC = NONSENSE, LOCALE = ''); -ERROR: conflicting or redundant options -LINE 1: ...ATE COLLATION coll_dup_chk (DETERMINISTIC = TRUE, DETERMINIS... - ^ --- VERSION -CREATE COLLATION coll_dup_chk (VERSION = '1', VERSION = "NONSENSE", LOCALE = ''); -ERROR: conflicting or redundant options -LINE 1: CREATE COLLATION coll_dup_chk (VERSION = '1', VERSION = "NON... - ^ --- LOCALE conflicts with LC_COLLATE and LC_CTYPE -CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_CTYPE = "POSIX", LOCALE = ''); -ERROR: conflicting or redundant options -DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. --- LOCALE conflicts with LC_COLLATE -CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LOCALE = ''); -ERROR: conflicting or redundant options -DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. --- LOCALE conflicts with LC_CTYPE -CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LOCALE = ''); -ERROR: conflicting or redundant options -DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. --- FROM conflicts with any other option -CREATE COLLATION coll_dup_chk (FROM = "C", VERSION = "1"); -ERROR: conflicting or redundant options -DETAIL: FROM cannot be specified together with any other options. --- --- Clean up. Many of these table names will be re-used if the user is --- trying to run any platform-specific collation tests later, so we --- must get rid of them. --- -DROP SCHEMA collate_tests CASCADE; -NOTICE: drop cascades to 19 other objects -DETAIL: drop cascades to table collate_test1 -drop cascades to table collate_test_like -drop cascades to table collate_test2 -drop cascades to type testdomain_p -drop cascades to table collate_test4 -drop cascades to table collate_test5 -drop cascades to table collate_test10 -drop cascades to view collview1 -drop cascades to view collview2 -drop cascades to view collview3 -drop cascades to type testdomain -drop cascades to function vc(text) -drop cascades to function dup(anyelement) -drop cascades to table collate_test20 -drop cascades to table collate_test21 -drop cascades to table collate_test22 -drop cascades to collation mycoll2 -drop cascades to table collate_test23 -drop cascades to view collate_on_int +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/matview.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/matview.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/matview.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/matview.out 2021-10-30 22:04:14 -0600 @@ -1,678 +1,2 @@ --- create a table to use as a basis for views and materialized views in various combinations -CREATE TABLE mvtest_t (id int NOT NULL PRIMARY KEY, type text NOT NULL, amt numeric NOT NULL); -INSERT INTO mvtest_t VALUES - (1, 'x', 2), - (2, 'x', 3), - (3, 'y', 5), - (4, 'y', 7), - (5, 'z', 11); --- we want a view based on the table, too, since views present additional challenges -CREATE VIEW mvtest_tv AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type; -SELECT * FROM mvtest_tv ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 11 -(3 rows) - --- create a materialized view with no data, and confirm correct behavior -EXPLAIN (costs off) - CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; - QUERY PLAN ----------------------------- - HashAggregate - Group Key: type - -> Seq Scan on mvtest_t -(3 rows) - -CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; - relispopulated ----------------- - f -(1 row) - -SELECT * FROM mvtest_tm ORDER BY type; -ERROR: materialized view "mvtest_tm" has not been populated -HINT: Use the REFRESH MATERIALIZED VIEW command. -REFRESH MATERIALIZED VIEW mvtest_tm; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; - relispopulated ----------------- - t -(1 row) - -CREATE UNIQUE INDEX mvtest_tm_type ON mvtest_tm (type); -SELECT * FROM mvtest_tm ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 11 -(3 rows) - --- create various views -EXPLAIN (costs off) - CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY type; - QUERY PLAN ----------------------------------- - Sort - Sort Key: mvtest_t.type - -> HashAggregate - Group Key: mvtest_t.type - -> Seq Scan on mvtest_t -(5 rows) - -CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY type; -SELECT * FROM mvtest_tvm; - type | totamt -------+-------- - x | 5 - y | 12 - z | 11 -(3 rows) - -CREATE MATERIALIZED VIEW mvtest_tmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tm; -CREATE MATERIALIZED VIEW mvtest_tvmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tvm; -CREATE UNIQUE INDEX mvtest_tvmm_expr ON mvtest_tvmm ((grandtot > 0)); -CREATE UNIQUE INDEX mvtest_tvmm_pred ON mvtest_tvmm (grandtot) WHERE grandtot < 0; -CREATE VIEW mvtest_tvv AS SELECT sum(totamt) AS grandtot FROM mvtest_tv; -EXPLAIN (costs off) - CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; - QUERY PLAN ----------------------------------- - Aggregate - -> HashAggregate - Group Key: mvtest_t.type - -> Seq Scan on mvtest_t -(4 rows) - -CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; -CREATE VIEW mvtest_tvvmv AS SELECT * FROM mvtest_tvvm; -CREATE MATERIALIZED VIEW mvtest_bb AS SELECT * FROM mvtest_tvvmv; -CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); --- check that plans seem reasonable -\d+ mvtest_tvm - Materialized view "public.mvtest_tvm" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+----------+--------------+------------- - type | text | | | | extended | | - totamt | numeric | | | | main | | -View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt - FROM mvtest_tv - ORDER BY mvtest_tv.type; - -\d+ mvtest_tvm - Materialized view "public.mvtest_tvm" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+----------+--------------+------------- - type | text | | | | extended | | - totamt | numeric | | | | main | | -View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt - FROM mvtest_tv - ORDER BY mvtest_tv.type; - -\d+ mvtest_tvvm - Materialized view "public.mvtest_tvvm" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+---------+---------+--------------+------------- - grandtot | numeric | | | | main | | -View definition: - SELECT mvtest_tvv.grandtot - FROM mvtest_tvv; - -\d+ mvtest_bb - Materialized view "public.mvtest_bb" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+---------+---------+--------------+------------- - grandtot | numeric | | | | main | | -Indexes: - "mvtest_aa" btree (grandtot) -View definition: - SELECT mvtest_tvvmv.grandtot - FROM mvtest_tvvmv; - --- test schema behavior -CREATE SCHEMA mvtest_mvschema; -ALTER MATERIALIZED VIEW mvtest_tvm SET SCHEMA mvtest_mvschema; -\d+ mvtest_tvm -\d+ mvtest_tvmm - Materialized view "public.mvtest_tvmm" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+---------+---------+--------------+------------- - grandtot | numeric | | | | main | | -Indexes: - "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) - "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric -View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot - FROM mvtest_mvschema.mvtest_tvm; - -SET search_path = mvtest_mvschema, public; -\d+ mvtest_tvm - Materialized view "mvtest_mvschema.mvtest_tvm" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+----------+--------------+------------- - type | text | | | | extended | | - totamt | numeric | | | | main | | -View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt - FROM mvtest_tv - ORDER BY mvtest_tv.type; - --- modify the underlying table data -INSERT INTO mvtest_t VALUES (6, 'z', 13); --- confirm pre- and post-refresh contents of fairly simple materialized views -SELECT * FROM mvtest_tm ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 11 -(3 rows) - -SELECT * FROM mvtest_tvm ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 11 -(3 rows) - -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tm; -REFRESH MATERIALIZED VIEW mvtest_tvm; -SELECT * FROM mvtest_tm ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 24 -(3 rows) - -SELECT * FROM mvtest_tvm ORDER BY type; - type | totamt -------+-------- - x | 5 - y | 12 - z | 24 -(3 rows) - -RESET search_path; --- confirm pre- and post-refresh contents of nested materialized views -EXPLAIN (costs off) - SELECT * FROM mvtest_tmm; - QUERY PLAN ------------------------- - Seq Scan on mvtest_tmm -(1 row) - -EXPLAIN (costs off) - SELECT * FROM mvtest_tvmm; - QUERY PLAN -------------------------- - Seq Scan on mvtest_tvmm -(1 row) - -EXPLAIN (costs off) - SELECT * FROM mvtest_tvvm; - QUERY PLAN -------------------------- - Seq Scan on mvtest_tvvm -(1 row) - -SELECT * FROM mvtest_tmm; - grandtot ----------- - 28 -(1 row) - -SELECT * FROM mvtest_tvmm; - grandtot ----------- - 28 -(1 row) - -SELECT * FROM mvtest_tvvm; - grandtot ----------- - 28 -(1 row) - -REFRESH MATERIALIZED VIEW mvtest_tmm; -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm; -ERROR: cannot refresh materialized view "public.mvtest_tvmm" concurrently -HINT: Create a unique index with no WHERE clause on one or more columns of the materialized view. -REFRESH MATERIALIZED VIEW mvtest_tvmm; -REFRESH MATERIALIZED VIEW mvtest_tvvm; -EXPLAIN (costs off) - SELECT * FROM mvtest_tmm; - QUERY PLAN ------------------------- - Seq Scan on mvtest_tmm -(1 row) - -EXPLAIN (costs off) - SELECT * FROM mvtest_tvmm; - QUERY PLAN -------------------------- - Seq Scan on mvtest_tvmm -(1 row) - -EXPLAIN (costs off) - SELECT * FROM mvtest_tvvm; - QUERY PLAN -------------------------- - Seq Scan on mvtest_tvvm -(1 row) - -SELECT * FROM mvtest_tmm; - grandtot ----------- - 41 -(1 row) - -SELECT * FROM mvtest_tvmm; - grandtot ----------- - 41 -(1 row) - -SELECT * FROM mvtest_tvvm; - grandtot ----------- - 41 -(1 row) - --- test diemv when the mv does not exist -DROP MATERIALIZED VIEW IF EXISTS no_such_mv; -NOTICE: materialized view "no_such_mv" does not exist, skipping --- make sure invalid combination of options is prohibited -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm WITH NO DATA; -ERROR: CONCURRENTLY and WITH NO DATA options cannot be used together --- no tuple locks on materialized views -SELECT * FROM mvtest_tvvm FOR SHARE; -ERROR: cannot lock rows in materialized view "mvtest_tvvm" --- test join of mv and view -SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM mvtest_tm m LEFT JOIN mvtest_tv v USING (type) ORDER BY type; - type | mtot | vtot -------+------+------ - x | 5 | 5 - y | 12 | 12 - z | 24 | 24 -(3 rows) - --- make sure that dependencies are reported properly when they block the drop -DROP TABLE mvtest_t; -ERROR: cannot drop table mvtest_t because other objects depend on it -DETAIL: view mvtest_tv depends on table mvtest_t -materialized view mvtest_mvschema.mvtest_tvm depends on view mvtest_tv -materialized view mvtest_tvmm depends on materialized view mvtest_mvschema.mvtest_tvm -view mvtest_tvv depends on view mvtest_tv -materialized view mvtest_tvvm depends on view mvtest_tvv -view mvtest_tvvmv depends on materialized view mvtest_tvvm -materialized view mvtest_bb depends on view mvtest_tvvmv -materialized view mvtest_tm depends on table mvtest_t -materialized view mvtest_tmm depends on materialized view mvtest_tm -HINT: Use DROP ... CASCADE to drop the dependent objects too. --- make sure dependencies are dropped and reported --- and make sure that transactional behavior is correct on rollback --- incidentally leaving some interesting materialized views for pg_dump testing -BEGIN; -DROP TABLE mvtest_t CASCADE; -NOTICE: drop cascades to 9 other objects -DETAIL: drop cascades to view mvtest_tv -drop cascades to materialized view mvtest_mvschema.mvtest_tvm -drop cascades to materialized view mvtest_tvmm -drop cascades to view mvtest_tvv -drop cascades to materialized view mvtest_tvvm -drop cascades to view mvtest_tvvmv -drop cascades to materialized view mvtest_bb -drop cascades to materialized view mvtest_tm -drop cascades to materialized view mvtest_tmm -ROLLBACK; --- some additional tests not using base tables -CREATE VIEW mvtest_vt1 AS SELECT 1 moo; -CREATE VIEW mvtest_vt2 AS SELECT moo, 2*moo FROM mvtest_vt1 UNION ALL SELECT moo, 3*moo FROM mvtest_vt1; -\d+ mvtest_vt2 - View "public.mvtest_vt2" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+---------+-----------+----------+---------+---------+------------- - moo | integer | | | | plain | - ?column? | integer | | | | plain | -View definition: - SELECT mvtest_vt1.moo, - 2 * mvtest_vt1.moo - FROM mvtest_vt1 -UNION ALL - SELECT mvtest_vt1.moo, - 3 * mvtest_vt1.moo - FROM mvtest_vt1; - -CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM mvtest_vt2 UNION ALL SELECT moo, 3*moo FROM mvtest_vt2; -\d+ mv_test2 - Materialized view "public.mv_test2" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+---------+---------+--------------+------------- - moo | integer | | | | plain | | - ?column? | integer | | | | plain | | -View definition: - SELECT mvtest_vt2.moo, - 2 * mvtest_vt2.moo - FROM mvtest_vt2 -UNION ALL - SELECT mvtest_vt2.moo, - 3 * mvtest_vt2.moo - FROM mvtest_vt2; - -CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; -SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass; - relispopulated ----------------- - t -(1 row) - -DROP VIEW mvtest_vt1 CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to view mvtest_vt2 -drop cascades to materialized view mv_test2 -drop cascades to materialized view mv_test3 --- test that duplicate values on unique index prevent refresh -CREATE TABLE mvtest_foo(a, b) AS VALUES(1, 10); -CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; -CREATE UNIQUE INDEX ON mvtest_mv(a); -INSERT INTO mvtest_foo SELECT * FROM mvtest_foo; -REFRESH MATERIALIZED VIEW mvtest_mv; -ERROR: could not create unique index "mvtest_mv_a_idx" -DETAIL: Key (a)=(1) is duplicated. -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; -ERROR: new data for materialized view "mvtest_mv" contains duplicate rows without any null columns -DETAIL: Row: (1,10) -DROP TABLE mvtest_foo CASCADE; -NOTICE: drop cascades to materialized view mvtest_mv --- make sure that all columns covered by unique indexes works -CREATE TABLE mvtest_foo(a, b, c) AS VALUES(1, 2, 3); -CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; -CREATE UNIQUE INDEX ON mvtest_mv (a); -CREATE UNIQUE INDEX ON mvtest_mv (b); -CREATE UNIQUE INDEX on mvtest_mv (c); -INSERT INTO mvtest_foo VALUES(2, 3, 4); -INSERT INTO mvtest_foo VALUES(3, 4, 5); -REFRESH MATERIALIZED VIEW mvtest_mv; -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; -DROP TABLE mvtest_foo CASCADE; -NOTICE: drop cascades to materialized view mvtest_mv --- allow subquery to reference unpopulated matview if WITH NO DATA is specified -CREATE MATERIALIZED VIEW mvtest_mv1 AS SELECT 1 AS col1 WITH NO DATA; -CREATE MATERIALIZED VIEW mvtest_mv2 AS SELECT * FROM mvtest_mv1 - WHERE col1 = (SELECT LEAST(col1) FROM mvtest_mv1) WITH NO DATA; -DROP MATERIALIZED VIEW mvtest_mv1 CASCADE; -NOTICE: drop cascades to materialized view mvtest_mv2 --- make sure that types with unusual equality tests work -CREATE TABLE mvtest_boxes (id serial primary key, b box); -INSERT INTO mvtest_boxes (b) VALUES - ('(32,32),(31,31)'), - ('(2.0000004,2.0000004),(1,1)'), - ('(1.9999996,1.9999996),(1,1)'); -CREATE MATERIALIZED VIEW mvtest_boxmv AS SELECT * FROM mvtest_boxes; -CREATE UNIQUE INDEX mvtest_boxmv_id ON mvtest_boxmv (id); -UPDATE mvtest_boxes SET b = '(2,2),(1,1)' WHERE id = 2; -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_boxmv; -SELECT * FROM mvtest_boxmv ORDER BY id; - id | b -----+----------------------------- - 1 | (32,32),(31,31) - 2 | (2,2),(1,1) - 3 | (1.9999996,1.9999996),(1,1) -(3 rows) - -DROP TABLE mvtest_boxes CASCADE; -NOTICE: drop cascades to materialized view mvtest_boxmv --- make sure that column names are handled correctly -CREATE TABLE mvtest_v (i int, j int); -CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj, kk) AS SELECT i, j FROM mvtest_v; -- error -ERROR: too many column names were specified -CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj) AS SELECT i, j FROM mvtest_v; -- ok -CREATE MATERIALIZED VIEW mvtest_mv_v_2 (ii) AS SELECT i, j FROM mvtest_v; -- ok -CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj, kk) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- error -ERROR: too many column names were specified -CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok -CREATE MATERIALIZED VIEW mvtest_mv_v_4 (ii) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok -ALTER TABLE mvtest_v RENAME COLUMN i TO x; -INSERT INTO mvtest_v values (1, 2); -CREATE UNIQUE INDEX mvtest_mv_v_ii ON mvtest_mv_v (ii); -REFRESH MATERIALIZED VIEW mvtest_mv_v; -UPDATE mvtest_v SET j = 3 WHERE x = 1; -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v; -REFRESH MATERIALIZED VIEW mvtest_mv_v_2; -REFRESH MATERIALIZED VIEW mvtest_mv_v_3; -REFRESH MATERIALIZED VIEW mvtest_mv_v_4; -SELECT * FROM mvtest_v; - x | j ----+--- - 1 | 3 -(1 row) - -SELECT * FROM mvtest_mv_v; - ii | jj -----+---- - 1 | 3 -(1 row) - -SELECT * FROM mvtest_mv_v_2; - ii | j -----+--- - 1 | 3 -(1 row) - -SELECT * FROM mvtest_mv_v_3; - ii | jj -----+---- - 1 | 3 -(1 row) - -SELECT * FROM mvtest_mv_v_4; - ii | j -----+--- - 1 | 3 -(1 row) - -DROP TABLE mvtest_v CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to materialized view mvtest_mv_v -drop cascades to materialized view mvtest_mv_v_2 -drop cascades to materialized view mvtest_mv_v_3 -drop cascades to materialized view mvtest_mv_v_4 --- Check that unknown literals are converted to "text" in CREATE MATVIEW, --- so that we don't end up with unknown-type columns. -CREATE MATERIALIZED VIEW mv_unspecified_types AS - SELECT 42 as i, 42.5 as num, 'foo' as u, 'foo'::unknown as u2, null as n; -\d+ mv_unspecified_types - Materialized view "public.mv_unspecified_types" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+----------+--------------+------------- - i | integer | | | | plain | | - num | numeric | | | | main | | - u | text | | | | extended | | - u2 | text | | | | extended | | - n | text | | | | extended | | -View definition: - SELECT 42 AS i, - 42.5 AS num, - 'foo'::text AS u, - 'foo'::text AS u2, - NULL::text AS n; - -SELECT * FROM mv_unspecified_types; - i | num | u | u2 | n -----+------+-----+-----+--- - 42 | 42.5 | foo | foo | -(1 row) - -DROP MATERIALIZED VIEW mv_unspecified_types; --- make sure that create WITH NO DATA does not plan the query (bug #13907) -create materialized view mvtest_error as select 1/0 as x; -- fail -ERROR: division by zero -create materialized view mvtest_error as select 1/0 as x with no data; -refresh materialized view mvtest_error; -- fail here -ERROR: division by zero -drop materialized view mvtest_error; --- make sure that matview rows can be referenced as source rows (bug #9398) -CREATE TABLE mvtest_v AS SELECT generate_series(1,10) AS a; -CREATE MATERIALIZED VIEW mvtest_mv_v AS SELECT a FROM mvtest_v WHERE a <= 5; -DELETE FROM mvtest_v WHERE EXISTS ( SELECT * FROM mvtest_mv_v WHERE mvtest_mv_v.a = mvtest_v.a ); -SELECT * FROM mvtest_v; - a ----- - 6 - 7 - 8 - 9 - 10 -(5 rows) - -SELECT * FROM mvtest_mv_v; - a ---- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -DROP TABLE mvtest_v CASCADE; -NOTICE: drop cascades to materialized view mvtest_mv_v --- make sure running as superuser works when MV owned by another role (bug #11208) -CREATE ROLE regress_user_mvtest; -SET ROLE regress_user_mvtest; --- this test case also checks for ambiguity in the queries issued by --- refresh_by_match_merge(), by choosing column names that intentionally --- duplicate all the aliases used in those queries -CREATE TABLE mvtest_foo_data AS SELECT i, - i+1 AS tid, - md5(random()::text) AS mv, - md5(random()::text) AS newdata, - md5(random()::text) AS newdata2, - md5(random()::text) AS diff - FROM generate_series(1, 10) i; -CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; -CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; -ERROR: relation "mvtest_mv_foo" already exists -CREATE MATERIALIZED VIEW IF NOT EXISTS mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; -NOTICE: relation "mvtest_mv_foo" already exists, skipping -CREATE UNIQUE INDEX ON mvtest_mv_foo (i); -RESET ROLE; -REFRESH MATERIALIZED VIEW mvtest_mv_foo; -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_foo; -DROP OWNED BY regress_user_mvtest CASCADE; -DROP ROLE regress_user_mvtest; --- make sure that create WITH NO DATA works via SPI -BEGIN; -CREATE FUNCTION mvtest_func() - RETURNS void AS $$ -BEGIN - CREATE MATERIALIZED VIEW mvtest1 AS SELECT 1 AS x; - CREATE MATERIALIZED VIEW mvtest2 AS SELECT 1 AS x WITH NO DATA; -END; -$$ LANGUAGE plpgsql; -SELECT mvtest_func(); - mvtest_func -------------- - -(1 row) - -SELECT * FROM mvtest1; - x ---- - 1 -(1 row) - -SELECT * FROM mvtest2; -ERROR: materialized view "mvtest2" has not been populated -HINT: Use the REFRESH MATERIALIZED VIEW command. -ROLLBACK; --- INSERT privileges if relation owner is not allowed to insert. -CREATE SCHEMA matview_schema; -CREATE USER regress_matview_user; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_matview_user - REVOKE INSERT ON TABLES FROM regress_matview_user; -GRANT ALL ON SCHEMA matview_schema TO public; -SET SESSION AUTHORIZATION regress_matview_user; -CREATE MATERIALIZED VIEW matview_schema.mv_withdata1 (a) AS - SELECT generate_series(1, 10) WITH DATA; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW matview_schema.mv_withdata2 (a) AS - SELECT generate_series(1, 10) WITH DATA; - QUERY PLAN --------------------------------------- - ProjectSet (actual rows=10 loops=1) - -> Result (actual rows=1 loops=1) -(2 rows) - -REFRESH MATERIALIZED VIEW matview_schema.mv_withdata2; -CREATE MATERIALIZED VIEW matview_schema.mv_nodata1 (a) AS - SELECT generate_series(1, 10) WITH NO DATA; -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW matview_schema.mv_nodata2 (a) AS - SELECT generate_series(1, 10) WITH NO DATA; - QUERY PLAN -------------------------------- - ProjectSet (never executed) - -> Result (never executed) -(2 rows) - -REFRESH MATERIALIZED VIEW matview_schema.mv_nodata2; -RESET SESSION AUTHORIZATION; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_matview_user - GRANT INSERT ON TABLES TO regress_matview_user; -DROP SCHEMA matview_schema CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to materialized view matview_schema.mv_withdata1 -drop cascades to materialized view matview_schema.mv_withdata2 -drop cascades to materialized view matview_schema.mv_nodata1 -drop cascades to materialized view matview_schema.mv_nodata2 -DROP USER regress_matview_user; --- CREATE MATERIALIZED VIEW ... IF NOT EXISTS -CREATE MATERIALIZED VIEW matview_ine_tab AS SELECT 1; -CREATE MATERIALIZED VIEW matview_ine_tab AS SELECT 1 / 0; -- error -ERROR: relation "matview_ine_tab" already exists -CREATE MATERIALIZED VIEW IF NOT EXISTS matview_ine_tab AS - SELECT 1 / 0; -- ok -NOTICE: relation "matview_ine_tab" already exists, skipping -CREATE MATERIALIZED VIEW matview_ine_tab AS - SELECT 1 / 0 WITH NO DATA; -- error -ERROR: relation "matview_ine_tab" already exists -CREATE MATERIALIZED VIEW IF NOT EXISTS matview_ine_tab AS - SELECT 1 / 0 WITH NO DATA; -- ok -NOTICE: relation "matview_ine_tab" already exists, skipping -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW matview_ine_tab AS - SELECT 1 / 0; -- error -ERROR: relation "matview_ine_tab" already exists -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW IF NOT EXISTS matview_ine_tab AS - SELECT 1 / 0; -- ok -NOTICE: relation "matview_ine_tab" already exists, skipping - QUERY PLAN ------------- -(0 rows) - -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW matview_ine_tab AS - SELECT 1 / 0 WITH NO DATA; -- error -ERROR: relation "matview_ine_tab" already exists -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) - CREATE MATERIALIZED VIEW IF NOT EXISTS matview_ine_tab AS - SELECT 1 / 0 WITH NO DATA; -- ok -NOTICE: relation "matview_ine_tab" already exists, skipping - QUERY PLAN ------------- -(0 rows) - -DROP MATERIALIZED VIEW matview_ine_tab; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/lock.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lock.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/lock.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lock.out 2021-10-30 22:04:14 -0600 @@ -1,178 +1,2 @@ --- --- Test the LOCK statement --- --- Setup -CREATE SCHEMA lock_schema1; -SET search_path = lock_schema1; -CREATE TABLE lock_tbl1 (a BIGINT); -CREATE TABLE lock_tbl1a (a BIGINT); -CREATE VIEW lock_view1 AS SELECT * FROM lock_tbl1; -CREATE VIEW lock_view2(a,b) AS SELECT * FROM lock_tbl1, lock_tbl1a; -CREATE VIEW lock_view3 AS SELECT * from lock_view2; -CREATE VIEW lock_view4 AS SELECT (select a from lock_tbl1a limit 1) from lock_tbl1; -CREATE VIEW lock_view5 AS SELECT * from lock_tbl1 where a in (select * from lock_tbl1a); -CREATE VIEW lock_view6 AS SELECT * from (select * from lock_tbl1) sub; -CREATE ROLE regress_rol_lock1; -ALTER ROLE regress_rol_lock1 SET search_path = lock_schema1; -GRANT USAGE ON SCHEMA lock_schema1 TO regress_rol_lock1; --- Try all valid lock options; also try omitting the optional TABLE keyword. -BEGIN TRANSACTION; -LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE; -LOCK lock_tbl1 IN ROW SHARE MODE; -LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE; -LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE; -LOCK TABLE lock_tbl1 IN SHARE MODE; -LOCK lock_tbl1 IN SHARE ROW EXCLUSIVE MODE; -LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE; -LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE; -ROLLBACK; --- Try using NOWAIT along with valid options. -BEGIN TRANSACTION; -LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN ROW SHARE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN SHARE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN SHARE ROW EXCLUSIVE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE NOWAIT; -LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE NOWAIT; -ROLLBACK; --- Verify that we can lock views. -BEGIN TRANSACTION; -LOCK TABLE lock_view1 IN EXCLUSIVE MODE; --- lock_view1 and lock_tbl1 are locked. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_view1 -(2 rows) - -ROLLBACK; -BEGIN TRANSACTION; -LOCK TABLE lock_view2 IN EXCLUSIVE MODE; --- lock_view1, lock_tbl1, and lock_tbl1a are locked. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_tbl1a - lock_view2 -(3 rows) - -ROLLBACK; -BEGIN TRANSACTION; -LOCK TABLE lock_view3 IN EXCLUSIVE MODE; --- lock_view3, lock_view2, lock_tbl1, and lock_tbl1a are locked recursively. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_tbl1a - lock_view2 - lock_view3 -(4 rows) - -ROLLBACK; -BEGIN TRANSACTION; -LOCK TABLE lock_view4 IN EXCLUSIVE MODE; --- lock_view4, lock_tbl1, and lock_tbl1a are locked. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_tbl1a - lock_view4 -(3 rows) - -ROLLBACK; -BEGIN TRANSACTION; -LOCK TABLE lock_view5 IN EXCLUSIVE MODE; --- lock_view5, lock_tbl1, and lock_tbl1a are locked. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_tbl1a - lock_view5 -(3 rows) - -ROLLBACK; -BEGIN TRANSACTION; -LOCK TABLE lock_view6 IN EXCLUSIVE MODE; --- lock_view6 an lock_tbl1 are locked. -select relname from pg_locks l, pg_class c - where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' - order by relname; - relname ------------- - lock_tbl1 - lock_view6 -(2 rows) - -ROLLBACK; --- Verify that we cope with infinite recursion in view definitions. -CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3; -BEGIN TRANSACTION; -LOCK TABLE lock_view2 IN EXCLUSIVE MODE; -ROLLBACK; -CREATE VIEW lock_view7 AS SELECT * from lock_view2; -BEGIN TRANSACTION; -LOCK TABLE lock_view7 IN EXCLUSIVE MODE; -ROLLBACK; --- Verify that we can lock a table with inheritance children. -CREATE TABLE lock_tbl2 (b BIGINT) INHERITS (lock_tbl1); -CREATE TABLE lock_tbl3 () INHERITS (lock_tbl2); -BEGIN TRANSACTION; -LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; -ROLLBACK; --- Child tables are locked without granting explicit permission to do so as --- long as we have permission to lock the parent. -GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1; -SET ROLE regress_rol_lock1; --- fail when child locked directly -BEGIN; -LOCK TABLE lock_tbl2; -ERROR: permission denied for table lock_tbl2 -ROLLBACK; -BEGIN; -LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; -ROLLBACK; -BEGIN; -LOCK TABLE ONLY lock_tbl1; -ROLLBACK; -RESET ROLE; --- --- Clean up --- -DROP VIEW lock_view7; -DROP VIEW lock_view6; -DROP VIEW lock_view5; -DROP VIEW lock_view4; -DROP VIEW lock_view3 CASCADE; -NOTICE: drop cascades to view lock_view2 -DROP VIEW lock_view1; -DROP TABLE lock_tbl3; -DROP TABLE lock_tbl2; -DROP TABLE lock_tbl1; -DROP TABLE lock_tbl1a; -DROP SCHEMA lock_schema1 CASCADE; -DROP ROLE regress_rol_lock1; --- atomic ops tests -RESET search_path; -SELECT test_atomic_ops(); - test_atomic_ops ------------------ - t -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/replica_identity.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/replica_identity.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/replica_identity.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/replica_identity.out 2021-10-30 22:04:14 -0600 @@ -1,229 +1,2 @@ -CREATE TABLE test_replica_identity ( - id serial primary key, - keya text not null, - keyb text not null, - nonkey text, - CONSTRAINT test_replica_identity_unique_defer UNIQUE (keya, keyb) DEFERRABLE, - CONSTRAINT test_replica_identity_unique_nondefer UNIQUE (keya, keyb) -) ; -CREATE TABLE test_replica_identity_othertable (id serial primary key); -CREATE INDEX test_replica_identity_keyab ON test_replica_identity (keya, keyb); -CREATE UNIQUE INDEX test_replica_identity_keyab_key ON test_replica_identity (keya, keyb); -CREATE UNIQUE INDEX test_replica_identity_nonkey ON test_replica_identity (keya, nonkey); -CREATE INDEX test_replica_identity_hash ON test_replica_identity USING hash (nonkey); -CREATE UNIQUE INDEX test_replica_identity_expr ON test_replica_identity (keya, keyb, (3)); -CREATE UNIQUE INDEX test_replica_identity_partial ON test_replica_identity (keya, keyb) WHERE keyb != '3'; --- default is 'd'/DEFAULT for user created tables -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - d -(1 row) - --- but 'none' for system tables -SELECT relreplident FROM pg_class WHERE oid = 'pg_class'::regclass; - relreplident --------------- - n -(1 row) - -SELECT relreplident FROM pg_class WHERE oid = 'pg_constraint'::regclass; - relreplident --------------- - n -(1 row) - ----- --- Make sure we detect ineligible indexes ----- --- fail, not unique -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab; -ERROR: cannot use non-unique index "test_replica_identity_keyab" as replica identity --- fail, not a candidate key, nullable column -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_nonkey; -ERROR: index "test_replica_identity_nonkey" cannot be used as replica identity because column "nonkey" is nullable --- fail, hash indexes cannot do uniqueness -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_hash; -ERROR: cannot use non-unique index "test_replica_identity_hash" as replica identity --- fail, expression index -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_expr; -ERROR: cannot use expression index "test_replica_identity_expr" as replica identity --- fail, partial index -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_partial; -ERROR: cannot use partial index "test_replica_identity_partial" as replica identity --- fail, not our index -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_othertable_pkey; -ERROR: "test_replica_identity_othertable_pkey" is not an index for table "test_replica_identity" --- fail, deferrable -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_defer; -ERROR: cannot use non-immediate index "test_replica_identity_unique_defer" as replica identity -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - d -(1 row) - ----- --- Make sure index cases succeed ----- --- succeed, primary key -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_pkey; -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - i -(1 row) - -\d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) - keya | text | | not null | - keyb | text | | not null | - nonkey | text | | | -Indexes: - "test_replica_identity_pkey" PRIMARY KEY, btree (id) REPLICA IDENTITY - "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) - "test_replica_identity_hash" hash (nonkey) - "test_replica_identity_keyab" btree (keya, keyb) - "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) - "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) - "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text - "test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE - "test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb) - --- succeed, nondeferrable unique constraint over nonnullable cols -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_nondefer; --- succeed unique index over nonnullable cols -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; -ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - i -(1 row) - -\d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) - keya | text | | not null | - keyb | text | | not null | - nonkey | text | | | -Indexes: - "test_replica_identity_pkey" PRIMARY KEY, btree (id) - "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) - "test_replica_identity_hash" hash (nonkey) - "test_replica_identity_keyab" btree (keya, keyb) - "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) REPLICA IDENTITY - "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) - "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text - "test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE - "test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb) - -SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; - count -------- - 1 -(1 row) - ----- --- Make sure non index cases work ----- -ALTER TABLE test_replica_identity REPLICA IDENTITY DEFAULT; -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - d -(1 row) - -SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; - count -------- - 0 -(1 row) - -ALTER TABLE test_replica_identity REPLICA IDENTITY FULL; -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - f -(1 row) - -\d+ test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | - keya | text | | not null | | extended | | - keyb | text | | not null | | extended | | - nonkey | text | | | | extended | | -Indexes: - "test_replica_identity_pkey" PRIMARY KEY, btree (id) - "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) - "test_replica_identity_hash" hash (nonkey) - "test_replica_identity_keyab" btree (keya, keyb) - "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) - "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) - "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text - "test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE - "test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb) -Replica Identity: FULL - -ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; -SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; - relreplident --------------- - n -(1 row) - ---- --- Test that ALTER TABLE rewrite preserves nondefault replica identity ---- --- constraint variant -CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL); -ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key; -\d test_replica_identity2 - Table "public.test_replica_identity2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - id | integer | | not null | -Indexes: - "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY - -ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint; -\d test_replica_identity2 - Table "public.test_replica_identity2" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+--------- - id | bigint | | not null | -Indexes: - "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY - --- straight index variant -CREATE TABLE test_replica_identity3 (id int NOT NULL); -CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id); -ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key; -\d test_replica_identity3 - Table "public.test_replica_identity3" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - id | integer | | not null | -Indexes: - "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY - -ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint; -\d test_replica_identity3 - Table "public.test_replica_identity3" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+--------- - id | bigint | | not null | -Indexes: - "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY - -DROP TABLE test_replica_identity; -DROP TABLE test_replica_identity2; -DROP TABLE test_replica_identity3; -DROP TABLE test_replica_identity_othertable; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rowsecurity.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rowsecurity.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rowsecurity.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rowsecurity.out 2021-10-30 22:04:14 -0600 @@ -1,4042 +1,2 @@ --- --- Test of Row-level security feature --- --- Clean up in case a prior regression run failed --- Suppress NOTICE messages when users/groups don't exist -SET client_min_messages TO 'warning'; -DROP USER IF EXISTS regress_rls_alice; -DROP USER IF EXISTS regress_rls_bob; -DROP USER IF EXISTS regress_rls_carol; -DROP USER IF EXISTS regress_rls_dave; -DROP USER IF EXISTS regress_rls_exempt_user; -DROP ROLE IF EXISTS regress_rls_group1; -DROP ROLE IF EXISTS regress_rls_group2; -DROP SCHEMA IF EXISTS regress_rls_schema CASCADE; -RESET client_min_messages; --- initial setup -CREATE USER regress_rls_alice NOLOGIN; -CREATE USER regress_rls_bob NOLOGIN; -CREATE USER regress_rls_carol NOLOGIN; -CREATE USER regress_rls_dave NOLOGIN; -CREATE USER regress_rls_exempt_user BYPASSRLS NOLOGIN; -CREATE ROLE regress_rls_group1 NOLOGIN; -CREATE ROLE regress_rls_group2 NOLOGIN; -GRANT regress_rls_group1 TO regress_rls_bob; -GRANT regress_rls_group2 TO regress_rls_carol; -CREATE SCHEMA regress_rls_schema; -GRANT ALL ON SCHEMA regress_rls_schema to public; -SET search_path = regress_rls_schema; --- setup of malicious function -CREATE OR REPLACE FUNCTION f_leak(text) RETURNS bool - COST 0.0000001 LANGUAGE plpgsql - AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END'; -GRANT EXECUTE ON FUNCTION f_leak(text) TO public; --- BASIC Row-Level Security Scenario -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE uaccount ( - pguser name primary key, - seclv int -); -GRANT SELECT ON uaccount TO public; -INSERT INTO uaccount VALUES - ('regress_rls_alice', 99), - ('regress_rls_bob', 1), - ('regress_rls_carol', 2), - ('regress_rls_dave', 3); -CREATE TABLE category ( - cid int primary key, - cname text -); -GRANT ALL ON category TO public; -INSERT INTO category VALUES - (11, 'novel'), - (22, 'science fiction'), - (33, 'technology'), - (44, 'manga'); -CREATE TABLE document ( - did int primary key, - cid int references category(cid), - dlevel int not null, - dauthor name, - dtitle text -); -GRANT ALL ON document TO public; -INSERT INTO document VALUES - ( 1, 11, 1, 'regress_rls_bob', 'my first novel'), - ( 2, 11, 2, 'regress_rls_bob', 'my second novel'), - ( 3, 22, 2, 'regress_rls_bob', 'my science fiction'), - ( 4, 44, 1, 'regress_rls_bob', 'my first manga'), - ( 5, 44, 2, 'regress_rls_bob', 'my second manga'), - ( 6, 22, 1, 'regress_rls_carol', 'great science fiction'), - ( 7, 33, 2, 'regress_rls_carol', 'great technology book'), - ( 8, 44, 1, 'regress_rls_carol', 'great manga'), - ( 9, 22, 1, 'regress_rls_dave', 'awesome science fiction'), - (10, 33, 2, 'regress_rls_dave', 'awesome technology book'); -ALTER TABLE document ENABLE ROW LEVEL SECURITY; --- user's security level must be higher than or equal to document's -CREATE POLICY p1 ON document AS PERMISSIVE - USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); --- try to create a policy of bogus type -CREATE POLICY p1 ON document AS UGLY - USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); -ERROR: unrecognized row security option "ugly" -LINE 1: CREATE POLICY p1 ON document AS UGLY - ^ -HINT: Only PERMISSIVE or RESTRICTIVE policies are supported currently. --- but Dave isn't allowed to anything at cid 50 or above --- this is to make sure that we sort the policies by name first --- when applying WITH CHECK, a later INSERT by Dave should fail due --- to p1r first -CREATE POLICY p2r ON document AS RESTRICTIVE TO regress_rls_dave - USING (cid <> 44 AND cid < 50); --- and Dave isn't allowed to see manga documents -CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave - USING (cid <> 44); -\dp - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------------------+----------+-------+---------------------------------------------+-------------------+-------------------------------------------- - regress_rls_schema | category | table | regress_rls_alice=arwdDxt/regress_rls_alice+| | - | | | =arwdDxt/regress_rls_alice | | - regress_rls_schema | document | table | regress_rls_alice=arwdDxt/regress_rls_alice+| | p1: + - | | | =arwdDxt/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv + - | | | | | FROM uaccount + - | | | | | WHERE (uaccount.pguser = CURRENT_USER)))+ - | | | | | p2r (RESTRICTIVE): + - | | | | | (u): ((cid <> 44) AND (cid < 50)) + - | | | | | to: regress_rls_dave + - | | | | | p1r (RESTRICTIVE): + - | | | | | (u): (cid <> 44) + - | | | | | to: regress_rls_dave - regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxt/regress_rls_alice+| | - | | | =r/regress_rls_alice | | -(3 rows) - -\d document - Table "regress_rls_schema.document" - Column | Type | Collation | Nullable | Default ----------+---------+-----------+----------+--------- - did | integer | | not null | - cid | integer | | | - dlevel | integer | | not null | - dauthor | name | | | - dtitle | text | | | -Indexes: - "document_pkey" PRIMARY KEY, btree (did) -Foreign-key constraints: - "document_cid_fkey" FOREIGN KEY (cid) REFERENCES category(cid) -Policies: - POLICY "p1" - USING ((dlevel <= ( SELECT uaccount.seclv - FROM uaccount - WHERE (uaccount.pguser = CURRENT_USER)))) - POLICY "p1r" AS RESTRICTIVE - TO regress_rls_dave - USING ((cid <> 44)) - POLICY "p2r" AS RESTRICTIVE - TO regress_rls_dave - USING (((cid <> 44) AND (cid < 50))) - -SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename = 'document' ORDER BY policyname; - schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check ---------------------+-----------+------------+-------------+--------------------+-----+--------------------------------------------+------------ - regress_rls_schema | document | p1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +| - | | | | | | FROM uaccount +| - | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) | - regress_rls_schema | document | p1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 44) | - regress_rls_schema | document | p2r | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 44) AND (cid < 50)) | -(3 rows) - --- viewpoint from regress_rls_bob -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO ON; -SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my first manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 4 | 44 | 1 | regress_rls_bob | my first manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction -(5 rows) - -SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my first manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction - cid | did | dlevel | dauthor | dtitle | cname ------+-----+--------+-------------------+-------------------------+----------------- - 11 | 1 | 1 | regress_rls_bob | my first novel | novel - 44 | 4 | 1 | regress_rls_bob | my first manga | manga - 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction - 44 | 8 | 1 | regress_rls_carol | great manga | manga - 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction -(5 rows) - --- try a sampled version -SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0) - WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 4 | 44 | 1 | regress_rls_bob | my first manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction -(4 rows) - --- viewpoint from regress_rls_carol -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => my first manga -NOTICE: f_leak => my second manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => awesome technology book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book -(10 rows) - -SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => my first manga -NOTICE: f_leak => my second manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => awesome technology book - cid | did | dlevel | dauthor | dtitle | cname ------+-----+--------+-------------------+-------------------------+----------------- - 11 | 1 | 1 | regress_rls_bob | my first novel | novel - 11 | 2 | 2 | regress_rls_bob | my second novel | novel - 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction - 44 | 4 | 1 | regress_rls_bob | my first manga | manga - 44 | 5 | 2 | regress_rls_bob | my second manga | manga - 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction - 33 | 7 | 2 | regress_rls_carol | great technology book | technology - 44 | 8 | 1 | regress_rls_carol | great manga | manga - 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction - 33 | 10 | 2 | regress_rls_dave | awesome technology book | technology -(10 rows) - --- try a sampled version -SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0) - WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first manga -NOTICE: f_leak => my second manga -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great manga -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------- - Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------ - Hash Join - Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) - -> Seq Scan on category - -> Hash - -> Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) -(9 rows) - --- viewpoint from regress_rls_dave -SET SESSION AUTHORIZATION regress_rls_dave; -SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => awesome technology book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book -(7 rows) - -SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => awesome technology book - cid | did | dlevel | dauthor | dtitle | cname ------+-----+--------+-------------------+-------------------------+----------------- - 11 | 1 | 1 | regress_rls_bob | my first novel | novel - 11 | 2 | 2 | regress_rls_bob | my second novel | novel - 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction - 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction - 33 | 7 | 2 | regress_rls_carol | great technology book | technology - 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction - 33 | 10 | 2 | regress_rls_dave | awesome technology book | technology -(7 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------- - Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------------------- - Hash Join - Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) - -> Seq Scan on category - -> Hash - -> Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) -(9 rows) - --- 44 would technically fail for both p2r and p1r, but we should get an error --- back from p1r for this because it sorts first -INSERT INTO document VALUES (100, 44, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -ERROR: new row violates row-level security policy "p1r" for table "document" --- Just to see a p2r error -INSERT INTO document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -ERROR: new row violates row-level security policy "p2r" for table "document" --- only owner can change policies -ALTER POLICY p1 ON document USING (true); --fail -ERROR: must be owner of table document -DROP POLICY p1 ON document; --fail -ERROR: must be owner of relation document -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER POLICY p1 ON document USING (dauthor = current_user); --- viewpoint from regress_rls_bob again -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => my first manga -NOTICE: f_leak => my second manga - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+-------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga -(5 rows) - -SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my science fiction -NOTICE: f_leak => my first manga -NOTICE: f_leak => my second manga - cid | did | dlevel | dauthor | dtitle | cname ------+-----+--------+-----------------+--------------------+----------------- - 11 | 1 | 1 | regress_rls_bob | my first novel | novel - 11 | 2 | 2 | regress_rls_bob | my second novel | novel - 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction - 44 | 4 | 1 | regress_rls_bob | my first manga | manga - 44 | 5 | 2 | regress_rls_bob | my second manga | manga -(5 rows) - --- viewpoint from rls_regres_carol again -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => great manga - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------- - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga -(3 rows) - -SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did; -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great technology book -NOTICE: f_leak => great manga - cid | did | dlevel | dauthor | dtitle | cname ------+-----+--------+-------------------+-----------------------+----------------- - 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction - 33 | 7 | 2 | regress_rls_carol | great technology book | technology - 44 | 8 | 1 | regress_rls_carol | great manga | manga -(3 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ---------------------------------------------------------- - Seq Scan on document - Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle)) -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ---------------------------------------------------------------- - Nested Loop - -> Seq Scan on document - Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle)) - -> Index Scan using category_pkey on category - Index Cond: (cid = document.cid) -(5 rows) - --- interaction of FK/PK constraints -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE POLICY p2 ON category - USING (CASE WHEN current_user = 'regress_rls_bob' THEN cid IN (11, 33) - WHEN current_user = 'regress_rls_carol' THEN cid IN (22, 44) - ELSE false END); -ALTER TABLE category ENABLE ROW LEVEL SECURITY; --- cannot delete PK referenced by invisible FK -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid; - did | cid | dlevel | dauthor | dtitle | cid | cname ------+-----+--------+-----------------+--------------------+-----+------------ - 1 | 11 | 1 | regress_rls_bob | my first novel | 11 | novel - 2 | 11 | 2 | regress_rls_bob | my second novel | 11 | novel - 3 | 22 | 2 | regress_rls_bob | my science fiction | | - 4 | 44 | 1 | regress_rls_bob | my first manga | | - 5 | 44 | 2 | regress_rls_bob | my second manga | | - | | | | | 33 | technology -(6 rows) - -DELETE FROM category WHERE cid = 33; -- fails with FK violation -ERROR: update or delete on table "category" violates foreign key constraint "document_cid_fkey" on table "document" -DETAIL: Key is still referenced from table "document". --- can insert FK referencing invisible PK -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid; - did | cid | dlevel | dauthor | dtitle | cid | cname ------+-----+--------+-------------------+-----------------------+-----+----------------- - 6 | 22 | 1 | regress_rls_carol | great science fiction | 22 | science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book | | - 8 | 44 | 1 | regress_rls_carol | great manga | 44 | manga -(3 rows) - -INSERT INTO document VALUES (11, 33, 1, current_user, 'hoge'); --- UNIQUE or PRIMARY KEY constraint violation DOES reveal presence of row -SET SESSION AUTHORIZATION regress_rls_bob; -INSERT INTO document VALUES (8, 44, 1, 'regress_rls_bob', 'my third manga'); -- Must fail with unique violation, revealing presence of did we can't see -ERROR: duplicate key value violates unique constraint "document_pkey" -SELECT * FROM document WHERE did = 8; -- and confirm we can't see it - did | cid | dlevel | dauthor | dtitle ------+-----+--------+---------+-------- -(0 rows) - --- RLS policies are checked before constraints -INSERT INTO document VALUES (8, 44, 1, 'regress_rls_carol', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation -ERROR: new row violates row-level security policy for table "document" -UPDATE document SET did = 8, dauthor = 'regress_rls_carol' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation -ERROR: new row violates row-level security policy for table "document" --- database superuser does bypass RLS policy when enabled -RESET SESSION AUTHORIZATION; -SET row_security TO ON; -SELECT * FROM document; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book - 11 | 33 | 1 | regress_rls_carol | hoge -(11 rows) - -SELECT * FROM category; - cid | cname ------+----------------- - 11 | novel - 22 | science fiction - 33 | technology - 44 | manga -(4 rows) - --- database superuser does bypass RLS policy when disabled -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -SELECT * FROM document; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book - 11 | 33 | 1 | regress_rls_carol | hoge -(11 rows) - -SELECT * FROM category; - cid | cname ------+----------------- - 11 | novel - 22 | science fiction - 33 | technology - 44 | manga -(4 rows) - --- database non-superuser with bypass privilege can bypass RLS policy when disabled -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO OFF; -SELECT * FROM document; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book - 11 | 33 | 1 | regress_rls_carol | hoge -(11 rows) - -SELECT * FROM category; - cid | cname ------+----------------- - 11 | novel - 22 | science fiction - 33 | technology - 44 | manga -(4 rows) - --- RLS policy does not apply to table owner when RLS enabled. -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO ON; -SELECT * FROM document; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book - 11 | 33 | 1 | regress_rls_carol | hoge -(11 rows) - -SELECT * FROM category; - cid | cname ------+----------------- - 11 | novel - 22 | science fiction - 33 | technology - 44 | manga -(4 rows) - --- RLS policy does not apply to table owner when RLS disabled. -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO OFF; -SELECT * FROM document; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 22 | 2 | regress_rls_bob | my science fiction - 4 | 44 | 1 | regress_rls_bob | my first manga - 5 | 44 | 2 | regress_rls_bob | my second manga - 6 | 22 | 1 | regress_rls_carol | great science fiction - 7 | 33 | 2 | regress_rls_carol | great technology book - 8 | 44 | 1 | regress_rls_carol | great manga - 9 | 22 | 1 | regress_rls_dave | awesome science fiction - 10 | 33 | 2 | regress_rls_dave | awesome technology book - 11 | 33 | 1 | regress_rls_carol | hoge -(11 rows) - -SELECT * FROM category; - cid | cname ------+----------------- - 11 | novel - 22 | science fiction - 33 | technology - 44 | manga -(4 rows) - --- --- Table inheritance and RLS policy --- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO ON; -CREATE TABLE t1 (id int not null primary key, a int, junk1 text, b text); -ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor -GRANT ALL ON t1 TO public; -COPY t1 FROM stdin WITH ; -CREATE TABLE t2 (c float) INHERITS (t1); -GRANT ALL ON t2 TO public; -COPY t2 FROM stdin; -CREATE TABLE t3 (id int not null primary key, c text, b text, a int); -ALTER TABLE t3 INHERIT t1; -GRANT ALL ON t3 TO public; -COPY t3(id, a,b,c) FROM stdin; -CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number -CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number -ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE t2 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM t1; - id | a | b ------+---+----- - 102 | 2 | bbb - 104 | 4 | dad - 202 | 2 | bcd - 204 | 4 | def - 302 | 2 | yyy -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1; - QUERY PLAN -------------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: ((a % 2) = 0) - -> Seq Scan on t2 t1_2 - Filter: ((a % 2) = 0) - -> Seq Scan on t3 t1_3 - Filter: ((a % 2) = 0) -(7 rows) - -SELECT * FROM t1 WHERE f_leak(b); -NOTICE: f_leak => bbb -NOTICE: f_leak => dad -NOTICE: f_leak => bcd -NOTICE: f_leak => def -NOTICE: f_leak => yyy - id | a | b ------+---+----- - 102 | 2 | bbb - 104 | 4 | dad - 202 | 2 | bcd - 204 | 4 | def - 302 | 2 | yyy -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------ - Append - -> Seq Scan on t1 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: (((a % 2) = 0) AND f_leak(b)) -(7 rows) - --- reference to system column -SELECT tableoid::regclass, * FROM t1; - tableoid | id | a | b -----------+-----+---+----- - t1 | 102 | 2 | bbb - t1 | 104 | 4 | dad - t2 | 202 | 2 | bcd - t2 | 204 | 4 | def - t3 | 302 | 2 | yyy -(5 rows) - -EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1; - QUERY PLAN -------------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: ((a % 2) = 0) - -> Seq Scan on t2 t1_2 - Filter: ((a % 2) = 0) - -> Seq Scan on t3 t1_3 - Filter: ((a % 2) = 0) -(7 rows) - --- reference to whole-row reference -SELECT *, t1 FROM t1; - id | a | b | t1 ------+---+-----+------------- - 102 | 2 | bbb | (102,2,bbb) - 104 | 4 | dad | (104,4,dad) - 202 | 2 | bcd | (202,2,bcd) - 204 | 4 | def | (204,4,def) - 302 | 2 | yyy | (302,2,yyy) -(5 rows) - -EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1; - QUERY PLAN -------------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: ((a % 2) = 0) - -> Seq Scan on t2 t1_2 - Filter: ((a % 2) = 0) - -> Seq Scan on t3 t1_3 - Filter: ((a % 2) = 0) -(7 rows) - --- for share/update lock -SELECT * FROM t1 FOR SHARE; - id | a | b ------+---+----- - 102 | 2 | bbb - 104 | 4 | dad - 202 | 2 | bcd - 204 | 4 | def - 302 | 2 | yyy -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 FOR SHARE; - QUERY PLAN -------------------------------------- - LockRows - -> Append - -> Seq Scan on t1 t1_1 - Filter: ((a % 2) = 0) - -> Seq Scan on t2 t1_2 - Filter: ((a % 2) = 0) - -> Seq Scan on t3 t1_3 - Filter: ((a % 2) = 0) -(8 rows) - -SELECT * FROM t1 WHERE f_leak(b) FOR SHARE; -NOTICE: f_leak => bbb -NOTICE: f_leak => dad -NOTICE: f_leak => bcd -NOTICE: f_leak => def -NOTICE: f_leak => yyy - id | a | b ------+---+----- - 102 | 2 | bbb - 104 | 4 | dad - 202 | 2 | bcd - 204 | 4 | def - 302 | 2 | yyy -(5 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b) FOR SHARE; - QUERY PLAN ------------------------------------------------------ - LockRows - -> Append - -> Seq Scan on t1 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: (((a % 2) = 0) AND f_leak(b)) -(8 rows) - --- union all query -SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; - a | b | tableoid ----+-----+---------- - 1 | abc | t2 - 3 | cde | t2 - 1 | xxx | t3 - 2 | yyy | t3 - 3 | zzz | t3 -(5 rows) - -EXPLAIN (COSTS OFF) SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; - QUERY PLAN -------------------------------- - Append - -> Seq Scan on t2 - Filter: ((a % 2) = 1) - -> Seq Scan on t3 -(4 rows) - --- superuser is allowed to bypass RLS checks -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -SELECT * FROM t1 WHERE f_leak(b); -NOTICE: f_leak => aba -NOTICE: f_leak => bbb -NOTICE: f_leak => ccc -NOTICE: f_leak => dad -NOTICE: f_leak => abc -NOTICE: f_leak => bcd -NOTICE: f_leak => cde -NOTICE: f_leak => def -NOTICE: f_leak => xxx -NOTICE: f_leak => yyy -NOTICE: f_leak => zzz - id | a | b ------+---+----- - 101 | 1 | aba - 102 | 2 | bbb - 103 | 3 | ccc - 104 | 4 | dad - 201 | 1 | abc - 202 | 2 | bcd - 203 | 3 | cde - 204 | 4 | def - 301 | 1 | xxx - 302 | 2 | yyy - 303 | 3 | zzz -(11 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); - QUERY PLAN ---------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: f_leak(b) - -> Seq Scan on t2 t1_2 - Filter: f_leak(b) - -> Seq Scan on t3 t1_3 - Filter: f_leak(b) -(7 rows) - --- non-superuser with bypass privilege can bypass RLS policy when disabled -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO OFF; -SELECT * FROM t1 WHERE f_leak(b); -NOTICE: f_leak => aba -NOTICE: f_leak => bbb -NOTICE: f_leak => ccc -NOTICE: f_leak => dad -NOTICE: f_leak => abc -NOTICE: f_leak => bcd -NOTICE: f_leak => cde -NOTICE: f_leak => def -NOTICE: f_leak => xxx -NOTICE: f_leak => yyy -NOTICE: f_leak => zzz - id | a | b ------+---+----- - 101 | 1 | aba - 102 | 2 | bbb - 103 | 3 | ccc - 104 | 4 | dad - 201 | 1 | abc - 202 | 2 | bcd - 203 | 3 | cde - 204 | 4 | def - 301 | 1 | xxx - 302 | 2 | yyy - 303 | 3 | zzz -(11 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); - QUERY PLAN ---------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: f_leak(b) - -> Seq Scan on t2 t1_2 - Filter: f_leak(b) - -> Seq Scan on t3 t1_3 - Filter: f_leak(b) -(7 rows) - --- --- Partitioned Tables --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE part_document ( - did int, - cid int, - dlevel int not null, - dauthor name, - dtitle text -) PARTITION BY RANGE (cid); -GRANT ALL ON part_document TO public; --- Create partitions for document categories -CREATE TABLE part_document_fiction PARTITION OF part_document FOR VALUES FROM (11) to (12); -CREATE TABLE part_document_satire PARTITION OF part_document FOR VALUES FROM (55) to (56); -CREATE TABLE part_document_nonfiction PARTITION OF part_document FOR VALUES FROM (99) to (100); -GRANT ALL ON part_document_fiction TO public; -GRANT ALL ON part_document_satire TO public; -GRANT ALL ON part_document_nonfiction TO public; -INSERT INTO part_document VALUES - ( 1, 11, 1, 'regress_rls_bob', 'my first novel'), - ( 2, 11, 2, 'regress_rls_bob', 'my second novel'), - ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'), - ( 4, 55, 1, 'regress_rls_bob', 'my first satire'), - ( 5, 99, 2, 'regress_rls_bob', 'my history book'), - ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'), - ( 7, 99, 2, 'regress_rls_carol', 'great technology book'), - ( 8, 55, 2, 'regress_rls_carol', 'great satire'), - ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'), - (10, 99, 2, 'regress_rls_dave', 'awesome technology book'); -ALTER TABLE part_document ENABLE ROW LEVEL SECURITY; --- Create policy on parent --- user's security level must be higher than or equal to document's -CREATE POLICY pp1 ON part_document AS PERMISSIVE - USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); --- Dave is only allowed to see cid < 55 -CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave - USING (cid < 55); -\d+ part_document - Partitioned table "regress_rls_schema.part_document" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ----------+---------+-----------+----------+---------+----------+--------------+------------- - did | integer | | | | plain | | - cid | integer | | | | plain | | - dlevel | integer | | not null | | plain | | - dauthor | name | | | | plain | | - dtitle | text | | | | extended | | -Partition key: RANGE (cid) -Policies: - POLICY "pp1" - USING ((dlevel <= ( SELECT uaccount.seclv - FROM uaccount - WHERE (uaccount.pguser = CURRENT_USER)))) - POLICY "pp1r" AS RESTRICTIVE - TO regress_rls_dave - USING ((cid < 55)) -Partitions: part_document_fiction FOR VALUES FROM (11) TO (12), - part_document_nonfiction FOR VALUES FROM (99) TO (100), - part_document_satire FOR VALUES FROM (55) TO (56) - -SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname; - schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check ---------------------+---------------+------------+-------------+--------------------+-----+--------------------------------------------+------------ - regress_rls_schema | part_document | pp1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +| - | | | | | | FROM uaccount +| - | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) | - regress_rls_schema | part_document | pp1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid < 55) | -(2 rows) - --- viewpoint from regress_rls_bob -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO ON; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => my first satire - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 4 | 55 | 1 | regress_rls_bob | my first satire - 6 | 11 | 1 | regress_rls_carol | great science fiction - 9 | 11 | 1 | regress_rls_dave | awesome science fiction -(4 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- - Append - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) - -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) -(10 rows) - --- viewpoint from regress_rls_carol -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => my first satire -NOTICE: f_leak => great satire -NOTICE: f_leak => my science textbook -NOTICE: f_leak => my history book -NOTICE: f_leak => great technology book -NOTICE: f_leak => awesome technology book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire - 9 | 11 | 1 | regress_rls_dave | awesome science fiction - 10 | 99 | 2 | regress_rls_dave | awesome technology book -(10 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- - Append - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) - -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) -(10 rows) - --- viewpoint from regress_rls_dave -SET SESSION AUTHORIZATION regress_rls_dave; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 6 | 11 | 1 | regress_rls_carol | great science fiction - 9 | 11 | 1 | regress_rls_dave | awesome science fiction -(4 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- - Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) -(5 rows) - --- pp1 ERROR -INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail -ERROR: new row violates row-level security policy for table "part_document" --- pp1r ERROR -INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail -ERROR: new row violates row-level security policy "pp1r" for table "part_document" --- Show that RLS policy does not apply for direct inserts to children --- This should fail with RLS POLICY pp1r violation. -INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -ERROR: new row violates row-level security policy "pp1r" for table "part_document" --- But this should succeed. -INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success --- We still cannot see the row using the parent -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 6 | 11 | 1 | regress_rls_carol | great science fiction - 9 | 11 | 1 | regress_rls_dave | awesome science fiction -(4 rows) - --- But we can if we look directly -SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first satire -NOTICE: f_leak => great satire -NOTICE: f_leak => testing RLS with partitions - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 4 | 55 | 1 | regress_rls_bob | my first satire - 8 | 55 | 2 | regress_rls_carol | great satire - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(3 rows) - --- Turn on RLS and create policy on child to show RLS is checked before constraints -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY; -CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE - USING (cid < 55); --- This should fail with RLS violation now. -SET SESSION AUTHORIZATION regress_rls_dave; -INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -ERROR: new row violates row-level security policy for table "part_document_satire" --- And now we cannot see directly into the partition either, due to RLS -SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+---------+-------- -(0 rows) - --- The parent looks same as before --- viewpoint from regress_rls_dave -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 6 | 11 | 1 | regress_rls_carol | great science fiction - 9 | 11 | 1 | regress_rls_dave | awesome science fiction -(4 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- - Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) -(5 rows) - --- viewpoint from regress_rls_carol -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => great science fiction -NOTICE: f_leak => awesome science fiction -NOTICE: f_leak => my first satire -NOTICE: f_leak => great satire -NOTICE: f_leak => testing RLS with partitions -NOTICE: f_leak => my science textbook -NOTICE: f_leak => my history book -NOTICE: f_leak => great technology book -NOTICE: f_leak => awesome technology book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire - 9 | 11 | 1 | regress_rls_dave | awesome science fiction - 10 | 99 | 2 | regress_rls_dave | awesome technology book - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(11 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- - Append - InitPlan 1 (returns $0) - -> Index Scan using uaccount_pkey on uaccount - Index Cond: (pguser = CURRENT_USER) - -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) -(10 rows) - --- only owner can change policies -ALTER POLICY pp1 ON part_document USING (true); --fail -ERROR: must be owner of table part_document -DROP POLICY pp1 ON part_document; --fail -ERROR: must be owner of relation part_document -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER POLICY pp1 ON part_document USING (dauthor = current_user); --- viewpoint from regress_rls_bob again -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => my first novel -NOTICE: f_leak => my second novel -NOTICE: f_leak => my first satire -NOTICE: f_leak => my science textbook -NOTICE: f_leak => my history book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+--------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book -(5 rows) - --- viewpoint from rls_regres_carol again -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -NOTICE: f_leak => great science fiction -NOTICE: f_leak => great satire -NOTICE: f_leak => great technology book - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------- - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire -(3 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ---------------------------------------------------------------- - Append - -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle)) - -> Seq Scan on part_document_satire part_document_2 - Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle)) - -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle)) -(7 rows) - --- database superuser does bypass RLS policy when enabled -RESET SESSION AUTHORIZATION; -SET row_security TO ON; -SELECT * FROM part_document ORDER BY did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire - 9 | 11 | 1 | regress_rls_dave | awesome science fiction - 10 | 99 | 2 | regress_rls_dave | awesome technology book - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(11 rows) - -SELECT * FROM part_document_satire ORDER by did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 4 | 55 | 1 | regress_rls_bob | my first satire - 8 | 55 | 2 | regress_rls_carol | great satire - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(3 rows) - --- database non-superuser with bypass privilege can bypass RLS policy when disabled -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO OFF; -SELECT * FROM part_document ORDER BY did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire - 9 | 11 | 1 | regress_rls_dave | awesome science fiction - 10 | 99 | 2 | regress_rls_dave | awesome technology book - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(11 rows) - -SELECT * FROM part_document_satire ORDER by did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 4 | 55 | 1 | regress_rls_bob | my first satire - 8 | 55 | 2 | regress_rls_carol | great satire - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(3 rows) - --- RLS policy does not apply to table owner when RLS enabled. -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO ON; -SELECT * FROM part_document ORDER by did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 1 | 11 | 1 | regress_rls_bob | my first novel - 2 | 11 | 2 | regress_rls_bob | my second novel - 3 | 99 | 2 | regress_rls_bob | my science textbook - 4 | 55 | 1 | regress_rls_bob | my first satire - 5 | 99 | 2 | regress_rls_bob | my history book - 6 | 11 | 1 | regress_rls_carol | great science fiction - 7 | 99 | 2 | regress_rls_carol | great technology book - 8 | 55 | 2 | regress_rls_carol | great satire - 9 | 11 | 1 | regress_rls_dave | awesome science fiction - 10 | 99 | 2 | regress_rls_dave | awesome technology book - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(11 rows) - -SELECT * FROM part_document_satire ORDER by did; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-------------------+----------------------------- - 4 | 55 | 1 | regress_rls_bob | my first satire - 8 | 55 | 2 | regress_rls_carol | great satire - 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions -(3 rows) - --- When RLS disabled, other users get ERROR. -SET SESSION AUTHORIZATION regress_rls_dave; -SET row_security TO OFF; -SELECT * FROM part_document ORDER by did; -ERROR: query would be affected by row-level security policy for table "part_document" -SELECT * FROM part_document_satire ORDER by did; -ERROR: query would be affected by row-level security policy for table "part_document_satire" --- Check behavior with a policy that uses a SubPlan not an InitPlan. -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO ON; -CREATE POLICY pp3 ON part_document AS RESTRICTIVE - USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = current_user)); -SET SESSION AUTHORIZATION regress_rls_carol; -INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail -ERROR: new row violates row-level security policy "pp3" for table "part_document" ------ Dependencies ----- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security TO ON; -CREATE TABLE dependee (x integer, y integer); -CREATE TABLE dependent (x integer, y integer); -CREATE POLICY d1 ON dependent FOR ALL - TO PUBLIC - USING (x = (SELECT d.x FROM dependee d WHERE d.y = y)); -DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual? -ERROR: cannot drop table dependee because other objects depend on it -DETAIL: policy d1 on table dependent depends on table dependee -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE dependee CASCADE; -NOTICE: drop cascades to policy d1 on table dependent -EXPLAIN (COSTS OFF) SELECT * FROM dependent; -- After drop, should be unqualified - QUERY PLAN ------------------------ - Seq Scan on dependent -(1 row) - ------ RECURSION ---- --- --- Simple recursion --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE rec1 (x integer, y integer); -CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y)); -ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rec1; -- fail, direct recursion -ERROR: infinite recursion detected in policy for relation "rec1" --- --- Mutual recursion --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE rec2 (a integer, b integer); -ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2 WHERE b = y)); -CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b)); -ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rec1; -- fail, mutual recursion -ERROR: infinite recursion detected in policy for relation "rec1" --- --- Mutual recursion via views --- -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE VIEW rec1v AS SELECT * FROM rec1; -CREATE VIEW rec2v AS SELECT * FROM rec2; -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); -ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rec1; -- fail, mutual recursion via views -ERROR: infinite recursion detected in policy for relation "rec1" --- --- Mutual recursion via .s.b views --- -SET SESSION AUTHORIZATION regress_rls_bob; -DROP VIEW rec1v, rec2v CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to policy r1 on table rec1 -drop cascades to policy r2 on table rec2 -CREATE VIEW rec1v WITH (security_barrier) AS SELECT * FROM rec1; -CREATE VIEW rec2v WITH (security_barrier) AS SELECT * FROM rec2; -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); -CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rec1; -- fail, mutual recursion via s.b. views -ERROR: infinite recursion detected in policy for relation "rec1" --- --- recursive RLS and VIEWs in policy --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE s1 (a int, b text); -INSERT INTO s1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x); -CREATE TABLE s2 (x int, y text); -INSERT INTO s2 (SELECT x, md5(x::text) FROM generate_series(-6,6) x); -GRANT SELECT ON s1, s2 TO regress_rls_bob; -CREATE POLICY p1 ON s1 USING (a in (select x from s2 where y like '%2f%')); -CREATE POLICY p2 ON s2 USING (x in (select a from s1 where b like '%22%')); -CREATE POLICY p3 ON s1 FOR INSERT WITH CHECK (a = (SELECT a FROM s1)); -ALTER TABLE s1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE s2 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%'; -SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion) -ERROR: infinite recursion detected in policy for relation "s1" -INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion) -ERROR: infinite recursion detected in policy for relation "s1" -SET SESSION AUTHORIZATION regress_rls_alice; -DROP POLICY p3 on s1; -ALTER POLICY p2 ON s2 USING (x % 2 = 0); -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM s1 WHERE f_leak(b); -- OK -NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c - a | b ----+---------------------------------- - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ - Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 - -> Seq Scan on s2 - Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) -(5 rows) - -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER POLICY p1 ON s1 USING (a in (select x from v2)); -- using VIEW in RLS policy -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM s1 WHERE f_leak(b); -- OK -NOTICE: f_leak => 0267aaf632e87a63288a08331f22c7c3 -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc - a | b -----+---------------------------------- - -4 | 0267aaf632e87a63288a08331f22c7c3 - 6 | 1679091c5a880faf6fb5e6087eb1b2dc -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ - Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 - -> Seq Scan on s2 - Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) -(5 rows) - -SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; - xx | x | y -----+----+---------------------------------- - -6 | -6 | 596a3d04481816330f07e4f97510c28f - -4 | -4 | 0267aaf632e87a63288a08331f22c7c3 - 2 | 2 | c81e728d9d4c2f636f067f89cc14862c -(3 rows) - -EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; - QUERY PLAN -------------------------------------------------------------------------- - Seq Scan on s2 - Filter: (((x % 2) = 0) AND (y ~~ '%28%'::text)) - SubPlan 2 - -> Limit - -> Seq Scan on s1 - Filter: (hashed SubPlan 1) - SubPlan 1 - -> Seq Scan on s2 s2_1 - Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) -(9 rows) - -SET SESSION AUTHORIZATION regress_rls_alice; -ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%')); -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view) -ERROR: infinite recursion detected in policy for relation "s1" --- prepared statement with regress_rls_alice privilege -PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1; -EXECUTE p1(2); - id | a | b ------+---+----- - 102 | 2 | bbb - 202 | 2 | bcd - 302 | 2 | yyy -(3 rows) - -EXPLAIN (COSTS OFF) EXECUTE p1(2); - QUERY PLAN ----------------------------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: ((a <= 2) AND ((a % 2) = 0)) - -> Seq Scan on t2 t1_2 - Filter: ((a <= 2) AND ((a % 2) = 0)) - -> Seq Scan on t3 t1_3 - Filter: ((a <= 2) AND ((a % 2) = 0)) -(7 rows) - --- superuser is allowed to bypass RLS checks -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -SELECT * FROM t1 WHERE f_leak(b); -NOTICE: f_leak => aba -NOTICE: f_leak => bbb -NOTICE: f_leak => ccc -NOTICE: f_leak => dad -NOTICE: f_leak => abc -NOTICE: f_leak => bcd -NOTICE: f_leak => cde -NOTICE: f_leak => def -NOTICE: f_leak => xxx -NOTICE: f_leak => yyy -NOTICE: f_leak => zzz - id | a | b ------+---+----- - 101 | 1 | aba - 102 | 2 | bbb - 103 | 3 | ccc - 104 | 4 | dad - 201 | 1 | abc - 202 | 2 | bcd - 203 | 3 | cde - 204 | 4 | def - 301 | 1 | xxx - 302 | 2 | yyy - 303 | 3 | zzz -(11 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); - QUERY PLAN ---------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: f_leak(b) - -> Seq Scan on t2 t1_2 - Filter: f_leak(b) - -> Seq Scan on t3 t1_3 - Filter: f_leak(b) -(7 rows) - --- plan cache should be invalidated -EXECUTE p1(2); - id | a | b ------+---+----- - 101 | 1 | aba - 102 | 2 | bbb - 201 | 1 | abc - 202 | 2 | bcd - 301 | 1 | xxx - 302 | 2 | yyy -(6 rows) - -EXPLAIN (COSTS OFF) EXECUTE p1(2); - QUERY PLAN ---------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: (a <= 2) - -> Seq Scan on t2 t1_2 - Filter: (a <= 2) - -> Seq Scan on t3 t1_3 - Filter: (a <= 2) -(7 rows) - -PREPARE p2(int) AS SELECT * FROM t1 WHERE a = $1; -EXECUTE p2(2); - id | a | b ------+---+----- - 102 | 2 | bbb - 202 | 2 | bcd - 302 | 2 | yyy -(3 rows) - -EXPLAIN (COSTS OFF) EXECUTE p2(2); - QUERY PLAN ---------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: (a = 2) - -> Seq Scan on t2 t1_2 - Filter: (a = 2) - -> Seq Scan on t3 t1_3 - Filter: (a = 2) -(7 rows) - --- also, case when privilege switch from superuser -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO ON; -EXECUTE p2(2); - id | a | b ------+---+----- - 102 | 2 | bbb - 202 | 2 | bcd - 302 | 2 | yyy -(3 rows) - -EXPLAIN (COSTS OFF) EXECUTE p2(2); - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on t1 t1_1 - Filter: ((a = 2) AND ((a % 2) = 0)) - -> Seq Scan on t2 t1_2 - Filter: ((a = 2) AND ((a % 2) = 0)) - -> Seq Scan on t3 t1_3 - Filter: ((a = 2) AND ((a % 2) = 0)) -(7 rows) - --- --- UPDATE / DELETE and Row-level security --- -SET SESSION AUTHORIZATION regress_rls_bob; -EXPLAIN (COSTS OFF) UPDATE t1 SET b = b || b WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ - Update on t1 - Update on t1 t1_1 - Update on t2 t1_2 - Update on t3 t1_3 - -> Result - -> Append - -> Seq Scan on t1 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: (((a % 2) = 0) AND f_leak(b)) -(12 rows) - -UPDATE t1 SET b = b || b WHERE f_leak(b); -NOTICE: f_leak => bbb -NOTICE: f_leak => dad -NOTICE: f_leak => bcd -NOTICE: f_leak => def -NOTICE: f_leak => yyy -EXPLAIN (COSTS OFF) UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b); - QUERY PLAN ------------------------------------------------ - Update on t1 - -> Seq Scan on t1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(3 rows) - -UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b); -NOTICE: f_leak => bbbbbb -NOTICE: f_leak => daddad --- returning clause with system column -UPDATE only t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -NOTICE: f_leak => bbbbbb_updt -NOTICE: f_leak => daddad_updt - tableoid | id | a | b | t1 -----------+-----+---+-------------+--------------------- - t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt) - t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt) -(2 rows) - -UPDATE t1 SET b = b WHERE f_leak(b) RETURNING *; -NOTICE: f_leak => bbbbbb_updt -NOTICE: f_leak => daddad_updt -NOTICE: f_leak => bcdbcd -NOTICE: f_leak => defdef -NOTICE: f_leak => yyyyyy - id | a | b ------+---+------------- - 102 | 2 | bbbbbb_updt - 104 | 4 | daddad_updt - 202 | 2 | bcdbcd - 204 | 4 | defdef - 302 | 2 | yyyyyy -(5 rows) - -UPDATE t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -NOTICE: f_leak => bbbbbb_updt -NOTICE: f_leak => daddad_updt -NOTICE: f_leak => bcdbcd -NOTICE: f_leak => defdef -NOTICE: f_leak => yyyyyy - tableoid | id | a | b | t1 -----------+-----+---+-------------+--------------------- - t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt) - t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt) - t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd) - t2 | 204 | 4 | defdef | (204,4,defdef) - t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy) -(5 rows) - --- updates with from clause -EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3 -WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b); - QUERY PLAN ------------------------------------------------------------------ - Update on t2 - -> Nested Loop - -> Seq Scan on t2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) - -> Seq Scan on t3 - Filter: ((a = 2) AND f_leak(b)) -(6 rows) - -UPDATE t2 SET b=t2.b FROM t3 -WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b); -NOTICE: f_leak => cde -NOTICE: f_leak => yyyyyy -EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2 -WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); - QUERY PLAN ------------------------------------------------------------------------ - Update on t1 - Update on t1 t1_1 - Update on t2 t1_2 - Update on t3 t1_3 - -> Nested Loop - -> Seq Scan on t2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) - -> Append - -> Seq Scan on t1 t1_1 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) -(14 rows) - -UPDATE t1 SET b=t1.b FROM t2 -WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); -NOTICE: f_leak => cde -EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1 -WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); - QUERY PLAN ------------------------------------------------------------------------ - Update on t2 - -> Nested Loop - -> Seq Scan on t2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) - -> Append - -> Seq Scan on t1 t1_1 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) -(11 rows) - -UPDATE t2 SET b=t2.b FROM t1 -WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); -NOTICE: f_leak => cde --- updates with from clause self join -EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 -WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b -AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2; - QUERY PLAN ------------------------------------------------------------------ - Update on t2 t2_1 - -> Nested Loop - Join Filter: (t2_1.b = t2_2.b) - -> Seq Scan on t2 t2_1 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) - -> Seq Scan on t2 t2_2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) -(7 rows) - -UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 -WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b -AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2; -NOTICE: f_leak => cde -NOTICE: f_leak => cde - id | a | b | c | id | a | b | c | t2_1 | t2_2 ------+---+-----+-----+-----+---+-----+-----+-----------------+----------------- - 203 | 3 | cde | 3.3 | 203 | 3 | cde | 3.3 | (203,3,cde,3.3) | (203,3,cde,3.3) -(1 row) - -EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 -WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b -AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; - QUERY PLAN ------------------------------------------------------------------------------ - Update on t1 t1_1 - Update on t1 t1_1_1 - Update on t2 t1_1_2 - Update on t3 t1_1_3 - -> Nested Loop - Join Filter: (t1_1.b = t1_2.b) - -> Append - -> Seq Scan on t1 t1_1_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_1_2 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_1_3 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Materialize - -> Append - -> Seq Scan on t1 t1_2_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_2 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_3 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -(21 rows) - -UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 -WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b -AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; -NOTICE: f_leak => daddad_updt -NOTICE: f_leak => daddad_updt -NOTICE: f_leak => defdef -NOTICE: f_leak => defdef - id | a | b | id | a | b | t1_1 | t1_2 ------+---+-------------+-----+---+-------------+---------------------+--------------------- - 104 | 4 | daddad_updt | 104 | 4 | daddad_updt | (104,4,daddad_updt) | (104,4,daddad_updt) - 204 | 4 | defdef | 204 | 4 | defdef | (204,4,defdef) | (204,4,defdef) -(2 rows) - -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -SELECT * FROM t1 ORDER BY a,b; - id | a | b ------+---+------------- - 101 | 1 | aba - 201 | 1 | abc - 301 | 1 | xxx - 102 | 2 | bbbbbb_updt - 202 | 2 | bcdbcd - 302 | 2 | yyyyyy - 103 | 3 | ccc - 203 | 3 | cde - 303 | 3 | zzz - 104 | 4 | daddad_updt - 204 | 4 | defdef -(11 rows) - -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO ON; -EXPLAIN (COSTS OFF) DELETE FROM only t1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------ - Delete on t1 - -> Seq Scan on t1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(3 rows) - -EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------ - Delete on t1 - Delete on t1 t1_1 - Delete on t2 t1_2 - Delete on t3 t1_3 - -> Append - -> Seq Scan on t1 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_3 - Filter: (((a % 2) = 0) AND f_leak(b)) -(11 rows) - -DELETE FROM only t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -NOTICE: f_leak => bbbbbb_updt -NOTICE: f_leak => daddad_updt - tableoid | id | a | b | t1 -----------+-----+---+-------------+--------------------- - t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt) - t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt) -(2 rows) - -DELETE FROM t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -NOTICE: f_leak => bcdbcd -NOTICE: f_leak => defdef -NOTICE: f_leak => yyyyyy - tableoid | id | a | b | t1 -----------+-----+---+--------+---------------- - t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd) - t2 | 204 | 4 | defdef | (204,4,defdef) - t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy) -(3 rows) - --- --- S.b. view on top of Row-level security --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE b1 (a int, b text); -INSERT INTO b1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x); -CREATE POLICY p1 ON b1 USING (a % 2 = 0); -ALTER TABLE b1 ENABLE ROW LEVEL SECURITY; -GRANT ALL ON b1 TO regress_rls_bob; -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION; -GRANT ALL ON bv1 TO regress_rls_carol; -SET SESSION AUTHORIZATION regress_rls_carol; -EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b); - QUERY PLAN ---------------------------------------------- - Subquery Scan on bv1 - Filter: f_leak(bv1.b) - -> Seq Scan on b1 - Filter: ((a > 0) AND ((a % 2) = 0)) -(4 rows) - -SELECT * FROM bv1 WHERE f_leak(b); -NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc -NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d -NOTICE: f_leak => d3d9446802a44259755d38e6d163e820 - a | b -----+---------------------------------- - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 -(5 rows) - -INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO -ERROR: new row violates row-level security policy for table "b1" -INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check -ERROR: new row violates row-level security policy for table "b1" -INSERT INTO bv1 VALUES (12, 'xxx'); -- ok -EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b); - QUERY PLAN ------------------------------------------------------------------------ - Update on b1 - -> Seq Scan on b1 - Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b)) -(3 rows) - -UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b); -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c -EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b); - QUERY PLAN ------------------------------------------------------------------------ - Delete on b1 - -> Seq Scan on b1 - Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0) AND f_leak(b)) -(3 rows) - -DELETE FROM bv1 WHERE a = 6 AND f_leak(b); -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM b1; - a | b ------+---------------------------------- - -10 | 1b0fd9efa5279c4203b7c70233f86dbf - -9 | 252e691406782824eec43d7eadc3d256 - -8 | a8d2ec85eaf98407310b72eb73dda247 - -7 | 74687a12d3915d3c4d83f1af7b3683d5 - -6 | 596a3d04481816330f07e4f97510c28f - -5 | 47c1b025fa18ea96c33fbb6718688c0f - -4 | 0267aaf632e87a63288a08331f22c7c3 - -3 | b3149ecea4628efd23d2f86e5a723472 - -2 | 5d7b9adcbe1c629ec722529dd12e5129 - -1 | 6bb61e3b7bce0931da574d19d1d82c88 - 0 | cfcd208495d565ef66e7dff9f98764da - 1 | c4ca4238a0b923820dcc509a6f75849b - 2 | c81e728d9d4c2f636f067f89cc14862c - 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 - 5 | e4da3b7fbbce2345d7772b0674a318d5 - 7 | 8f14e45fceea167a5a36dedd4bea2543 - 8 | c9f0f895fb98ab9159f51fd0297e236d - 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 - 10 | d3d9446802a44259755d38e6d163e820 - 12 | xxx - 4 | yyy -(21 rows) - --- --- INSERT ... ON CONFLICT DO UPDATE and Row-level security --- -SET SESSION AUTHORIZATION regress_rls_alice; -DROP POLICY p1 ON document; -DROP POLICY p1r ON document; -CREATE POLICY p1 ON document FOR SELECT USING (true); -CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user); -CREATE POLICY p3 ON document FOR UPDATE - USING (cid = (SELECT cid from category WHERE cname = 'novel')) - WITH CHECK (dauthor = current_user); -SET SESSION AUTHORIZATION regress_rls_bob; --- Exists... -SELECT * FROM document WHERE did = 2; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+----------------- - 2 | 11 | 2 | regress_rls_bob | my second novel -(1 row) - --- ...so violates actual WITH CHECK OPTION within UPDATE (not INSERT, since --- alternative UPDATE path happens to be taken): -INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor; -ERROR: new row violates row-level security policy for table "document" --- Violates USING qual for UPDATE policy p3. --- --- UPDATE path is taken, but UPDATE fails purely because *existing* row to be --- updated is not a "novel"/cid 11 (row is not leaked, even though we have --- SELECT privileges sufficient to see the row in this instance): -INSERT INTO document VALUES (33, 22, 1, 'regress_rls_bob', 'okay science fiction'); -- preparation for next statement -INSERT INTO document VALUES (33, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'Some novel, replaces sci-fi') -- takes UPDATE path - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -ERROR: new row violates row-level security policy (USING expression) for table "document" --- Fine (we UPDATE, since INSERT WCOs and UPDATE security barrier quals + WCOs --- not violated): -INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+---------------- - 2 | 11 | 2 | regress_rls_bob | my first novel -(1 row) - --- Fine (we INSERT, so "cid = 33" ("technology") isn't evaluated): -INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+----------------------- - 78 | 11 | 1 | regress_rls_bob | some technology novel -(1 row) - --- Fine (same query, but we UPDATE, so "cid = 33", ("technology") is not the --- case in respect of *existing* tuple): -INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+----------------------- - 78 | 33 | 1 | regress_rls_bob | some technology novel -(1 row) - --- Same query a third time, but now fails due to existing tuple finally not --- passing quals: -INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -ERROR: new row violates row-level security policy (USING expression) for table "document" --- Don't fail just because INSERT doesn't satisfy WITH CHECK option that --- originated as a barrier/USING() qual from the UPDATE. Note that the UPDATE --- path *isn't* taken, and so UPDATE-related policy does not apply: -INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; - did | cid | dlevel | dauthor | dtitle ------+-----+--------+-----------------+---------------------------------- - 79 | 33 | 1 | regress_rls_bob | technology book, can only insert -(1 row) - --- But this time, the same statement fails, because the UPDATE path is taken, --- and updating the row just inserted falls afoul of security barrier qual --- (enforced as WCO) -- what we might have updated target tuple to is --- irrelevant, in fact. -INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -ERROR: new row violates row-level security policy (USING expression) for table "document" --- Test default USING qual enforced as WCO -SET SESSION AUTHORIZATION regress_rls_alice; -DROP POLICY p1 ON document; -DROP POLICY p2 ON document; -DROP POLICY p3 ON document; -CREATE POLICY p3_with_default ON document FOR UPDATE - USING (cid = (SELECT cid from category WHERE cname = 'novel')); -SET SESSION AUTHORIZATION regress_rls_bob; --- Just because WCO-style enforcement of USING quals occurs with --- existing/target tuple does not mean that the implementation can be allowed --- to fail to also enforce this qual against the final tuple appended to --- relation (since in the absence of an explicit WCO, this is also interpreted --- as an UPDATE/ALL WCO in general). --- --- UPDATE path is taken here (fails due to existing tuple). Note that this is --- not reported as a "USING expression", because it's an RLS UPDATE check that originated as --- a USING qual for the purposes of RLS in general, as opposed to an explicit --- USING qual that is ordinarily a security barrier. We leave it up to the --- UPDATE to make this fail: -INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -ERROR: new row violates row-level security policy for table "document" --- UPDATE path is taken here. Existing tuple passes, since its cid --- corresponds to "novel", but default USING qual is enforced against --- post-UPDATE tuple too (as always when updating with a policy that lacks an --- explicit WCO), and so this fails: -INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'my first novel') - ON CONFLICT (did) DO UPDATE SET cid = EXCLUDED.cid, dtitle = EXCLUDED.dtitle RETURNING *; -ERROR: new row violates row-level security policy for table "document" -SET SESSION AUTHORIZATION regress_rls_alice; -DROP POLICY p3_with_default ON document; --- --- Test ALL policies with ON CONFLICT DO UPDATE (much the same as existing UPDATE --- tests) --- -CREATE POLICY p3_with_all ON document FOR ALL - USING (cid = (SELECT cid from category WHERE cname = 'novel')) - WITH CHECK (dauthor = current_user); -SET SESSION AUTHORIZATION regress_rls_bob; --- Fails, since ALL WCO is enforced in insert path: -INSERT INTO document VALUES (80, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33; -ERROR: new row violates row-level security policy for table "document" --- Fails, since ALL policy USING qual is enforced (existing, target tuple is in --- violation, since it has the "manga" cid): -INSERT INTO document VALUES (4, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') - ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -ERROR: new row violates row-level security policy (USING expression) for table "document" --- Fails, since ALL WCO are enforced: -INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') - ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol'; -ERROR: new row violates row-level security policy for table "document" --- --- ROLE/GROUP --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE z1 (a int, b text); -CREATE TABLE z2 (a int, b text); -GRANT SELECT ON z1,z2 TO regress_rls_group1, regress_rls_group2, - regress_rls_bob, regress_rls_carol; -INSERT INTO z1 VALUES - (1, 'aba'), - (2, 'bbb'), - (3, 'ccc'), - (4, 'dad'); -CREATE POLICY p1 ON z1 TO regress_rls_group1 USING (a % 2 = 0); -CREATE POLICY p2 ON z1 TO regress_rls_group2 USING (a % 2 = 1); -ALTER TABLE z1 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM z1 WHERE f_leak(b); -NOTICE: f_leak => bbb -NOTICE: f_leak => dad - a | b ----+----- - 2 | bbb - 4 | dad -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - -PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b); -EXPLAIN (COSTS OFF) EXECUTE plancache_test; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - -PREPARE plancache_test2 AS WITH q AS MATERIALIZED (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2; -EXPLAIN (COSTS OFF) EXECUTE plancache_test2; - QUERY PLAN -------------------------------------------------- - Nested Loop - CTE q - -> Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> CTE Scan on q - -> Materialize - -> Seq Scan on z2 -(7 rows) - -PREPARE plancache_test3 AS WITH q AS MATERIALIZED (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b); -EXPLAIN (COSTS OFF) EXECUTE plancache_test3; - QUERY PLAN ------------------------------------------------------ - Nested Loop - CTE q - -> Seq Scan on z2 - -> CTE Scan on q - -> Materialize - -> Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(7 rows) - -SET ROLE regress_rls_group1; -SELECT * FROM z1 WHERE f_leak(b); -NOTICE: f_leak => bbb -NOTICE: f_leak => dad - a | b ----+----- - 2 | bbb - 4 | dad -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test2; - QUERY PLAN -------------------------------------------------- - Nested Loop - CTE q - -> Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> CTE Scan on q - -> Materialize - -> Seq Scan on z2 -(7 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test3; - QUERY PLAN ------------------------------------------------------ - Nested Loop - CTE q - -> Seq Scan on z2 - -> CTE Scan on q - -> Materialize - -> Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(7 rows) - -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM z1 WHERE f_leak(b); -NOTICE: f_leak => aba -NOTICE: f_leak => ccc - a | b ----+----- - 1 | aba - 3 | ccc -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test2; - QUERY PLAN -------------------------------------------------- - Nested Loop - CTE q - -> Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) - -> CTE Scan on q - -> Materialize - -> Seq Scan on z2 -(7 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test3; - QUERY PLAN ------------------------------------------------------ - Nested Loop - CTE q - -> Seq Scan on z2 - -> CTE Scan on q - -> Materialize - -> Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(7 rows) - -SET ROLE regress_rls_group2; -SELECT * FROM z1 WHERE f_leak(b); -NOTICE: f_leak => aba -NOTICE: f_leak => ccc - a | b ----+----- - 1 | aba - 3 | ccc -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(2 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test2; - QUERY PLAN -------------------------------------------------- - Nested Loop - CTE q - -> Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) - -> CTE Scan on q - -> Materialize - -> Seq Scan on z2 -(7 rows) - -EXPLAIN (COSTS OFF) EXECUTE plancache_test3; - QUERY PLAN ------------------------------------------------------ - Nested Loop - CTE q - -> Seq Scan on z2 - -> CTE Scan on q - -> Materialize - -> Seq Scan on z1 - Filter: (((a % 2) = 1) AND f_leak(b)) -(7 rows) - --- --- Views should follow policy for view owner. --- --- View and Table owner are the same. -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b); -GRANT SELECT ON rls_view TO regress_rls_bob; --- Query as role that is not owner of view or table. Should return all records. -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rls_view; -NOTICE: f_leak => aba -NOTICE: f_leak => bbb -NOTICE: f_leak => ccc -NOTICE: f_leak => dad - a | b ----+----- - 1 | aba - 2 | bbb - 3 | ccc - 4 | dad -(4 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ---------------------- - Seq Scan on z1 - Filter: f_leak(b) -(2 rows) - --- Query as view/table owner. Should return all records. -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM rls_view; -NOTICE: f_leak => aba -NOTICE: f_leak => bbb -NOTICE: f_leak => ccc -NOTICE: f_leak => dad - a | b ----+----- - 1 | aba - 2 | bbb - 3 | ccc - 4 | dad -(4 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ---------------------- - Seq Scan on z1 - Filter: f_leak(b) -(2 rows) - -DROP VIEW rls_view; --- View and Table owners are different. -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b); -GRANT SELECT ON rls_view TO regress_rls_alice; --- Query as role that is not owner of view but is owner of table. --- Should return records based on view owner policies. -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM rls_view; -NOTICE: f_leak => bbb -NOTICE: f_leak => dad - a | b ----+----- - 2 | bbb - 4 | dad -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - --- Query as role that is not owner of table but is owner of view. --- Should return records based on view owner policies. -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM rls_view; -NOTICE: f_leak => bbb -NOTICE: f_leak => dad - a | b ----+----- - 2 | bbb - 4 | dad -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - --- Query as role that is not the owner of the table or view without permissions. -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM rls_view; --fail - permission denied. -ERROR: permission denied for view rls_view -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied. -ERROR: permission denied for view rls_view --- Query as role that is not the owner of the table or view with permissions. -SET SESSION AUTHORIZATION regress_rls_bob; -GRANT SELECT ON rls_view TO regress_rls_carol; -SELECT * FROM rls_view; -NOTICE: f_leak => bbb -NOTICE: f_leak => dad - a | b ----+----- - 2 | bbb - 4 | dad -(2 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ------------------------------------------ - Seq Scan on z1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(2 rows) - -SET SESSION AUTHORIZATION regress_rls_bob; -DROP VIEW rls_view; --- --- Command specific --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE x1 (a int, b text, c text); -GRANT ALL ON x1 TO PUBLIC; -INSERT INTO x1 VALUES - (1, 'abc', 'regress_rls_bob'), - (2, 'bcd', 'regress_rls_bob'), - (3, 'cde', 'regress_rls_carol'), - (4, 'def', 'regress_rls_carol'), - (5, 'efg', 'regress_rls_bob'), - (6, 'fgh', 'regress_rls_bob'), - (7, 'fgh', 'regress_rls_carol'), - (8, 'fgh', 'regress_rls_carol'); -CREATE POLICY p0 ON x1 FOR ALL USING (c = current_user); -CREATE POLICY p1 ON x1 FOR SELECT USING (a % 2 = 0); -CREATE POLICY p2 ON x1 FOR INSERT WITH CHECK (a % 2 = 1); -CREATE POLICY p3 ON x1 FOR UPDATE USING (a % 2 = 0); -CREATE POLICY p4 ON x1 FOR DELETE USING (a < 8); -ALTER TABLE x1 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC; -NOTICE: f_leak => abc -NOTICE: f_leak => bcd -NOTICE: f_leak => def -NOTICE: f_leak => efg -NOTICE: f_leak => fgh -NOTICE: f_leak => fgh - a | b | c ----+-----+------------------- - 1 | abc | regress_rls_bob - 2 | bcd | regress_rls_bob - 4 | def | regress_rls_carol - 5 | efg | regress_rls_bob - 6 | fgh | regress_rls_bob - 8 | fgh | regress_rls_carol -(6 rows) - -UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *; -NOTICE: f_leak => abc -NOTICE: f_leak => bcd -NOTICE: f_leak => def -NOTICE: f_leak => efg -NOTICE: f_leak => fgh -NOTICE: f_leak => fgh - a | b | c ----+----------+------------------- - 1 | abc_updt | regress_rls_bob - 2 | bcd_updt | regress_rls_bob - 4 | def_updt | regress_rls_carol - 5 | efg_updt | regress_rls_bob - 6 | fgh_updt | regress_rls_bob - 8 | fgh_updt | regress_rls_carol -(6 rows) - -SET SESSION AUTHORIZATION regress_rls_carol; -SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC; -NOTICE: f_leak => cde -NOTICE: f_leak => fgh -NOTICE: f_leak => bcd_updt -NOTICE: f_leak => def_updt -NOTICE: f_leak => fgh_updt -NOTICE: f_leak => fgh_updt - a | b | c ----+----------+------------------- - 2 | bcd_updt | regress_rls_bob - 3 | cde | regress_rls_carol - 4 | def_updt | regress_rls_carol - 6 | fgh_updt | regress_rls_bob - 7 | fgh | regress_rls_carol - 8 | fgh_updt | regress_rls_carol -(6 rows) - -UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *; -NOTICE: f_leak => cde -NOTICE: f_leak => fgh -NOTICE: f_leak => bcd_updt -NOTICE: f_leak => def_updt -NOTICE: f_leak => fgh_updt -NOTICE: f_leak => fgh_updt - a | b | c ----+---------------+------------------- - 3 | cde_updt | regress_rls_carol - 7 | fgh_updt | regress_rls_carol - 2 | bcd_updt_updt | regress_rls_bob - 4 | def_updt_updt | regress_rls_carol - 6 | fgh_updt_updt | regress_rls_bob - 8 | fgh_updt_updt | regress_rls_carol -(6 rows) - -DELETE FROM x1 WHERE f_leak(b) RETURNING *; -NOTICE: f_leak => cde_updt -NOTICE: f_leak => fgh_updt -NOTICE: f_leak => bcd_updt_updt -NOTICE: f_leak => def_updt_updt -NOTICE: f_leak => fgh_updt_updt -NOTICE: f_leak => fgh_updt_updt - a | b | c ----+---------------+------------------- - 3 | cde_updt | regress_rls_carol - 7 | fgh_updt | regress_rls_carol - 2 | bcd_updt_updt | regress_rls_bob - 4 | def_updt_updt | regress_rls_carol - 6 | fgh_updt_updt | regress_rls_bob - 8 | fgh_updt_updt | regress_rls_carol -(6 rows) - --- --- Duplicate Policy Names --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE y1 (a int, b text); -CREATE TABLE y2 (a int, b text); -GRANT ALL ON y1, y2 TO regress_rls_bob; -CREATE POLICY p1 ON y1 FOR ALL USING (a % 2 = 0); -CREATE POLICY p2 ON y1 FOR SELECT USING (a > 2); -CREATE POLICY p1 ON y1 FOR SELECT USING (a % 2 = 1); --fail -ERROR: policy "p1" for table "y1" already exists -CREATE POLICY p1 ON y2 FOR ALL USING (a % 2 = 0); --OK -ALTER TABLE y1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE y2 ENABLE ROW LEVEL SECURITY; --- --- Expression structure with SBV --- --- Create view as table owner. RLS should NOT be applied. -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE VIEW rls_sbv WITH (security_barrier) AS - SELECT * FROM y1 WHERE f_leak(b); -EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1); - QUERY PLAN ------------------------------------ - Seq Scan on y1 - Filter: (f_leak(b) AND (a = 1)) -(2 rows) - -DROP VIEW rls_sbv; --- Create view as role that does not own table. RLS should be applied. -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE VIEW rls_sbv WITH (security_barrier) AS - SELECT * FROM y1 WHERE f_leak(b); -EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1); - QUERY PLAN ------------------------------------------------------------------- - Seq Scan on y1 - Filter: ((a = 1) AND ((a > 2) OR ((a % 2) = 0)) AND f_leak(b)) -(2 rows) - -DROP VIEW rls_sbv; --- --- Expression structure --- -SET SESSION AUTHORIZATION regress_rls_alice; -INSERT INTO y2 (SELECT x, md5(x::text) FROM generate_series(0,20) x); -CREATE POLICY p2 ON y2 USING (a % 3 = 0); -CREATE POLICY p3 ON y2 USING (a % 4 = 0); -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM y2 WHERE f_leak(b); -NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da -NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c -NOTICE: f_leak => eccbc87e4b5ce2fe28308fd9f2a7baf3 -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc -NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d -NOTICE: f_leak => 45c48cce2e2d7fbdea1afc51c7c6ad26 -NOTICE: f_leak => d3d9446802a44259755d38e6d163e820 -NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710 -NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56 -NOTICE: f_leak => 9bf31c7ff062936a96d3c8bd1f8f2ff3 -NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf -NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23 -NOTICE: f_leak => 98f13708210194c475687be6106a3b84 - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 -(14 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------------------------ - Seq Scan on y2 - Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b)) -(2 rows) - --- --- Qual push-down of leaky functions, when not referring to table --- -SELECT * FROM y2 WHERE f_leak('abc'); -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc -NOTICE: f_leak => abc - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 -(14 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak('abc'); - QUERY PLAN ---------------------------------------------------------------------------------------- - Seq Scan on y2 - Filter: (f_leak('abc'::text) AND (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0))) -(2 rows) - -CREATE TABLE test_qual_pushdown ( - abc text -); -INSERT INTO test_qual_pushdown VALUES ('abc'),('def'); -SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc); -NOTICE: f_leak => abc -NOTICE: f_leak => def - a | b | abc ----+---+----- -(0 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc); - QUERY PLAN -------------------------------------------------------------------------- - Hash Join - Hash Cond: (test_qual_pushdown.abc = y2.b) - -> Seq Scan on test_qual_pushdown - Filter: f_leak(abc) - -> Hash - -> Seq Scan on y2 - Filter: (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) -(7 rows) - -SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b); -NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da -NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c -NOTICE: f_leak => eccbc87e4b5ce2fe28308fd9f2a7baf3 -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc -NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d -NOTICE: f_leak => 45c48cce2e2d7fbdea1afc51c7c6ad26 -NOTICE: f_leak => d3d9446802a44259755d38e6d163e820 -NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710 -NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56 -NOTICE: f_leak => 9bf31c7ff062936a96d3c8bd1f8f2ff3 -NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf -NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23 -NOTICE: f_leak => 98f13708210194c475687be6106a3b84 - a | b | abc ----+---+----- -(0 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------------------------------------ - Hash Join - Hash Cond: (test_qual_pushdown.abc = y2.b) - -> Seq Scan on test_qual_pushdown - -> Hash - -> Seq Scan on y2 - Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b)) -(6 rows) - -DROP TABLE test_qual_pushdown; --- --- Plancache invalidate on user change. --- -RESET SESSION AUTHORIZATION; -DROP TABLE t1 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table t2 -drop cascades to table t3 -CREATE TABLE t1 (a integer); -GRANT SELECT ON t1 TO regress_rls_bob, regress_rls_carol; -CREATE POLICY p1 ON t1 TO regress_rls_bob USING ((a % 2) = 0); -CREATE POLICY p2 ON t1 TO regress_rls_carol USING ((a % 4) = 0); -ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; --- Prepare as regress_rls_bob -SET ROLE regress_rls_bob; -PREPARE role_inval AS SELECT * FROM t1; --- Check plan -EXPLAIN (COSTS OFF) EXECUTE role_inval; - QUERY PLAN -------------------------- - Seq Scan on t1 - Filter: ((a % 2) = 0) -(2 rows) - --- Change to regress_rls_carol -SET ROLE regress_rls_carol; --- Check plan- should be different -EXPLAIN (COSTS OFF) EXECUTE role_inval; - QUERY PLAN -------------------------- - Seq Scan on t1 - Filter: ((a % 4) = 0) -(2 rows) - --- Change back to regress_rls_bob -SET ROLE regress_rls_bob; --- Check plan- should be back to original -EXPLAIN (COSTS OFF) EXECUTE role_inval; - QUERY PLAN -------------------------- - Seq Scan on t1 - Filter: ((a % 2) = 0) -(2 rows) - --- --- CTE and RLS --- -RESET SESSION AUTHORIZATION; -DROP TABLE t1 CASCADE; -CREATE TABLE t1 (a integer, b text); -CREATE POLICY p1 ON t1 USING (a % 2 = 0); -ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; -GRANT ALL ON t1 TO regress_rls_bob; -INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(0,20) x); -SET SESSION AUTHORIZATION regress_rls_bob; -WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1; -NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da -NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c -NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c -NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc -NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d -NOTICE: f_leak => d3d9446802a44259755d38e6d163e820 -NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710 -NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56 -NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf -NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23 -NOTICE: f_leak => 98f13708210194c475687be6106a3b84 - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 -(11 rows) - -EXPLAIN (COSTS OFF) -WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1; - QUERY PLAN -------------------------------------------------- - CTE Scan on cte1 - CTE cte1 - -> Seq Scan on t1 - Filter: (((a % 2) = 0) AND f_leak(b)) -(4 rows) - -WITH cte1 AS (UPDATE t1 SET a = a + 1 RETURNING *) SELECT * FROM cte1; --fail -ERROR: new row violates row-level security policy for table "t1" -WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 -(11 rows) - -WITH cte1 AS (INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail -ERROR: new row violates row-level security policy for table "t1" -WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok - a | b -----+--------- - 20 | Success -(1 row) - --- --- Rename Policy --- -RESET SESSION AUTHORIZATION; -ALTER POLICY p1 ON t1 RENAME TO p1; --fail -ERROR: policy "p1" for table "t1" already exists -SELECT polname, relname - FROM pg_policy pol - JOIN pg_class pc ON (pc.oid = pol.polrelid) - WHERE relname = 't1'; - polname | relname ----------+--------- - p1 | t1 -(1 row) - -ALTER POLICY p1 ON t1 RENAME TO p2; --ok -SELECT polname, relname - FROM pg_policy pol - JOIN pg_class pc ON (pc.oid = pol.polrelid) - WHERE relname = 't1'; - polname | relname ----------+--------- - p2 | t1 -(1 row) - --- --- Check INSERT SELECT --- -SET SESSION AUTHORIZATION regress_rls_bob; -CREATE TABLE t2 (a integer, b text); -INSERT INTO t2 (SELECT * FROM t1); -EXPLAIN (COSTS OFF) INSERT INTO t2 (SELECT * FROM t1); - QUERY PLAN -------------------------------- - Insert on t2 - -> Seq Scan on t1 - Filter: ((a % 2) = 0) -(3 rows) - -SELECT * FROM t2; - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 - 20 | Success -(12 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t2; - QUERY PLAN ----------------- - Seq Scan on t2 -(1 row) - -CREATE TABLE t3 AS SELECT * FROM t1; -SELECT * FROM t3; - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 - 20 | Success -(12 rows) - -SELECT * INTO t4 FROM t1; -SELECT * FROM t4; - a | b -----+---------------------------------- - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 - 20 | Success -(12 rows) - --- --- RLS with JOIN --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE blog (id integer, author text, post text); -CREATE TABLE comment (blog_id integer, message text); -GRANT ALL ON blog, comment TO regress_rls_bob; -CREATE POLICY blog_1 ON blog USING (id % 2 = 0); -ALTER TABLE blog ENABLE ROW LEVEL SECURITY; -INSERT INTO blog VALUES - (1, 'alice', 'blog #1'), - (2, 'bob', 'blog #1'), - (3, 'alice', 'blog #2'), - (4, 'alice', 'blog #3'), - (5, 'john', 'blog #1'); -INSERT INTO comment VALUES - (1, 'cool blog'), - (1, 'fun blog'), - (3, 'crazy blog'), - (5, 'what?'), - (4, 'insane!'), - (2, 'who did it?'); -SET SESSION AUTHORIZATION regress_rls_bob; --- Check RLS JOIN with Non-RLS. -SELECT id, author, message FROM blog JOIN comment ON id = blog_id; - id | author | message -----+--------+------------- - 4 | alice | insane! - 2 | bob | who did it? -(2 rows) - --- Check Non-RLS JOIN with RLS. -SELECT id, author, message FROM comment JOIN blog ON id = blog_id; - id | author | message -----+--------+------------- - 4 | alice | insane! - 2 | bob | who did it? -(2 rows) - -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE POLICY comment_1 ON comment USING (blog_id < 4); -ALTER TABLE comment ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; --- Check RLS JOIN RLS -SELECT id, author, message FROM blog JOIN comment ON id = blog_id; - id | author | message -----+--------+------------- - 2 | bob | who did it? -(1 row) - -SELECT id, author, message FROM comment JOIN blog ON id = blog_id; - id | author | message -----+--------+------------- - 2 | bob | who did it? -(1 row) - -SET SESSION AUTHORIZATION regress_rls_alice; -DROP TABLE blog, comment; --- --- Default Deny Policy --- -RESET SESSION AUTHORIZATION; -DROP POLICY p2 ON t1; -ALTER TABLE t1 OWNER TO regress_rls_alice; --- Check that default deny does not apply to superuser. -RESET SESSION AUTHORIZATION; -SELECT * FROM t1; - a | b -----+---------------------------------- - 1 | c4ca4238a0b923820dcc509a6f75849b - 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 - 5 | e4da3b7fbbce2345d7772b0674a318d5 - 7 | 8f14e45fceea167a5a36dedd4bea2543 - 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 - 11 | 6512bd43d9caa6e02c990b0a82652dca - 13 | c51ce410c124a10e0db5e4b97fc2af39 - 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 - 17 | 70efdf2ec9b086079795c442636b55fb - 19 | 1f0e3dad99908345f7439f8ffabdffc4 - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 - 20 | Success -(22 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1; - QUERY PLAN ----------------- - Seq Scan on t1 -(1 row) - --- Check that default deny does not apply to table owner. -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM t1; - a | b -----+---------------------------------- - 1 | c4ca4238a0b923820dcc509a6f75849b - 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 - 5 | e4da3b7fbbce2345d7772b0674a318d5 - 7 | 8f14e45fceea167a5a36dedd4bea2543 - 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 - 11 | 6512bd43d9caa6e02c990b0a82652dca - 13 | c51ce410c124a10e0db5e4b97fc2af39 - 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 - 17 | 70efdf2ec9b086079795c442636b55fb - 19 | 1f0e3dad99908345f7439f8ffabdffc4 - 0 | cfcd208495d565ef66e7dff9f98764da - 2 | c81e728d9d4c2f636f067f89cc14862c - 4 | a87ff679a2f3e71d9181a67b7542122c - 6 | 1679091c5a880faf6fb5e6087eb1b2dc - 8 | c9f0f895fb98ab9159f51fd0297e236d - 10 | d3d9446802a44259755d38e6d163e820 - 12 | c20ad4d76fe97759aa27a0c99bff6710 - 14 | aab3238922bcc25a6f606eb525ffdc56 - 16 | c74d97b01eae257e44aa9d5bade97baf - 18 | 6f4922f45568161a8cdf4ad2299f6d23 - 20 | 98f13708210194c475687be6106a3b84 - 20 | Success -(22 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1; - QUERY PLAN ----------------- - Seq Scan on t1 -(1 row) - --- Check that default deny applies to non-owner/non-superuser when RLS on. -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO ON; -SELECT * FROM t1; - a | b ----+--- -(0 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM t1; - a | b ----+--- -(0 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM t1; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - --- --- COPY TO/FROM --- -RESET SESSION AUTHORIZATION; -DROP TABLE copy_t CASCADE; -ERROR: table "copy_t" does not exist -CREATE TABLE copy_t (a integer, b text); -CREATE POLICY p1 ON copy_t USING (a % 2 = 0); -ALTER TABLE copy_t ENABLE ROW LEVEL SECURITY; -GRANT ALL ON copy_t TO regress_rls_bob, regress_rls_exempt_user; -INSERT INTO copy_t (SELECT x, md5(x::text) FROM generate_series(0,10) x); --- Check COPY TO as Superuser/owner. -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; -0,cfcd208495d565ef66e7dff9f98764da -1,c4ca4238a0b923820dcc509a6f75849b -2,c81e728d9d4c2f636f067f89cc14862c -3,eccbc87e4b5ce2fe28308fd9f2a7baf3 -4,a87ff679a2f3e71d9181a67b7542122c -5,e4da3b7fbbce2345d7772b0674a318d5 -6,1679091c5a880faf6fb5e6087eb1b2dc -7,8f14e45fceea167a5a36dedd4bea2543 -8,c9f0f895fb98ab9159f51fd0297e236d -9,45c48cce2e2d7fbdea1afc51c7c6ad26 -10,d3d9446802a44259755d38e6d163e820 -SET row_security TO ON; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; -0,cfcd208495d565ef66e7dff9f98764da -1,c4ca4238a0b923820dcc509a6f75849b -2,c81e728d9d4c2f636f067f89cc14862c -3,eccbc87e4b5ce2fe28308fd9f2a7baf3 -4,a87ff679a2f3e71d9181a67b7542122c -5,e4da3b7fbbce2345d7772b0674a318d5 -6,1679091c5a880faf6fb5e6087eb1b2dc -7,8f14e45fceea167a5a36dedd4bea2543 -8,c9f0f895fb98ab9159f51fd0297e236d -9,45c48cce2e2d7fbdea1afc51c7c6ad26 -10,d3d9446802a44259755d38e6d163e820 --- Check COPY TO as user with permissions. -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO OFF; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS -ERROR: query would be affected by row-level security policy for table "copy_t" -SET row_security TO ON; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -0,cfcd208495d565ef66e7dff9f98764da -2,c81e728d9d4c2f636f067f89cc14862c -4,a87ff679a2f3e71d9181a67b7542122c -6,1679091c5a880faf6fb5e6087eb1b2dc -8,c9f0f895fb98ab9159f51fd0297e236d -10,d3d9446802a44259755d38e6d163e820 --- Check COPY TO as user with permissions and BYPASSRLS -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO OFF; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -0,cfcd208495d565ef66e7dff9f98764da -1,c4ca4238a0b923820dcc509a6f75849b -2,c81e728d9d4c2f636f067f89cc14862c -3,eccbc87e4b5ce2fe28308fd9f2a7baf3 -4,a87ff679a2f3e71d9181a67b7542122c -5,e4da3b7fbbce2345d7772b0674a318d5 -6,1679091c5a880faf6fb5e6087eb1b2dc -7,8f14e45fceea167a5a36dedd4bea2543 -8,c9f0f895fb98ab9159f51fd0297e236d -9,45c48cce2e2d7fbdea1afc51c7c6ad26 -10,d3d9446802a44259755d38e6d163e820 -SET row_security TO ON; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -0,cfcd208495d565ef66e7dff9f98764da -1,c4ca4238a0b923820dcc509a6f75849b -2,c81e728d9d4c2f636f067f89cc14862c -3,eccbc87e4b5ce2fe28308fd9f2a7baf3 -4,a87ff679a2f3e71d9181a67b7542122c -5,e4da3b7fbbce2345d7772b0674a318d5 -6,1679091c5a880faf6fb5e6087eb1b2dc -7,8f14e45fceea167a5a36dedd4bea2543 -8,c9f0f895fb98ab9159f51fd0297e236d -9,45c48cce2e2d7fbdea1afc51c7c6ad26 -10,d3d9446802a44259755d38e6d163e820 --- Check COPY TO as user without permissions. SET row_security TO OFF; -SET SESSION AUTHORIZATION regress_rls_carol; -SET row_security TO OFF; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS -ERROR: query would be affected by row-level security policy for table "copy_t" -SET row_security TO ON; -COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied -ERROR: permission denied for table copy_t --- Check COPY relation TO; keep it just one row to avoid reordering issues -RESET SESSION AUTHORIZATION; -SET row_security TO ON; -CREATE TABLE copy_rel_to (a integer, b text); -CREATE POLICY p1 ON copy_rel_to USING (a % 2 = 0); -ALTER TABLE copy_rel_to ENABLE ROW LEVEL SECURITY; -GRANT ALL ON copy_rel_to TO regress_rls_bob, regress_rls_exempt_user; -INSERT INTO copy_rel_to VALUES (1, md5('1')); --- Check COPY TO as Superuser/owner. -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; -1,c4ca4238a0b923820dcc509a6f75849b -SET row_security TO ON; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; -1,c4ca4238a0b923820dcc509a6f75849b --- Check COPY TO as user with permissions. -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO OFF; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS -ERROR: query would be affected by row-level security policy for table "copy_rel_to" -SET row_security TO ON; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok --- Check COPY TO as user with permissions and BYPASSRLS -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO OFF; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -1,c4ca4238a0b923820dcc509a6f75849b -SET row_security TO ON; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -1,c4ca4238a0b923820dcc509a6f75849b --- Check COPY TO as user without permissions. SET row_security TO OFF; -SET SESSION AUTHORIZATION regress_rls_carol; -SET row_security TO OFF; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied -ERROR: permission denied for table copy_rel_to -SET row_security TO ON; -COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied -ERROR: permission denied for table copy_rel_to --- Check COPY FROM as Superuser/owner. -RESET SESSION AUTHORIZATION; -SET row_security TO OFF; -COPY copy_t FROM STDIN; --ok -SET row_security TO ON; -COPY copy_t FROM STDIN; --ok --- Check COPY FROM as user with permissions. -SET SESSION AUTHORIZATION regress_rls_bob; -SET row_security TO OFF; -COPY copy_t FROM STDIN; --fail - would be affected by RLS. -ERROR: query would be affected by row-level security policy for table "copy_t" -SET row_security TO ON; -COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS. -ERROR: COPY FROM not supported with row-level security -HINT: Use INSERT statements instead. --- Check COPY FROM as user with permissions and BYPASSRLS -SET SESSION AUTHORIZATION regress_rls_exempt_user; -SET row_security TO ON; -COPY copy_t FROM STDIN; --ok --- Check COPY FROM as user without permissions. -SET SESSION AUTHORIZATION regress_rls_carol; -SET row_security TO OFF; -COPY copy_t FROM STDIN; --fail - permission denied. -ERROR: permission denied for table copy_t -SET row_security TO ON; -COPY copy_t FROM STDIN; --fail - permission denied. -ERROR: permission denied for table copy_t -RESET SESSION AUTHORIZATION; -DROP TABLE copy_t; -DROP TABLE copy_rel_to CASCADE; --- Check WHERE CURRENT OF -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE current_check (currentid int, payload text, rlsuser text); -GRANT ALL ON current_check TO PUBLIC; -INSERT INTO current_check VALUES - (1, 'abc', 'regress_rls_bob'), - (2, 'bcd', 'regress_rls_bob'), - (3, 'cde', 'regress_rls_bob'), - (4, 'def', 'regress_rls_bob'); -CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0); -CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user); -CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user); -ALTER TABLE current_check ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; --- Can SELECT even rows -SELECT * FROM current_check; - currentid | payload | rlsuser ------------+---------+----------------- - 2 | bcd | regress_rls_bob - 4 | def | regress_rls_bob -(2 rows) - --- Cannot UPDATE row 2 -UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *; - currentid | payload | rlsuser ------------+---------+--------- -(0 rows) - -BEGIN; -DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check; --- Returns rows that can be seen according to SELECT policy, like plain SELECT --- above (even rows) -FETCH ABSOLUTE 1 FROM current_check_cursor; - currentid | payload | rlsuser ------------+---------+----------------- - 2 | bcd | regress_rls_bob -(1 row) - --- Still cannot UPDATE row 2 through cursor -UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; - currentid | payload | rlsuser ------------+---------+--------- -(0 rows) - --- Can update row 4 through cursor, which is the next visible row -FETCH RELATIVE 1 FROM current_check_cursor; - currentid | payload | rlsuser ------------+---------+----------------- - 4 | def | regress_rls_bob -(1 row) - -UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; - currentid | payload | rlsuser ------------+---------+----------------- - 4 | def_new | regress_rls_bob -(1 row) - -SELECT * FROM current_check; - currentid | payload | rlsuser ------------+---------+----------------- - 2 | bcd | regress_rls_bob - 4 | def_new | regress_rls_bob -(2 rows) - --- Plan should be a subquery TID scan -EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor; - QUERY PLAN -------------------------------------------------------------- - Update on current_check - -> Tid Scan on current_check - TID Cond: CURRENT OF current_check_cursor - Filter: ((currentid = 4) AND ((currentid % 2) = 0)) -(4 rows) - --- Similarly can only delete row 4 -FETCH ABSOLUTE 1 FROM current_check_cursor; - currentid | payload | rlsuser ------------+---------+----------------- - 2 | bcd | regress_rls_bob -(1 row) - -DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; - currentid | payload | rlsuser ------------+---------+--------- -(0 rows) - -FETCH RELATIVE 1 FROM current_check_cursor; - currentid | payload | rlsuser ------------+---------+----------------- - 4 | def | regress_rls_bob -(1 row) - -DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; - currentid | payload | rlsuser ------------+---------+----------------- - 4 | def_new | regress_rls_bob -(1 row) - -SELECT * FROM current_check; - currentid | payload | rlsuser ------------+---------+----------------- - 2 | bcd | regress_rls_bob -(1 row) - -COMMIT; --- --- check pg_stats view filtering --- -SET row_security TO ON; -SET SESSION AUTHORIZATION regress_rls_alice; -ANALYZE current_check; --- Stats visible -SELECT row_security_active('current_check'); - row_security_active ---------------------- - f -(1 row) - -SELECT attname, most_common_vals FROM pg_stats - WHERE tablename = 'current_check' - ORDER BY 1; - attname | most_common_vals ------------+------------------- - currentid | - payload | - rlsuser | {regress_rls_bob} -(3 rows) - -SET SESSION AUTHORIZATION regress_rls_bob; --- Stats not visible -SELECT row_security_active('current_check'); - row_security_active ---------------------- - t -(1 row) - -SELECT attname, most_common_vals FROM pg_stats - WHERE tablename = 'current_check' - ORDER BY 1; - attname | most_common_vals ----------+------------------ -(0 rows) - --- --- Collation support --- -BEGIN; -CREATE TABLE coll_t (c) AS VALUES ('bar'::text); -CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); -ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; -GRANT SELECT ON coll_t TO regress_rls_alice; -SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass; - inputcollid ------------------- - inputcollid 950 -(1 row) - -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM coll_t; - c ------ - bar -(1 row) - -ROLLBACK; --- --- Shared Object Dependencies --- -RESET SESSION AUTHORIZATION; -BEGIN; -CREATE ROLE regress_rls_eve; -CREATE ROLE regress_rls_frank; -CREATE TABLE tbl1 (c) AS VALUES ('bar'::text); -GRANT SELECT ON TABLE tbl1 TO regress_rls_eve; -CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true); -SELECT refclassid::regclass, deptype - FROM pg_depend - WHERE classid = 'pg_policy'::regclass - AND refobjid = 'tbl1'::regclass; - refclassid | deptype -------------+--------- - pg_class | a -(1 row) - -SELECT refclassid::regclass, deptype - FROM pg_shdepend - WHERE classid = 'pg_policy'::regclass - AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole); - refclassid | deptype -------------+--------- - pg_authid | r - pg_authid | r -(2 rows) - -SAVEPOINT q; -DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p -ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it -DETAIL: privileges for table tbl1 -target of policy p on table tbl1 -ROLLBACK TO q; -ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true); -SAVEPOINT q; -DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT -ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it -DETAIL: privileges for table tbl1 -ROLLBACK TO q; -REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve; -SAVEPOINT q; -DROP ROLE regress_rls_eve; --succeeds -ROLLBACK TO q; -SAVEPOINT q; -DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p -ERROR: role "regress_rls_frank" cannot be dropped because some objects depend on it -DETAIL: target of policy p on table tbl1 -ROLLBACK TO q; -DROP POLICY p ON tbl1; -SAVEPOINT q; -DROP ROLE regress_rls_frank; -- succeeds -ROLLBACK TO q; -ROLLBACK; -- cleanup --- --- Converting table to view --- -BEGIN; -CREATE TABLE t (c int); -CREATE POLICY p ON t USING (c % 2 = 1); -ALTER TABLE t ENABLE ROW LEVEL SECURITY; -SAVEPOINT q; -CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD - SELECT * FROM generate_series(1,5) t0(c); -- fails due to row-level security enabled -ERROR: could not convert table "t" to a view because it has row security enabled -ROLLBACK TO q; -ALTER TABLE t DISABLE ROW LEVEL SECURITY; -SAVEPOINT q; -CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD - SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t -ERROR: could not convert table "t" to a view because it has row security policies -ROLLBACK TO q; -DROP POLICY p ON t; -CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD - SELECT * FROM generate_series(1,5) t0(c); -- succeeds -ROLLBACK; --- --- Policy expression handling --- -BEGIN; -CREATE TABLE t (c) AS VALUES ('bar'::text); -CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions -ERROR: aggregate functions are not allowed in policy expressions -ROLLBACK; --- --- Non-target relations are only subject to SELECT policies --- -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE r1 (a int); -CREATE TABLE r2 (a int); -INSERT INTO r1 VALUES (10), (20); -INSERT INTO r2 VALUES (10), (20); -GRANT ALL ON r1, r2 TO regress_rls_bob; -CREATE POLICY p1 ON r1 USING (true); -ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; -CREATE POLICY p1 ON r2 FOR SELECT USING (true); -CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); -CREATE POLICY p3 ON r2 FOR UPDATE USING (false); -CREATE POLICY p4 ON r2 FOR DELETE USING (false); -ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; -SET SESSION AUTHORIZATION regress_rls_bob; -SELECT * FROM r1; - a ----- - 10 - 20 -(2 rows) - -SELECT * FROM r2; - a ----- - 10 - 20 -(2 rows) - --- r2 is read-only -INSERT INTO r2 VALUES (2); -- Not allowed -ERROR: new row violates row-level security policy for table "r2" -UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing - a ---- -(0 rows) - -DELETE FROM r2 RETURNING *; -- Deletes nothing - a ---- -(0 rows) - --- r2 can be used as a non-target relation in DML -INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK - a ----- - 11 - 21 -(2 rows) - -UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK - a | a -----+---- - 12 | 10 - 22 | 20 -(2 rows) - -DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK - a | a -----+---- - 12 | 10 - 22 | 20 -(2 rows) - -SELECT * FROM r1; - a ----- - 11 - 21 -(2 rows) - -SELECT * FROM r2; - a ----- - 10 - 20 -(2 rows) - -SET SESSION AUTHORIZATION regress_rls_alice; -DROP TABLE r1; -DROP TABLE r2; --- --- FORCE ROW LEVEL SECURITY applies RLS to owners too --- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security = on; -CREATE TABLE r1 (a int); -INSERT INTO r1 VALUES (10), (20); -CREATE POLICY p1 ON r1 USING (false); -ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r1 FORCE ROW LEVEL SECURITY; --- No error, but no rows -TABLE r1; - a ---- -(0 rows) - --- RLS error -INSERT INTO r1 VALUES (1); -ERROR: new row violates row-level security policy for table "r1" --- No error (unable to see any rows to update) -UPDATE r1 SET a = 1; -TABLE r1; - a ---- -(0 rows) - --- No error (unable to see any rows to delete) -DELETE FROM r1; -TABLE r1; - a ---- -(0 rows) - -SET row_security = off; --- these all fail, would be affected by RLS -TABLE r1; -ERROR: query would be affected by row-level security policy for table "r1" -HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY. -UPDATE r1 SET a = 1; -ERROR: query would be affected by row-level security policy for table "r1" -HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY. -DELETE FROM r1; -ERROR: query would be affected by row-level security policy for table "r1" -HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY. -DROP TABLE r1; --- --- FORCE ROW LEVEL SECURITY does not break RI --- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security = on; -CREATE TABLE r1 (a int PRIMARY KEY); -CREATE TABLE r2 (a int REFERENCES r1); -INSERT INTO r1 VALUES (10), (20); -INSERT INTO r2 VALUES (10), (20); --- Create policies on r2 which prevent the --- owner from seeing any rows, but RI should --- still see them. -CREATE POLICY p1 ON r2 USING (false); -ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r2 FORCE ROW LEVEL SECURITY; --- Errors due to rows in r2 -DELETE FROM r1; -ERROR: update or delete on table "r1" violates foreign key constraint "r2_a_fkey" on table "r2" -DETAIL: Key (a)=(10) is still referenced from table "r2". --- Reset r2 to no-RLS -DROP POLICY p1 ON r2; -ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; -ALTER TABLE r2 DISABLE ROW LEVEL SECURITY; --- clean out r2 for INSERT test below -DELETE FROM r2; --- Change r1 to not allow rows to be seen -CREATE POLICY p1 ON r1 USING (false); -ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r1 FORCE ROW LEVEL SECURITY; --- No rows seen -TABLE r1; - a ---- -(0 rows) - --- No error, RI still sees that row exists in r1 -INSERT INTO r2 VALUES (10); -DROP TABLE r2; -DROP TABLE r1; --- Ensure cascaded DELETE works -CREATE TABLE r1 (a int PRIMARY KEY); -CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE); -INSERT INTO r1 VALUES (10), (20); -INSERT INTO r2 VALUES (10), (20); --- Create policies on r2 which prevent the --- owner from seeing any rows, but RI should --- still see them. -CREATE POLICY p1 ON r2 USING (false); -ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r2 FORCE ROW LEVEL SECURITY; --- Deletes all records from both -DELETE FROM r1; --- Remove FORCE from r2 -ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; --- As owner, we now bypass RLS --- verify no rows in r2 now -TABLE r2; - a ---- -(0 rows) - -DROP TABLE r2; -DROP TABLE r1; --- Ensure cascaded UPDATE works -CREATE TABLE r1 (a int PRIMARY KEY); -CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE); -INSERT INTO r1 VALUES (10), (20); -INSERT INTO r2 VALUES (10), (20); --- Create policies on r2 which prevent the --- owner from seeing any rows, but RI should --- still see them. -CREATE POLICY p1 ON r2 USING (false); -ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r2 FORCE ROW LEVEL SECURITY; --- Updates records in both -UPDATE r1 SET a = a+5; --- Remove FORCE from r2 -ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; --- As owner, we now bypass RLS --- verify records in r2 updated -TABLE r2; - a ----- - 15 - 25 -(2 rows) - -DROP TABLE r2; -DROP TABLE r1; --- --- Test INSERT+RETURNING applies SELECT policies as --- WithCheckOptions (meaning an error is thrown) --- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security = on; -CREATE TABLE r1 (a int); -CREATE POLICY p1 ON r1 FOR SELECT USING (false); -CREATE POLICY p2 ON r1 FOR INSERT WITH CHECK (true); -ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r1 FORCE ROW LEVEL SECURITY; --- Works fine -INSERT INTO r1 VALUES (10), (20); --- No error, but no rows -TABLE r1; - a ---- -(0 rows) - -SET row_security = off; --- fail, would be affected by RLS -TABLE r1; -ERROR: query would be affected by row-level security policy for table "r1" -HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY. -SET row_security = on; --- Error -INSERT INTO r1 VALUES (10), (20) RETURNING *; -ERROR: new row violates row-level security policy for table "r1" -DROP TABLE r1; --- --- Test UPDATE+RETURNING applies SELECT policies as --- WithCheckOptions (meaning an error is thrown) --- -SET SESSION AUTHORIZATION regress_rls_alice; -SET row_security = on; -CREATE TABLE r1 (a int PRIMARY KEY); -CREATE POLICY p1 ON r1 FOR SELECT USING (a < 20); -CREATE POLICY p2 ON r1 FOR UPDATE USING (a < 20) WITH CHECK (true); -CREATE POLICY p3 ON r1 FOR INSERT WITH CHECK (true); -INSERT INTO r1 VALUES (10); -ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE r1 FORCE ROW LEVEL SECURITY; --- Works fine -UPDATE r1 SET a = 30; --- Show updated rows -ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY; -TABLE r1; - a ----- - 30 -(1 row) - --- reset value in r1 for test with RETURNING -UPDATE r1 SET a = 10; --- Verify row reset -TABLE r1; - a ----- - 10 -(1 row) - -ALTER TABLE r1 FORCE ROW LEVEL SECURITY; --- Error -UPDATE r1 SET a = 30 RETURNING *; -ERROR: new row violates row-level security policy for table "r1" --- UPDATE path of INSERT ... ON CONFLICT DO UPDATE should also error out -INSERT INTO r1 VALUES (10) - ON CONFLICT (a) DO UPDATE SET a = 30 RETURNING *; -ERROR: new row violates row-level security policy for table "r1" --- Should still error out without RETURNING (use of arbiter always requires --- SELECT permissions) -INSERT INTO r1 VALUES (10) - ON CONFLICT (a) DO UPDATE SET a = 30; -ERROR: new row violates row-level security policy for table "r1" -INSERT INTO r1 VALUES (10) - ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30; -ERROR: new row violates row-level security policy for table "r1" -DROP TABLE r1; --- Check dependency handling -RESET SESSION AUTHORIZATION; -CREATE TABLE dep1 (c1 int); -CREATE TABLE dep2 (c1 int); -CREATE POLICY dep_p1 ON dep1 TO regress_rls_bob USING (c1 > (select max(dep2.c1) from dep2)); -ALTER POLICY dep_p1 ON dep1 TO regress_rls_bob,regress_rls_carol; --- Should return one -SELECT count(*) = 1 FROM pg_depend - WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') - AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); - ?column? ----------- - t -(1 row) - -ALTER POLICY dep_p1 ON dep1 USING (true); --- Should return one -SELECT count(*) = 1 FROM pg_shdepend - WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') - AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_bob'); - ?column? ----------- - t -(1 row) - --- Should return one -SELECT count(*) = 1 FROM pg_shdepend - WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') - AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_carol'); - ?column? ----------- - t -(1 row) - --- Should return zero -SELECT count(*) = 0 FROM pg_depend - WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') - AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); - ?column? ----------- - t -(1 row) - --- DROP OWNED BY testing -RESET SESSION AUTHORIZATION; -CREATE ROLE regress_rls_dob_role1; -CREATE ROLE regress_rls_dob_role2; -CREATE TABLE dob_t1 (c1 int); -CREATE TABLE dob_t2 (c1 int) PARTITION BY RANGE (c1); -CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1 USING (true); -DROP OWNED BY regress_rls_dob_role1; -DROP POLICY p1 ON dob_t1; -- should fail, already gone -ERROR: policy "p1" for table "dob_t1" does not exist -CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true); -DROP OWNED BY regress_rls_dob_role1; -DROP POLICY p1 ON dob_t1; -- should succeed --- same cases with duplicate polroles entries -CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1 USING (true); -DROP OWNED BY regress_rls_dob_role1; -DROP POLICY p1 ON dob_t1; -- should fail, already gone -ERROR: policy "p1" for table "dob_t1" does not exist -CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1,regress_rls_dob_role2 USING (true); -DROP OWNED BY regress_rls_dob_role1; -DROP POLICY p1 ON dob_t1; -- should succeed --- partitioned target -CREATE POLICY p1 ON dob_t2 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true); -DROP OWNED BY regress_rls_dob_role1; -DROP POLICY p1 ON dob_t2; -- should succeed -DROP USER regress_rls_dob_role1; -DROP USER regress_rls_dob_role2; --- Bug #15708: view + table with RLS should check policies as view owner -CREATE TABLE ref_tbl (a int); -INSERT INTO ref_tbl VALUES (1); -CREATE TABLE rls_tbl (a int); -INSERT INTO rls_tbl VALUES (10); -ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; -CREATE POLICY p1 ON rls_tbl USING (EXISTS (SELECT 1 FROM ref_tbl)); -GRANT SELECT ON ref_tbl TO regress_rls_bob; -GRANT SELECT ON rls_tbl TO regress_rls_bob; -CREATE VIEW rls_view AS SELECT * FROM rls_tbl; -ALTER VIEW rls_view OWNER TO regress_rls_bob; -GRANT SELECT ON rls_view TO regress_rls_alice; -SET SESSION AUTHORIZATION regress_rls_alice; -SELECT * FROM ref_tbl; -- Permission denied -ERROR: permission denied for table ref_tbl -SELECT * FROM rls_tbl; -- Permission denied -ERROR: permission denied for table rls_tbl -SELECT * FROM rls_view; -- OK - a ----- - 10 -(1 row) - -RESET SESSION AUTHORIZATION; -DROP VIEW rls_view; -DROP TABLE rls_tbl; -DROP TABLE ref_tbl; --- Leaky operator test -CREATE TABLE rls_tbl (a int); -INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x; -ANALYZE rls_tbl; -ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; -GRANT SELECT ON rls_tbl TO regress_rls_alice; -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE FUNCTION op_leak(int, int) RETURNS bool - AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END' - LANGUAGE plpgsql; -CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int, - restrict = scalarltsel); -SELECT * FROM rls_tbl WHERE a <<< 1000; - a ---- -(0 rows) - -DROP OPERATOR <<< (int, int); -DROP FUNCTION op_leak(int, int); -RESET SESSION AUTHORIZATION; -DROP TABLE rls_tbl; --- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects -SET SESSION AUTHORIZATION regress_rls_alice; -CREATE TABLE rls_tbl (a int, b int, c int); -CREATE POLICY p1 ON rls_tbl USING (rls_tbl >= ROW(1,1,1)); -ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; -ALTER TABLE rls_tbl FORCE ROW LEVEL SECURITY; -INSERT INTO rls_tbl SELECT 10, 20, 30; -EXPLAIN (VERBOSE, COSTS OFF) -INSERT INTO rls_tbl - SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss; - QUERY PLAN --------------------------------------------------------------------- - Insert on regress_rls_schema.rls_tbl - -> Subquery Scan on ss - Output: ss.b, ss.c, NULL::integer - -> Sort - Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a - Sort Key: rls_tbl_1.a - -> Seq Scan on regress_rls_schema.rls_tbl rls_tbl_1 - Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a - Filter: (rls_tbl_1.* >= '(1,1,1)'::record) -(9 rows) - -INSERT INTO rls_tbl - SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss; -SELECT * FROM rls_tbl; - a | b | c -----+----+---- - 10 | 20 | 30 - 20 | 30 | -(2 rows) - -DROP TABLE rls_tbl; -RESET SESSION AUTHORIZATION; --- --- Clean up objects --- -RESET SESSION AUTHORIZATION; -DROP SCHEMA regress_rls_schema CASCADE; -NOTICE: drop cascades to 29 other objects -DETAIL: drop cascades to function f_leak(text) -drop cascades to table uaccount -drop cascades to table category -drop cascades to table document -drop cascades to table part_document -drop cascades to table dependent -drop cascades to table rec1 -drop cascades to table rec2 -drop cascades to view rec1v -drop cascades to view rec2v -drop cascades to table s1 -drop cascades to table s2 -drop cascades to view v2 -drop cascades to table b1 -drop cascades to view bv1 -drop cascades to table z1 -drop cascades to table z2 -drop cascades to table x1 -drop cascades to table y1 -drop cascades to table y2 -drop cascades to table t1 -drop cascades to table t2 -drop cascades to table t3 -drop cascades to table t4 -drop cascades to table current_check -drop cascades to table dep1 -drop cascades to table dep2 -drop cascades to table dob_t1 -drop cascades to table dob_t2 -DROP USER regress_rls_alice; -DROP USER regress_rls_bob; -DROP USER regress_rls_carol; -DROP USER regress_rls_dave; -DROP USER regress_rls_exempt_user; -DROP ROLE regress_rls_group1; -DROP ROLE regress_rls_group2; --- Arrange to have a few policies left over, for testing --- pg_dump/pg_restore -CREATE SCHEMA regress_rls_schema; -CREATE TABLE rls_tbl (c1 int); -ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; -CREATE POLICY p1 ON rls_tbl USING (c1 > 5); -CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3); -CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5); -CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3); -CREATE TABLE rls_tbl_force (c1 int); -ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY; -ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY; -CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5); -CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8); -CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5); -CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8); +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/object_address.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/object_address.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/object_address.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/object_address.out 2021-10-30 22:04:14 -0600 @@ -1,623 +1,2 @@ --- --- Test for pg_get_object_address --- --- Clean up in case a prior regression run failed -SET client_min_messages TO 'warning'; -DROP ROLE IF EXISTS regress_addr_user; -RESET client_min_messages; -CREATE USER regress_addr_user; --- Test generic object addressing/identification functions -CREATE SCHEMA addr_nsp; -SET search_path TO 'addr_nsp'; -CREATE FOREIGN DATA WRAPPER addr_fdw; -CREATE SERVER addr_fserv FOREIGN DATA WRAPPER addr_fdw; -CREATE TEXT SEARCH DICTIONARY addr_ts_dict (template=simple); -CREATE TEXT SEARCH CONFIGURATION addr_ts_conf (copy=english); -CREATE TEXT SEARCH TEMPLATE addr_ts_temp (lexize=dsimple_lexize); -CREATE TEXT SEARCH PARSER addr_ts_prs - (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); -CREATE TABLE addr_nsp.gentable ( - a serial primary key CONSTRAINT a_chk CHECK (a > 0), - b text DEFAULT 'hello'); -CREATE TABLE addr_nsp.parttable ( - a int PRIMARY KEY -) PARTITION BY RANGE (a); -CREATE VIEW addr_nsp.genview AS SELECT * from addr_nsp.gentable; -CREATE MATERIALIZED VIEW addr_nsp.genmatview AS SELECT * FROM addr_nsp.gentable; -CREATE TYPE addr_nsp.gencomptype AS (a int); -CREATE TYPE addr_nsp.genenum AS ENUM ('one', 'two'); -CREATE FOREIGN TABLE addr_nsp.genftable (a int) SERVER addr_fserv; -CREATE AGGREGATE addr_nsp.genaggr(int4) (sfunc = int4pl, stype = int4); -CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0); -CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; -CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig(); -CREATE POLICY genpol ON addr_nsp.gentable; -CREATE PROCEDURE addr_nsp.proc(int4) LANGUAGE SQL AS $$ $$; -CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw; -CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; -ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user; --- this transform would be quite unsafe to leave lying around, --- except that the SQL language pays no attention to transforms: -CREATE TRANSFORM FOR int LANGUAGE SQL ( - FROM SQL WITH FUNCTION prsd_lextype(internal), - TO SQL WITH FUNCTION int4recv(internal)); --- suppress warning that depends on wal_level -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; -RESET client_min_messages; -CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; --- test some error cases -SELECT pg_get_object_address('stone', '{}', '{}'); -ERROR: unrecognized object type "stone" -SELECT pg_get_object_address('table', '{}', '{}'); -ERROR: name list length must be at least 1 -SELECT pg_get_object_address('table', '{NULL}', '{}'); -ERROR: name or argument lists may not contain nulls --- unrecognized object types -DO $$ -DECLARE - objtype text; -BEGIN - FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), - ('toast table column'), ('view column'), ('materialized view column') - LOOP - BEGIN - PERFORM pg_get_object_address(objtype, '{one}', '{}'); - EXCEPTION WHEN invalid_parameter_value THEN - RAISE WARNING 'error for %: %', objtype, sqlerrm; - END; - END LOOP; -END; -$$; -WARNING: error for toast table: unsupported object type "toast table" -WARNING: error for index column: unsupported object type "index column" -WARNING: error for sequence column: unsupported object type "sequence column" -WARNING: error for toast table column: unsupported object type "toast table column" -WARNING: error for view column: unsupported object type "view column" -WARNING: error for materialized view column: unsupported object type "materialized view column" --- miscellaneous other errors -select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}'); -ERROR: operator 1 (int4, bool) of operator family integer_ops for access method btree does not exist -select * from pg_get_object_address('operator of access method', '{btree,integer_ops,99}', '{int4,int4}'); -ERROR: operator 99 (int4, int4) of operator family integer_ops for access method btree does not exist -select * from pg_get_object_address('function of access method', '{btree,integer_ops,1}', '{int4,bool}'); -ERROR: function 1 (int4, bool) of operator family integer_ops for access method btree does not exist -select * from pg_get_object_address('function of access method', '{btree,integer_ops,99}', '{int4,int4}'); -ERROR: function 99 (int4, int4) of operator family integer_ops for access method btree does not exist -DO $$ -DECLARE - objtype text; - names text[]; - args text[]; -BEGIN - FOR objtype IN VALUES - ('table'), ('index'), ('sequence'), ('view'), - ('materialized view'), ('foreign table'), - ('table column'), ('foreign table column'), - ('aggregate'), ('function'), ('procedure'), ('type'), ('cast'), - ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), - ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), - ('text search parser'), ('text search dictionary'), - ('text search template'), ('text search configuration'), - ('policy'), ('user mapping'), ('default acl'), ('transform'), - ('operator of access method'), ('function of access method'), - ('publication relation') - LOOP - FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') - LOOP - FOR args IN VALUES ('{}'), ('{integer}') - LOOP - BEGIN - PERFORM pg_get_object_address(objtype, names, args); - EXCEPTION WHEN OTHERS THEN - RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; - END; - END LOOP; - END LOOP; - END LOOP; -END; -$$; -WARNING: error for table,{eins},{}: relation "eins" does not exist -WARNING: error for table,{eins},{integer}: relation "eins" does not exist -WARNING: error for table,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for table,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for table,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for table,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for index,{eins},{}: relation "eins" does not exist -WARNING: error for index,{eins},{integer}: relation "eins" does not exist -WARNING: error for index,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for index,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for index,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for index,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for sequence,{eins},{}: relation "eins" does not exist -WARNING: error for sequence,{eins},{integer}: relation "eins" does not exist -WARNING: error for sequence,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for sequence,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for sequence,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for sequence,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for view,{eins},{}: relation "eins" does not exist -WARNING: error for view,{eins},{integer}: relation "eins" does not exist -WARNING: error for view,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for view,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for view,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for view,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for materialized view,{eins},{}: relation "eins" does not exist -WARNING: error for materialized view,{eins},{integer}: relation "eins" does not exist -WARNING: error for materialized view,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for materialized view,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for materialized view,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for materialized view,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for foreign table,{eins},{}: relation "eins" does not exist -WARNING: error for foreign table,{eins},{integer}: relation "eins" does not exist -WARNING: error for foreign table,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist -WARNING: error for foreign table,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for foreign table,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for foreign table,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" -WARNING: error for table column,{eins},{}: column name must be qualified -WARNING: error for table column,{eins},{integer}: column name must be qualified -WARNING: error for table column,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for table column,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for table column,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for table column,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for foreign table column,{eins},{}: column name must be qualified -WARNING: error for foreign table column,{eins},{integer}: column name must be qualified -WARNING: error for foreign table column,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for foreign table column,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for foreign table column,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for foreign table column,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for aggregate,{eins},{}: aggregate eins(*) does not exist -WARNING: error for aggregate,{eins},{integer}: aggregate eins(integer) does not exist -WARNING: error for aggregate,{addr_nsp,zwei},{}: aggregate addr_nsp.zwei(*) does not exist -WARNING: error for aggregate,{addr_nsp,zwei},{integer}: aggregate addr_nsp.zwei(integer) does not exist -WARNING: error for aggregate,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for aggregate,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for function,{eins},{}: function eins() does not exist -WARNING: error for function,{eins},{integer}: function eins(integer) does not exist -WARNING: error for function,{addr_nsp,zwei},{}: function addr_nsp.zwei() does not exist -WARNING: error for function,{addr_nsp,zwei},{integer}: function addr_nsp.zwei(integer) does not exist -WARNING: error for function,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for function,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for procedure,{eins},{}: procedure eins() does not exist -WARNING: error for procedure,{eins},{integer}: procedure eins(integer) does not exist -WARNING: error for procedure,{addr_nsp,zwei},{}: procedure addr_nsp.zwei() does not exist -WARNING: error for procedure,{addr_nsp,zwei},{integer}: procedure addr_nsp.zwei(integer) does not exist -WARNING: error for procedure,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for procedure,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for type,{eins},{}: type "eins" does not exist -WARNING: error for type,{eins},{integer}: type "eins" does not exist -WARNING: error for type,{addr_nsp,zwei},{}: name list length must be exactly 1 -WARNING: error for type,{addr_nsp,zwei},{integer}: name list length must be exactly 1 -WARNING: error for type,{eins,zwei,drei},{}: name list length must be exactly 1 -WARNING: error for type,{eins,zwei,drei},{integer}: name list length must be exactly 1 -WARNING: error for cast,{eins},{}: argument list length must be exactly 1 -WARNING: error for cast,{eins},{integer}: type "eins" does not exist -WARNING: error for cast,{addr_nsp,zwei},{}: name list length must be exactly 1 -WARNING: error for cast,{addr_nsp,zwei},{integer}: name list length must be exactly 1 -WARNING: error for cast,{eins,zwei,drei},{}: name list length must be exactly 1 -WARNING: error for cast,{eins,zwei,drei},{integer}: name list length must be exactly 1 -WARNING: error for table constraint,{eins},{}: must specify relation and object name -WARNING: error for table constraint,{eins},{integer}: must specify relation and object name -WARNING: error for table constraint,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for table constraint,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for table constraint,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for table constraint,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for domain constraint,{eins},{}: argument list length must be exactly 1 -WARNING: error for domain constraint,{eins},{integer}: type "eins" does not exist -WARNING: error for domain constraint,{addr_nsp,zwei},{}: name list length must be exactly 1 -WARNING: error for domain constraint,{addr_nsp,zwei},{integer}: name list length must be exactly 1 -WARNING: error for domain constraint,{eins,zwei,drei},{}: name list length must be exactly 1 -WARNING: error for domain constraint,{eins,zwei,drei},{integer}: name list length must be exactly 1 -WARNING: error for conversion,{eins},{}: conversion "eins" does not exist -WARNING: error for conversion,{eins},{integer}: conversion "eins" does not exist -WARNING: error for conversion,{addr_nsp,zwei},{}: conversion "addr_nsp.zwei" does not exist -WARNING: error for conversion,{addr_nsp,zwei},{integer}: conversion "addr_nsp.zwei" does not exist -WARNING: error for conversion,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for conversion,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for default value,{eins},{}: column name must be qualified -WARNING: error for default value,{eins},{integer}: column name must be qualified -WARNING: error for default value,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for default value,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for default value,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for default value,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for operator,{eins},{}: argument list length must be exactly 2 -WARNING: error for operator,{eins},{integer}: argument list length must be exactly 2 -WARNING: error for operator,{addr_nsp,zwei},{}: argument list length must be exactly 2 -WARNING: error for operator,{addr_nsp,zwei},{integer}: argument list length must be exactly 2 -WARNING: error for operator,{eins,zwei,drei},{}: argument list length must be exactly 2 -WARNING: error for operator,{eins,zwei,drei},{integer}: argument list length must be exactly 2 -WARNING: error for operator class,{eins},{}: name list length must be at least 2 -WARNING: error for operator class,{eins},{integer}: name list length must be at least 2 -WARNING: error for operator class,{addr_nsp,zwei},{}: access method "addr_nsp" does not exist -WARNING: error for operator class,{addr_nsp,zwei},{integer}: access method "addr_nsp" does not exist -WARNING: error for operator class,{eins,zwei,drei},{}: access method "eins" does not exist -WARNING: error for operator class,{eins,zwei,drei},{integer}: access method "eins" does not exist -WARNING: error for operator family,{eins},{}: name list length must be at least 2 -WARNING: error for operator family,{eins},{integer}: name list length must be at least 2 -WARNING: error for operator family,{addr_nsp,zwei},{}: access method "addr_nsp" does not exist -WARNING: error for operator family,{addr_nsp,zwei},{integer}: access method "addr_nsp" does not exist -WARNING: error for operator family,{eins,zwei,drei},{}: access method "eins" does not exist -WARNING: error for operator family,{eins,zwei,drei},{integer}: access method "eins" does not exist -WARNING: error for rule,{eins},{}: must specify relation and object name -WARNING: error for rule,{eins},{integer}: must specify relation and object name -WARNING: error for rule,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for rule,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for rule,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for rule,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for trigger,{eins},{}: must specify relation and object name -WARNING: error for trigger,{eins},{integer}: must specify relation and object name -WARNING: error for trigger,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for trigger,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for trigger,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for trigger,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for text search parser,{eins},{}: text search parser "eins" does not exist -WARNING: error for text search parser,{eins},{integer}: text search parser "eins" does not exist -WARNING: error for text search parser,{addr_nsp,zwei},{}: text search parser "addr_nsp.zwei" does not exist -WARNING: error for text search parser,{addr_nsp,zwei},{integer}: text search parser "addr_nsp.zwei" does not exist -WARNING: error for text search parser,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search parser,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search dictionary,{eins},{}: text search dictionary "eins" does not exist -WARNING: error for text search dictionary,{eins},{integer}: text search dictionary "eins" does not exist -WARNING: error for text search dictionary,{addr_nsp,zwei},{}: text search dictionary "addr_nsp.zwei" does not exist -WARNING: error for text search dictionary,{addr_nsp,zwei},{integer}: text search dictionary "addr_nsp.zwei" does not exist -WARNING: error for text search dictionary,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search dictionary,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search template,{eins},{}: text search template "eins" does not exist -WARNING: error for text search template,{eins},{integer}: text search template "eins" does not exist -WARNING: error for text search template,{addr_nsp,zwei},{}: text search template "addr_nsp.zwei" does not exist -WARNING: error for text search template,{addr_nsp,zwei},{integer}: text search template "addr_nsp.zwei" does not exist -WARNING: error for text search template,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search template,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search configuration,{eins},{}: text search configuration "eins" does not exist -WARNING: error for text search configuration,{eins},{integer}: text search configuration "eins" does not exist -WARNING: error for text search configuration,{addr_nsp,zwei},{}: text search configuration "addr_nsp.zwei" does not exist -WARNING: error for text search configuration,{addr_nsp,zwei},{integer}: text search configuration "addr_nsp.zwei" does not exist -WARNING: error for text search configuration,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for text search configuration,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei -WARNING: error for policy,{eins},{}: must specify relation and object name -WARNING: error for policy,{eins},{integer}: must specify relation and object name -WARNING: error for policy,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist -WARNING: error for policy,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist -WARNING: error for policy,{eins,zwei,drei},{}: schema "eins" does not exist -WARNING: error for policy,{eins,zwei,drei},{integer}: schema "eins" does not exist -WARNING: error for user mapping,{eins},{}: argument list length must be exactly 1 -WARNING: error for user mapping,{eins},{integer}: user mapping for user "eins" on server "integer" does not exist -WARNING: error for user mapping,{addr_nsp,zwei},{}: argument list length must be exactly 1 -WARNING: error for user mapping,{addr_nsp,zwei},{integer}: user mapping for user "addr_nsp" on server "integer" does not exist -WARNING: error for user mapping,{eins,zwei,drei},{}: argument list length must be exactly 1 -WARNING: error for user mapping,{eins,zwei,drei},{integer}: user mapping for user "eins" on server "integer" does not exist -WARNING: error for default acl,{eins},{}: argument list length must be exactly 1 -WARNING: error for default acl,{eins},{integer}: unrecognized default ACL object type "i" -WARNING: error for default acl,{addr_nsp,zwei},{}: argument list length must be exactly 1 -WARNING: error for default acl,{addr_nsp,zwei},{integer}: unrecognized default ACL object type "i" -WARNING: error for default acl,{eins,zwei,drei},{}: argument list length must be exactly 1 -WARNING: error for default acl,{eins,zwei,drei},{integer}: unrecognized default ACL object type "i" -WARNING: error for transform,{eins},{}: argument list length must be exactly 1 -WARNING: error for transform,{eins},{integer}: type "eins" does not exist -WARNING: error for transform,{addr_nsp,zwei},{}: name list length must be exactly 1 -WARNING: error for transform,{addr_nsp,zwei},{integer}: name list length must be exactly 1 -WARNING: error for transform,{eins,zwei,drei},{}: name list length must be exactly 1 -WARNING: error for transform,{eins,zwei,drei},{integer}: name list length must be exactly 1 -WARNING: error for operator of access method,{eins},{}: name list length must be at least 3 -WARNING: error for operator of access method,{eins},{integer}: name list length must be at least 3 -WARNING: error for operator of access method,{addr_nsp,zwei},{}: name list length must be at least 3 -WARNING: error for operator of access method,{addr_nsp,zwei},{integer}: name list length must be at least 3 -WARNING: error for operator of access method,{eins,zwei,drei},{}: argument list length must be exactly 2 -WARNING: error for operator of access method,{eins,zwei,drei},{integer}: argument list length must be exactly 2 -WARNING: error for function of access method,{eins},{}: name list length must be at least 3 -WARNING: error for function of access method,{eins},{integer}: name list length must be at least 3 -WARNING: error for function of access method,{addr_nsp,zwei},{}: name list length must be at least 3 -WARNING: error for function of access method,{addr_nsp,zwei},{integer}: name list length must be at least 3 -WARNING: error for function of access method,{eins,zwei,drei},{}: argument list length must be exactly 2 -WARNING: error for function of access method,{eins,zwei,drei},{integer}: argument list length must be exactly 2 -WARNING: error for publication relation,{eins},{}: argument list length must be exactly 1 -WARNING: error for publication relation,{eins},{integer}: relation "eins" does not exist -WARNING: error for publication relation,{addr_nsp,zwei},{}: argument list length must be exactly 1 -WARNING: error for publication relation,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist -WARNING: error for publication relation,{eins,zwei,drei},{}: argument list length must be exactly 1 -WARNING: error for publication relation,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" --- these object types cannot be qualified names -SELECT pg_get_object_address('language', '{one}', '{}'); -ERROR: language "one" does not exist -SELECT pg_get_object_address('language', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('large object', '{123}', '{}'); -ERROR: large object 123 does not exist -SELECT pg_get_object_address('large object', '{123,456}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('large object', '{blargh}', '{}'); -ERROR: invalid input syntax for type oid: "blargh" -SELECT pg_get_object_address('schema', '{one}', '{}'); -ERROR: schema "one" does not exist -SELECT pg_get_object_address('schema', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('role', '{one}', '{}'); -ERROR: role "one" does not exist -SELECT pg_get_object_address('role', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('database', '{one}', '{}'); -ERROR: database "one" does not exist -SELECT pg_get_object_address('database', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('tablespace', '{one}', '{}'); -ERROR: tablespace "one" does not exist -SELECT pg_get_object_address('tablespace', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('foreign-data wrapper', '{one}', '{}'); -ERROR: foreign-data wrapper "one" does not exist -SELECT pg_get_object_address('foreign-data wrapper', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('server', '{one}', '{}'); -ERROR: server "one" does not exist -SELECT pg_get_object_address('server', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('extension', '{one}', '{}'); -ERROR: extension "one" does not exist -SELECT pg_get_object_address('extension', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('event trigger', '{one}', '{}'); -ERROR: event trigger "one" does not exist -SELECT pg_get_object_address('event trigger', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('access method', '{one}', '{}'); -ERROR: access method "one" does not exist -SELECT pg_get_object_address('access method', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('publication', '{one}', '{}'); -ERROR: publication "one" does not exist -SELECT pg_get_object_address('publication', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 -SELECT pg_get_object_address('subscription', '{one}', '{}'); -ERROR: subscription "one" does not exist -SELECT pg_get_object_address('subscription', '{one,two}', '{}'); -ERROR: name list length must be exactly 1 --- test successful cases -WITH objects (type, name, args) AS (VALUES - ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), - ('table', '{addr_nsp, parttable}'::text[], '{}'::text[]), - ('index', '{addr_nsp, gentable_pkey}', '{}'), - ('index', '{addr_nsp, parttable_pkey}', '{}'), - ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), - -- toast table - ('view', '{addr_nsp, genview}', '{}'), - ('materialized view', '{addr_nsp, genmatview}', '{}'), - ('foreign table', '{addr_nsp, genftable}', '{}'), - ('table column', '{addr_nsp, gentable, b}', '{}'), - ('foreign table column', '{addr_nsp, genftable, a}', '{}'), - ('aggregate', '{addr_nsp, genaggr}', '{int4}'), - ('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'), - ('procedure', '{addr_nsp, proc}', '{int4}'), - ('type', '{pg_catalog._int4}', '{}'), - ('type', '{addr_nsp.gendomain}', '{}'), - ('type', '{addr_nsp.gencomptype}', '{}'), - ('type', '{addr_nsp.genenum}', '{}'), - ('cast', '{int8}', '{int4}'), - ('collation', '{default}', '{}'), - ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), - ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'), - ('conversion', '{pg_catalog, koi8_r_to_mic}', '{}'), - ('default value', '{addr_nsp, gentable, b}', '{}'), - ('language', '{plpgsql}', '{}'), - -- large object - ('operator', '{+}', '{int4, int4}'), - ('operator class', '{btree, int4_ops}', '{}'), - ('operator family', '{btree, integer_ops}', '{}'), - ('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'), - ('function of access method', '{btree,integer_ops,2}', '{integer,integer}'), - ('rule', '{addr_nsp, genview, _RETURN}', '{}'), - ('trigger', '{addr_nsp, gentable, t}', '{}'), - ('schema', '{addr_nsp}', '{}'), - ('text search parser', '{addr_ts_prs}', '{}'), - ('text search dictionary', '{addr_ts_dict}', '{}'), - ('text search template', '{addr_ts_temp}', '{}'), - ('text search configuration', '{addr_ts_conf}', '{}'), - ('role', '{regress_addr_user}', '{}'), - -- database - -- tablespace - ('foreign-data wrapper', '{addr_fdw}', '{}'), - ('server', '{addr_fserv}', '{}'), - ('user mapping', '{regress_addr_user}', '{integer}'), - ('default acl', '{regress_addr_user,public}', '{r}'), - ('default acl', '{regress_addr_user}', '{r}'), - -- extension - -- event trigger - ('policy', '{addr_nsp, gentable, genpol}', '{}'), - ('transform', '{int}', '{sql}'), - ('access method', '{btree}', '{}'), - ('publication', '{addr_pub}', '{}'), - ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), - ('subscription', '{regress_addr_sub}', '{}'), - ('statistics object', '{addr_nsp, gentable_stat}', '{}') - ) -SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, - -- test roundtrip through pg_identify_object_as_address - ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)) = - ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.objsubid)) - FROM objects, pg_get_object_address(type, name, args) addr1, - pg_identify_object_as_address(classid, objid, objsubid) ioa(typ,nms,args), - pg_get_object_address(typ, nms, ioa.args) as addr2 - ORDER BY addr1.classid, addr1.objid, addr1.objsubid; - type | schema | name | identity | ?column? ----------------------------+------------+-------------------+----------------------------------------------------------------------+---------- - default acl | | | for role regress_addr_user in schema public on tables | t - default acl | | | for role regress_addr_user on tables | t - type | pg_catalog | _int4 | integer[] | t - type | addr_nsp | gencomptype | addr_nsp.gencomptype | t - type | addr_nsp | genenum | addr_nsp.genenum | t - type | addr_nsp | gendomain | addr_nsp.gendomain | t - function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | t - aggregate | addr_nsp | | addr_nsp.genaggr(integer) | t - procedure | addr_nsp | | addr_nsp.proc(integer) | t - sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq | t - table | addr_nsp | gentable | addr_nsp.gentable | t - table column | addr_nsp | gentable | addr_nsp.gentable.b | t - index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | t - table | addr_nsp | parttable | addr_nsp.parttable | t - index | addr_nsp | parttable_pkey | addr_nsp.parttable_pkey | t - view | addr_nsp | genview | addr_nsp.genview | t - materialized view | addr_nsp | genmatview | addr_nsp.genmatview | t - foreign table | addr_nsp | genftable | addr_nsp.genftable | t - foreign table column | addr_nsp | genftable | addr_nsp.genftable.a | t - role | | regress_addr_user | regress_addr_user | t - server | | addr_fserv | addr_fserv | t - user mapping | | | regress_addr_user on server integer | t - foreign-data wrapper | | addr_fdw | addr_fdw | t - access method | | btree | btree | t - operator of access method | | | operator 1 (integer, integer) of pg_catalog.integer_ops USING btree | t - function of access method | | | function 2 (integer, integer) of pg_catalog.integer_ops USING btree | t - default value | | | for addr_nsp.gentable.b | t - cast | | | (bigint AS integer) | t - table constraint | addr_nsp | | a_chk on addr_nsp.gentable | t - domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain | t - conversion | pg_catalog | koi8_r_to_mic | pg_catalog.koi8_r_to_mic | t - language | | plpgsql | plpgsql | t - schema | | addr_nsp | addr_nsp | t - operator class | pg_catalog | int4_ops | pg_catalog.int4_ops USING btree | t - operator | pg_catalog | | pg_catalog.+(integer,integer) | t - rule | | | "_RETURN" on addr_nsp.genview | t - trigger | | | t on addr_nsp.gentable | t - operator family | pg_catalog | integer_ops | pg_catalog.integer_ops USING btree | t - policy | | | genpol on addr_nsp.gentable | t - statistics object | addr_nsp | gentable_stat | addr_nsp.gentable_stat | t - collation | pg_catalog | "default" | pg_catalog."default" | t - transform | | | for integer on language sql | t - text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict | t - text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs | t - text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf | t - text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp | t - subscription | | regress_addr_sub | regress_addr_sub | t - publication | | addr_pub | addr_pub | t - publication relation | | | addr_nsp.gentable in publication addr_pub | t -(49 rows) - ---- ---- Cleanup resources ---- -DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to server addr_fserv -drop cascades to foreign table genftable -drop cascades to server integer -drop cascades to user mapping for regress_addr_user on server integer -DROP PUBLICATION addr_pub; -DROP SUBSCRIPTION regress_addr_sub; -DROP SCHEMA addr_nsp CASCADE; -NOTICE: drop cascades to 14 other objects -DETAIL: drop cascades to text search dictionary addr_ts_dict -drop cascades to text search configuration addr_ts_conf -drop cascades to text search template addr_ts_temp -drop cascades to text search parser addr_ts_prs -drop cascades to table gentable -drop cascades to table parttable -drop cascades to view genview -drop cascades to materialized view genmatview -drop cascades to type gencomptype -drop cascades to type genenum -drop cascades to function genaggr(integer) -drop cascades to type gendomain -drop cascades to function trig() -drop cascades to function proc(integer) -DROP OWNED BY regress_addr_user; -DROP USER regress_addr_user; --- --- Checks for invalid objects --- --- Make sure that NULL handling is correct. -\pset null 'NULL' --- Temporarily disable fancy output, so as future additions never create --- a large amount of diffs. -\a\t --- Keep this list in the same order as getObjectIdentityParts() --- in objectaddress.c. -WITH objects (classid, objid, objsubid) AS (VALUES - ('pg_class'::regclass, 0, 0), -- no relation - ('pg_class'::regclass, 'pg_class'::regclass, 100), -- no column for relation - ('pg_proc'::regclass, 0, 0), -- no function - ('pg_type'::regclass, 0, 0), -- no type - ('pg_cast'::regclass, 0, 0), -- no cast - ('pg_collation'::regclass, 0, 0), -- no collation - ('pg_constraint'::regclass, 0, 0), -- no constraint - ('pg_conversion'::regclass, 0, 0), -- no conversion - ('pg_attrdef'::regclass, 0, 0), -- no default attribute - ('pg_language'::regclass, 0, 0), -- no language - ('pg_largeobject'::regclass, 0, 0), -- no large object, no error - ('pg_operator'::regclass, 0, 0), -- no operator - ('pg_opclass'::regclass, 0, 0), -- no opclass, no need to check for no access method - ('pg_opfamily'::regclass, 0, 0), -- no opfamily - ('pg_am'::regclass, 0, 0), -- no access method - ('pg_amop'::regclass, 0, 0), -- no AM operator - ('pg_amproc'::regclass, 0, 0), -- no AM proc - ('pg_rewrite'::regclass, 0, 0), -- no rewrite - ('pg_trigger'::regclass, 0, 0), -- no trigger - ('pg_namespace'::regclass, 0, 0), -- no schema - ('pg_statistic_ext'::regclass, 0, 0), -- no statistics - ('pg_ts_parser'::regclass, 0, 0), -- no TS parser - ('pg_ts_dict'::regclass, 0, 0), -- no TS dictionnary - ('pg_ts_template'::regclass, 0, 0), -- no TS template - ('pg_ts_config'::regclass, 0, 0), -- no TS configuration - ('pg_authid'::regclass, 0, 0), -- no role - ('pg_database'::regclass, 0, 0), -- no database - ('pg_tablespace'::regclass, 0, 0), -- no tablespace - ('pg_foreign_data_wrapper'::regclass, 0, 0), -- no FDW - ('pg_foreign_server'::regclass, 0, 0), -- no server - ('pg_user_mapping'::regclass, 0, 0), -- no user mapping - ('pg_default_acl'::regclass, 0, 0), -- no default ACL - ('pg_extension'::regclass, 0, 0), -- no extension - ('pg_event_trigger'::regclass, 0, 0), -- no event trigger - ('pg_policy'::regclass, 0, 0), -- no policy - ('pg_publication'::regclass, 0, 0), -- no publication - ('pg_publication_rel'::regclass, 0, 0), -- no publication relation - ('pg_subscription'::regclass, 0, 0), -- no subscription - ('pg_transform'::regclass, 0, 0) -- no transformation - ) -SELECT ROW(pg_identify_object(objects.classid, objects.objid, objects.objsubid)) - AS ident, - ROW(pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid)) - AS addr, - pg_describe_object(objects.classid, objects.objid, objects.objsubid) - AS descr -FROM objects -ORDER BY objects.classid, objects.objid, objects.objsubid; -("(""default acl"",,,)")|("(""default acl"",,)")|NULL -("(tablespace,,,)")|("(tablespace,,)")|NULL -("(type,,,)")|("(type,,)")|NULL -("(routine,,,)")|("(routine,,)")|NULL -("(relation,,,)")|("(relation,,)")|NULL -("(""table column"",,,)")|("(""table column"",,)")|NULL -("(role,,,)")|("(role,,)")|NULL -("(database,,,)")|("(database,,)")|NULL -("(server,,,)")|("(server,,)")|NULL -("(""user mapping"",,,)")|("(""user mapping"",,)")|NULL -("(""foreign-data wrapper"",,,)")|("(""foreign-data wrapper"",,)")|NULL -("(""access method"",,,)")|("(""access method"",,)")|NULL -("(""operator of access method"",,,)")|("(""operator of access method"",,)")|NULL -("(""function of access method"",,,)")|("(""function of access method"",,)")|NULL -("(""default value"",,,)")|("(""default value"",,)")|NULL -("(cast,,,)")|("(cast,,)")|NULL -("(constraint,,,)")|("(constraint,,)")|NULL -("(conversion,,,)")|("(conversion,,)")|NULL -("(language,,,)")|("(language,,)")|NULL -("(""large object"",,,)")|("(""large object"",,)")|NULL -("(schema,,,)")|("(schema,,)")|NULL -("(""operator class"",,,)")|("(""operator class"",,)")|NULL -("(operator,,,)")|("(operator,,)")|NULL -("(rule,,,)")|("(rule,,)")|NULL -("(trigger,,,)")|("(trigger,,)")|NULL -("(""operator family"",,,)")|("(""operator family"",,)")|NULL -("(extension,,,)")|("(extension,,)")|NULL -("(policy,,,)")|("(policy,,)")|NULL -("(""statistics object"",,,)")|("(""statistics object"",,)")|NULL -("(collation,,,)")|("(collation,,)")|NULL -("(""event trigger"",,,)")|("(""event trigger"",,)")|NULL -("(transform,,,)")|("(transform,,)")|NULL -("(""text search dictionary"",,,)")|("(""text search dictionary"",,)")|NULL -("(""text search parser"",,,)")|("(""text search parser"",,)")|NULL -("(""text search configuration"",,,)")|("(""text search configuration"",,)")|NULL -("(""text search template"",,,)")|("(""text search template"",,)")|NULL -("(subscription,,,)")|("(subscription,,)")|NULL -("(publication,,,)")|("(publication,,)")|NULL -("(""publication relation"",,,)")|("(""publication relation"",,)")|NULL --- restore normal output mode -\a\t +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tablesample.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tablesample.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tablesample.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tablesample.out 2021-10-30 22:04:14 -0600 @@ -1,331 +1,2 @@ -CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); --- use fillfactor so we don't have to load too much data to get multiple pages -INSERT INTO test_tablesample - SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i); -SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (0); - id ----- - 3 - 4 - 5 - 6 - 7 - 8 -(6 rows) - -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (0); - id ----- -(0 rows) - -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); - id ----- - 3 - 4 - 5 - 6 - 7 - 8 -(6 rows) - -SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (0); - id ----- - 4 - 5 - 6 - 7 - 8 -(5 rows) - -SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (0); - id ----- - 7 -(1 row) - --- 100% should give repeatable count results (ie, all rows) in any case -SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100); - count -------- - 10 -(1 row) - -SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (1+2); - count -------- - 10 -(1 row) - -SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (0.4); - count -------- - 10 -(1 row) - -CREATE VIEW test_tablesample_v1 AS - SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2); -CREATE VIEW test_tablesample_v2 AS - SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99); -\d+ test_tablesample_v1 - View "public.test_tablesample_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+---------+------------- - id | integer | | | | plain | -View definition: - SELECT test_tablesample.id - FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); - -\d+ test_tablesample_v2 - View "public.test_tablesample_v2" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+---------+------------- - id | integer | | | | plain | -View definition: - SELECT test_tablesample.id - FROM test_tablesample TABLESAMPLE system (99); - --- check a sampled query doesn't affect cursor in progress -BEGIN; -DECLARE tablesample_cur SCROLL CURSOR FOR - SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); -FETCH FIRST FROM tablesample_cur; - id ----- - 3 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 4 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 5 -(1 row) - -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); - id ----- - 3 - 4 - 5 - 6 - 7 - 8 -(6 rows) - -FETCH NEXT FROM tablesample_cur; - id ----- - 6 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 7 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 8 -(1 row) - -FETCH FIRST FROM tablesample_cur; - id ----- - 3 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 4 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 5 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 6 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 7 -(1 row) - -FETCH NEXT FROM tablesample_cur; - id ----- - 8 -(1 row) - -CLOSE tablesample_cur; -END; -EXPLAIN (COSTS OFF) - SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (2); - QUERY PLAN --------------------------------------------------------------------- - Sample Scan on test_tablesample - Sampling: system ('50'::real) REPEATABLE ('2'::double precision) -(2 rows) - -EXPLAIN (COSTS OFF) - SELECT * FROM test_tablesample_v1; - QUERY PLAN --------------------------------------------------------------------- - Sample Scan on test_tablesample - Sampling: system ('20'::real) REPEATABLE ('2'::double precision) -(2 rows) - --- check inheritance behavior -explain (costs off) - select count(*) from person tablesample bernoulli (100); - QUERY PLAN -------------------------------------------------- - Aggregate - -> Append - -> Sample Scan on person person_1 - Sampling: bernoulli ('100'::real) - -> Sample Scan on emp person_2 - Sampling: bernoulli ('100'::real) - -> Sample Scan on student person_3 - Sampling: bernoulli ('100'::real) - -> Sample Scan on stud_emp person_4 - Sampling: bernoulli ('100'::real) -(10 rows) - -select count(*) from person tablesample bernoulli (100); - count -------- - 58 -(1 row) - -select count(*) from person; - count -------- - 58 -(1 row) - --- check that collations get assigned within the tablesample arguments -SELECT count(*) FROM test_tablesample TABLESAMPLE bernoulli (('1'::text < '0'::text)::int); - count -------- - 0 -(1 row) - --- check behavior during rescans, as well as correct handling of min/max pct -select * from - (values (0),(100)) v(pct), - lateral (select count(*) from tenk1 tablesample bernoulli (pct)) ss; - pct | count ------+------- - 0 | 0 - 100 | 10000 -(2 rows) - -select * from - (values (0),(100)) v(pct), - lateral (select count(*) from tenk1 tablesample system (pct)) ss; - pct | count ------+------- - 0 | 0 - 100 | 10000 -(2 rows) - -explain (costs off) -select pct, count(unique1) from - (values (0),(100)) v(pct), - lateral (select * from tenk1 tablesample bernoulli (pct)) ss - group by pct; - QUERY PLAN --------------------------------------------------------- - HashAggregate - Group Key: "*VALUES*".column1 - -> Nested Loop - -> Values Scan on "*VALUES*" - -> Sample Scan on tenk1 - Sampling: bernoulli ("*VALUES*".column1) -(6 rows) - -select pct, count(unique1) from - (values (0),(100)) v(pct), - lateral (select * from tenk1 tablesample bernoulli (pct)) ss - group by pct; - pct | count ------+------- - 100 | 10000 -(1 row) - -select pct, count(unique1) from - (values (0),(100)) v(pct), - lateral (select * from tenk1 tablesample system (pct)) ss - group by pct; - pct | count ------+------- - 100 | 10000 -(1 row) - --- errors -SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1); -ERROR: tablesample method foobar does not exist -LINE 1: SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1); - ^ -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (NULL); -ERROR: TABLESAMPLE parameter cannot be null -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL); -ERROR: TABLESAMPLE REPEATABLE parameter cannot be null -SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (-1); -ERROR: sample percentage must be between 0 and 100 -SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (200); -ERROR: sample percentage must be between 0 and 100 -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (-1); -ERROR: sample percentage must be between 0 and 100 -SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (200); -ERROR: sample percentage must be between 0 and 100 -SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1); -ERROR: TABLESAMPLE clause can only be applied to tables and materialized views -LINE 1: SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1)... - ^ -INSERT INTO test_tablesample_v1 VALUES(1); -ERROR: cannot insert into view "test_tablesample_v1" -DETAIL: Views containing TABLESAMPLE are not automatically updatable. -HINT: To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule. -WITH query_select AS (SELECT * FROM test_tablesample) -SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1); -ERROR: TABLESAMPLE clause can only be applied to tables and materialized views -LINE 2: SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEA... - ^ -SELECT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPLE BERNOULLI (5); -ERROR: syntax error at or near "TABLESAMPLE" -LINE 1: ...CT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPL... - ^ --- check partitioned tables support tablesample -create table parted_sample (a int) partition by list (a); -create table parted_sample_1 partition of parted_sample for values in (1); -create table parted_sample_2 partition of parted_sample for values in (2); -explain (costs off) - select * from parted_sample tablesample bernoulli (100); - QUERY PLAN -------------------------------------------- - Append - -> Sample Scan on parted_sample_1 - Sampling: bernoulli ('100'::real) - -> Sample Scan on parted_sample_2 - Sampling: bernoulli ('100'::real) -(5 rows) - -drop table parted_sample, parted_sample_1, parted_sample_2; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/groupingsets.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/groupingsets.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/groupingsets.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/groupingsets.out 2021-10-30 22:04:14 -0600 @@ -1,2043 +1,2 @@ --- --- grouping sets --- --- test data sources -create temp view gstest1(a,b,v) - as values (1,1,10),(1,1,11),(1,2,12),(1,2,13),(1,3,14), - (2,3,15), - (3,3,16),(3,4,17), - (4,1,18),(4,1,19); -create temp table gstest2 (a integer, b integer, c integer, d integer, - e integer, f integer, g integer, h integer); -copy gstest2 from stdin; -create temp table gstest3 (a integer, b integer, c integer, d integer); -copy gstest3 from stdin; -alter table gstest3 add primary key (a); -create temp table gstest4(id integer, v integer, - unhashable_col bit(4), unsortable_col xid); -insert into gstest4 -values (1,1,b'0000','1'), (2,2,b'0001','1'), - (3,4,b'0010','2'), (4,8,b'0011','2'), - (5,16,b'0000','2'), (6,32,b'0001','2'), - (7,64,b'0010','1'), (8,128,b'0011','1'); -create temp table gstest_empty (a integer, b integer, v integer); -create function gstest_data(v integer, out a integer, out b integer) - returns setof record - as $f$ - begin - return query select v, i from generate_series(1,3) i; - end; - $f$ language plpgsql; --- basic functionality -set enable_hashagg = false; -- test hashing explicitly later --- simple rollup with multiple plain aggregates, with and without ordering --- (and with ordering differing from grouping) -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by rollup (a,b); - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 1 | | 1 | 60 | 5 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 2 | | 1 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 3 | | 1 | 33 | 2 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - 4 | | 1 | 37 | 2 | 19 - | | 3 | 145 | 10 | 19 -(12 rows) - -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by rollup (a,b) order by a,b; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 1 | | 1 | 60 | 5 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 2 | | 1 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 3 | | 1 | 33 | 2 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - 4 | | 1 | 37 | 2 | 19 - | | 3 | 145 | 10 | 19 -(12 rows) - -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by rollup (a,b) order by b desc, a; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | | 1 | 60 | 5 | 14 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 4 | | 1 | 37 | 2 | 19 - | | 3 | 145 | 10 | 19 - 3 | 4 | 0 | 17 | 1 | 17 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 1 | 0 | 21 | 2 | 11 - 4 | 1 | 0 | 37 | 2 | 19 -(12 rows) - -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by rollup (a,b) order by coalesce(a,0)+coalesce(b,0); - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - | | 3 | 145 | 10 | 19 - 1 | | 1 | 60 | 5 | 14 - 1 | 1 | 0 | 21 | 2 | 11 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 4 | | 1 | 37 | 2 | 19 - 4 | 1 | 0 | 37 | 2 | 19 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 -(12 rows) - --- various types of ordered aggs -select a, b, grouping(a,b), - array_agg(v order by v), - string_agg(v::text, ':' order by v desc), - percentile_disc(0.5) within group (order by v), - rank(1,2,12) within group (order by a,b,v) - from gstest1 group by rollup (a,b) order by a,b; - a | b | grouping | array_agg | string_agg | percentile_disc | rank ----+---+----------+---------------------------------+-------------------------------+-----------------+------ - 1 | 1 | 0 | {10,11} | 11:10 | 10 | 3 - 1 | 2 | 0 | {12,13} | 13:12 | 12 | 1 - 1 | 3 | 0 | {14} | 14 | 14 | 1 - 1 | | 1 | {10,11,12,13,14} | 14:13:12:11:10 | 12 | 3 - 2 | 3 | 0 | {15} | 15 | 15 | 1 - 2 | | 1 | {15} | 15 | 15 | 1 - 3 | 3 | 0 | {16} | 16 | 16 | 1 - 3 | 4 | 0 | {17} | 17 | 17 | 1 - 3 | | 1 | {16,17} | 17:16 | 16 | 1 - 4 | 1 | 0 | {18,19} | 19:18 | 18 | 1 - 4 | | 1 | {18,19} | 19:18 | 18 | 1 - | | 3 | {10,11,12,13,14,15,16,17,18,19} | 19:18:17:16:15:14:13:12:11:10 | 14 | 3 -(12 rows) - --- test usage of grouped columns in direct args of aggs -select grouping(a), a, array_agg(b), - rank(a) within group (order by b nulls first), - rank(a) within group (order by b nulls last) - from (values (1,1),(1,4),(1,5),(3,1),(3,2)) v(a,b) - group by rollup (a) order by a; - grouping | a | array_agg | rank | rank -----------+---+-------------+------+------ - 0 | 1 | {1,4,5} | 1 | 1 - 0 | 3 | {1,2} | 3 | 3 - 1 | | {1,4,5,1,2} | 1 | 6 -(3 rows) - --- nesting with window functions -select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by rollup (a,b) order by rsum, a, b; - a | b | sum | rsum ----+---+-----+------ - 1 | 1 | 8 | 8 - 1 | 2 | 2 | 10 - 1 | | 10 | 20 - 2 | 2 | 2 | 22 - 2 | | 2 | 24 - | | 12 | 36 -(6 rows) - --- nesting with grouping sets -select sum(c) from gstest2 - group by grouping sets((), grouping sets((), grouping sets(()))) - order by 1 desc; - sum ------ - 12 - 12 - 12 -(3 rows) - -select sum(c) from gstest2 - group by grouping sets((), grouping sets((), grouping sets(((a, b))))) - order by 1 desc; - sum ------ - 12 - 12 - 8 - 2 - 2 -(5 rows) - -select sum(c) from gstest2 - group by grouping sets(grouping sets(rollup(c), grouping sets(cube(c)))) - order by 1 desc; - sum ------ - 12 - 12 - 6 - 6 - 6 - 6 -(6 rows) - -select sum(c) from gstest2 - group by grouping sets(a, grouping sets(a, cube(b))) - order by 1 desc; - sum ------ - 12 - 10 - 10 - 8 - 4 - 2 - 2 -(7 rows) - -select sum(c) from gstest2 - group by grouping sets(grouping sets((a, (b)))) - order by 1 desc; - sum ------ - 8 - 2 - 2 -(3 rows) - -select sum(c) from gstest2 - group by grouping sets(grouping sets((a, b))) - order by 1 desc; - sum ------ - 8 - 2 - 2 -(3 rows) - -select sum(c) from gstest2 - group by grouping sets(grouping sets(a, grouping sets(a), a)) - order by 1 desc; - sum ------ - 10 - 10 - 10 - 2 - 2 - 2 -(6 rows) - -select sum(c) from gstest2 - group by grouping sets(grouping sets(a, grouping sets(a, grouping sets(a), ((a)), a, grouping sets(a), (a)), a)) - order by 1 desc; - sum ------ - 10 - 10 - 10 - 10 - 10 - 10 - 10 - 10 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 -(16 rows) - -select sum(c) from gstest2 - group by grouping sets((a,(a,b)), grouping sets((a,(a,b)),a)) - order by 1 desc; - sum ------ - 10 - 8 - 8 - 2 - 2 - 2 - 2 - 2 -(8 rows) - --- empty input: first is 0 rows, second 1, third 3 etc. -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); - a | b | sum | count ----+---+-----+------- -(0 rows) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),()); - a | b | sum | count ----+---+-----+------- - | | | 0 -(1 row) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); - a | b | sum | count ----+---+-----+------- - | | | 0 - | | | 0 - | | | 0 -(3 rows) - -select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); - sum | count ------+------- - | 0 - | 0 - | 0 -(3 rows) - --- empty input with joins tests some important code paths -select t1.a, t2.b, sum(t1.v), count(*) from gstest_empty t1, gstest_empty t2 - group by grouping sets ((t1.a,t2.b),()); - a | b | sum | count ----+---+-----+------- - | | | 0 -(1 row) - --- simple joins, var resolution, GROUPING on join vars -select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) - from gstest1 t1, gstest2 t2 - group by grouping sets ((t1.a, t2.b), ()); - a | b | grouping | sum | max ----+---+----------+------+----- - 1 | 1 | 0 | 420 | 1 - 1 | 2 | 0 | 120 | 2 - 2 | 1 | 0 | 105 | 1 - 2 | 2 | 0 | 30 | 2 - 3 | 1 | 0 | 231 | 1 - 3 | 2 | 0 | 66 | 2 - 4 | 1 | 0 | 259 | 1 - 4 | 2 | 0 | 74 | 2 - | | 3 | 1305 | 2 -(9 rows) - -select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) - from gstest1 t1 join gstest2 t2 on (t1.a=t2.a) - group by grouping sets ((t1.a, t2.b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 420 | 1 - 1 | 2 | 0 | 60 | 1 - 2 | 2 | 0 | 15 | 2 - | | 3 | 495 | 2 -(4 rows) - -select a, b, grouping(a, b), sum(t1.v), max(t2.c) - from gstest1 t1 join gstest2 t2 using (a,b) - group by grouping sets ((a, b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 147 | 2 - 1 | 2 | 0 | 25 | 2 - | | 3 | 172 | 2 -(3 rows) - --- check that functionally dependent cols are not nulled -select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - a | d | grouping ----+---+---------- - 1 | 1 | 1 - 2 | 2 | 1 - 1 | 1 | 2 - 2 | 2 | 2 -(4 rows) - --- check that distinct grouping columns are kept separate --- even if they are equal() -explain (costs off) -select g as alias1, g as alias2 - from generate_series(1,3) g - group by alias1, rollup(alias2); - QUERY PLAN ------------------------------------------------- - GroupAggregate - Group Key: g, g - Group Key: g - -> Sort - Sort Key: g - -> Function Scan on generate_series g -(6 rows) - -select g as alias1, g as alias2 - from generate_series(1,3) g - group by alias1, rollup(alias2); - alias1 | alias2 ---------+-------- - 1 | 1 - 1 | - 2 | 2 - 2 | - 3 | 3 - 3 | -(6 rows) - --- check that pulled-up subquery outputs still go to null when appropriate -select four, x - from (select four, ten, 'foo'::text as x from tenk1) as t - group by grouping sets (four, x) - having x = 'foo'; - four | x -------+----- - | foo -(1 row) - -select four, x || 'x' - from (select four, ten, 'foo'::text as x from tenk1) as t - group by grouping sets (four, x) - order by four; - four | ?column? -------+---------- - 0 | - 1 | - 2 | - 3 | - | foox -(5 rows) - -select (x+y)*1, sum(z) - from (select 1 as x, 2 as y, 3 as z) s - group by grouping sets (x+y, x); - ?column? | sum -----------+----- - 3 | 3 - | 3 -(2 rows) - -select x, not x as not_x, q2 from - (select *, q1 = 1 as x from int8_tbl i1) as t - group by grouping sets(x, q2) - order by x, q2; - x | not_x | q2 ----+-------+------------------- - f | t | - | | -4567890123456789 - | | 123 - | | 456 - | | 4567890123456789 -(5 rows) - --- check qual push-down rules for a subquery with grouping sets -explain (verbose, costs off) -select * from ( - select 1 as x, q1, sum(q2) - from int8_tbl i1 - group by grouping sets(1, 2) -) ss -where x = 1 and q1 = 123; - QUERY PLAN --------------------------------------------- - Subquery Scan on ss - Output: ss.x, ss.q1, ss.sum - Filter: ((ss.x = 1) AND (ss.q1 = 123)) - -> GroupAggregate - Output: (1), i1.q1, sum(i1.q2) - Group Key: 1 - Sort Key: i1.q1 - Group Key: i1.q1 - -> Seq Scan on public.int8_tbl i1 - Output: 1, i1.q1, i1.q2 -(10 rows) - -select * from ( - select 1 as x, q1, sum(q2) - from int8_tbl i1 - group by grouping sets(1, 2) -) ss -where x = 1 and q1 = 123; - x | q1 | sum ----+----+----- -(0 rows) - --- simple rescan tests -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by rollup (a,b); - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | | 9 -(9 rows) - -select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by rollup (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 3: lateral (select a, b, sum(v.x) from gstest_data(v.x) ... - ^ --- min max optimization should still work with GROUP BY () -explain (costs off) - select min(unique1) from tenk1 GROUP BY (); - QUERY PLAN ------------------------------------------------------------- - Result - InitPlan 1 (returns $0) - -> Limit - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 IS NOT NULL) -(5 rows) - --- Views with GROUPING SET queries -CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) - from gstest2 group by rollup ((a,b,c),(c,d)); -NOTICE: view "gstest_view" will be a temporary view -select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); -(1 row) - --- Nested queries with 3 or more levels of nesting -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 0 - 0 -(3 rows) - -select(select (select grouping(e,f) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 1 - 3 -(3 rows) - -select(select (select grouping(c) from (values (1)) v2(c) GROUP BY c) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 0 - 0 -(3 rows) - --- Combinations of operations -select a, b, c, d from gstest2 group by rollup(a,b),grouping sets(c,d); - a | b | c | d ----+---+---+--- - 1 | 1 | 1 | - 1 | | 1 | - | | 1 | - 1 | 1 | 2 | - 1 | 2 | 2 | - 1 | | 2 | - 2 | 2 | 2 | - 2 | | 2 | - | | 2 | - 1 | 1 | | 1 - 1 | | | 1 - | | | 1 - 1 | 1 | | 2 - 1 | 2 | | 2 - 1 | | | 2 - 2 | 2 | | 2 - 2 | | | 2 - | | | 2 -(18 rows) - -select a, b from (values (1,2),(2,3)) v(a,b) group by a,b, grouping sets(a); - a | b ----+--- - 1 | 2 - 2 | 3 -(2 rows) - --- Tests for chained aggregates -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - | | 3 | 21 | 2 | 11 - | | 3 | 21 | 2 | 11 - | | 3 | 25 | 2 | 13 - | | 3 | 25 | 2 | 13 - | | 3 | 14 | 1 | 14 - | | 3 | 14 | 1 | 14 - | | 3 | 15 | 1 | 15 - | | 3 | 15 | 1 | 15 - | | 3 | 16 | 1 | 16 - | | 3 | 16 | 1 | 16 - | | 3 | 17 | 1 | 17 - | | 3 | 17 | 1 | 17 - | | 3 | 37 | 2 | 19 - | | 3 | 37 | 2 | 19 -(21 rows) - -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP((e+1),(f+1)); - grouping ----------- - 0 - 0 - 0 -(3 rows) - -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY CUBE((e+1),(f+1)) ORDER BY (e+1),(f+1); - grouping ----------- - 0 - 0 - 0 - 0 -(4 rows) - -select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - a | b | sum | rsum ----+---+-----+------ - 1 | 1 | 8 | 8 - 1 | 2 | 2 | 10 - 1 | | 10 | 20 - 2 | 2 | 2 | 22 - 2 | | 2 | 24 - | 1 | 8 | 32 - | 2 | 4 | 36 - | | 12 | 48 -(8 rows) - -select a, b, sum(c) from (values (1,1,10),(1,1,11),(1,2,12),(1,2,13),(1,3,14),(2,3,15),(3,3,16),(3,4,17),(4,1,18),(4,1,19)) v(a,b,c) group by rollup (a,b); - a | b | sum ----+---+----- - 1 | 1 | 21 - 1 | 2 | 25 - 1 | 3 | 14 - 1 | | 60 - 2 | 3 | 15 - 2 | | 15 - 3 | 3 | 16 - 3 | 4 | 17 - 3 | | 33 - 4 | 1 | 37 - 4 | | 37 - | | 145 -(12 rows) - -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 - | | 9 -(12 rows) - --- Test reordering of grouping sets -explain (costs off) -select * from gstest1 group by grouping sets((a,b,v),(v)) order by v,b,a; - QUERY PLAN ------------------------------------------------------------------------------- - GroupAggregate - Group Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1 - Group Key: "*VALUES*".column3 - -> Sort - Sort Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1 - -> Values Scan on "*VALUES*" -(6 rows) - --- Agg level check. This query should error out. -select (select grouping(a,b) from gstest2) from gstest2 group by a,b; -ERROR: arguments to GROUPING must be grouping expressions of the associated query level -LINE 1: select (select grouping(a,b) from gstest2) from gstest2 grou... - ^ ---Nested queries -select a, b, sum(c), count(*) from gstest2 group by grouping sets (rollup(a,b),a); - a | b | sum | count ----+---+-----+------- - 1 | 1 | 8 | 7 - 1 | 2 | 2 | 1 - 1 | | 10 | 8 - 1 | | 10 | 8 - 2 | 2 | 2 | 1 - 2 | | 2 | 1 - 2 | | 2 | 1 - | | 12 | 9 -(8 rows) - --- HAVING queries -select ten, sum(distinct four) from onek a -group by grouping sets((ten,four),(ten)) -having exists (select 1 from onek b where sum(distinct a.four) = b.four); - ten | sum ------+----- - 0 | 0 - 0 | 2 - 0 | 2 - 1 | 1 - 1 | 3 - 2 | 0 - 2 | 2 - 2 | 2 - 3 | 1 - 3 | 3 - 4 | 0 - 4 | 2 - 4 | 2 - 5 | 1 - 5 | 3 - 6 | 0 - 6 | 2 - 6 | 2 - 7 | 1 - 7 | 3 - 8 | 0 - 8 | 2 - 8 | 2 - 9 | 1 - 9 | 3 -(25 rows) - --- Tests around pushdown of HAVING clauses, partially testing against previous bugs -select a,count(*) from gstest2 group by rollup(a) order by a; - a | count ----+------- - 1 | 8 - 2 | 1 - | 9 -(3 rows) - -select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; - a | count ----+------- - 2 | 1 - | 9 -(2 rows) - -explain (costs off) - select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; - QUERY PLAN ----------------------------------- - GroupAggregate - Group Key: a - Group Key: () - Filter: (a IS DISTINCT FROM 1) - -> Sort - Sort Key: a - -> Seq Scan on gstest2 -(7 rows) - -select v.c, (select count(*) from gstest2 group by () having v.c) - from (values (false),(true)) v(c) order by v.c; - c | count ----+------- - f | - t | 9 -(2 rows) - -explain (costs off) - select v.c, (select count(*) from gstest2 group by () having v.c) - from (values (false),(true)) v(c) order by v.c; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - SubPlan 1 - -> Aggregate - Group Key: () - Filter: "*VALUES*".column1 - -> Result - One-Time Filter: "*VALUES*".column1 - -> Seq Scan on gstest2 -(10 rows) - --- HAVING with GROUPING queries -select ten, grouping(ten) from onek -group by grouping sets(ten) having grouping(ten) >= 0 -order by 2,1; - ten | grouping ------+---------- - 0 | 0 - 1 | 0 - 2 | 0 - 3 | 0 - 4 | 0 - 5 | 0 - 6 | 0 - 7 | 0 - 8 | 0 - 9 | 0 -(10 rows) - -select ten, grouping(ten) from onek -group by grouping sets(ten, four) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 - | 1 - | 1 - | 1 -(4 rows) - -select ten, grouping(ten) from onek -group by rollup(ten) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 -(1 row) - -select ten, grouping(ten) from onek -group by cube(ten) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 -(1 row) - -select ten, grouping(ten) from onek -group by (ten) having grouping(ten) >= 0 -order by 2,1; - ten | grouping ------+---------- - 0 | 0 - 1 | 0 - 2 | 0 - 3 | 0 - 4 | 0 - 5 | 0 - 6 | 0 - 7 | 0 - 8 | 0 - 9 | 0 -(10 rows) - --- FILTER queries -select ten, sum(distinct four) filter (where four::text ~ '123') from onek a -group by rollup(ten); - ten | sum ------+----- - 0 | - 1 | - 2 | - 3 | - 4 | - 5 | - 6 | - 7 | - 8 | - 9 | - | -(11 rows) - --- More rescan tests -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | 0 | 50 - 1 | 1 | 0 | 2 | 50 - 1 | 1 | 0 | 4 | 50 - 1 | 1 | 0 | 6 | 50 - 1 | 1 | 0 | 8 | 50 - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | 1 | 50 - 1 | 1 | 1 | 3 | 50 - 1 | 1 | 1 | 5 | 50 - 1 | 1 | 1 | 7 | 50 - 1 | 1 | 1 | 9 | 50 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | 0 | 50 - 1 | 1 | 2 | 2 | 50 - 1 | 1 | 2 | 4 | 50 - 1 | 1 | 2 | 6 | 50 - 1 | 1 | 2 | 8 | 50 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | 1 | 50 - 1 | 1 | 3 | 3 | 50 - 1 | 1 | 3 | 5 | 50 - 1 | 1 | 3 | 7 | 50 - 1 | 1 | 3 | 9 | 50 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 1 | 1 | | | 1000 - 2 | 2 | 0 | 0 | 50 - 2 | 2 | 0 | 2 | 50 - 2 | 2 | 0 | 4 | 50 - 2 | 2 | 0 | 6 | 50 - 2 | 2 | 0 | 8 | 50 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | 1 | 50 - 2 | 2 | 1 | 3 | 50 - 2 | 2 | 1 | 5 | 50 - 2 | 2 | 1 | 7 | 50 - 2 | 2 | 1 | 9 | 50 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | 0 | 50 - 2 | 2 | 2 | 2 | 50 - 2 | 2 | 2 | 4 | 50 - 2 | 2 | 2 | 6 | 50 - 2 | 2 | 2 | 8 | 50 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | 1 | 50 - 2 | 2 | 3 | 3 | 50 - 2 | 2 | 3 | 5 | 50 - 2 | 2 | 3 | 7 | 50 - 2 | 2 | 3 | 9 | 50 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 - 2 | 2 | | | 1000 -(70 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ------------------------------------------------------------------------------------------------------------------------------------------------------- - {"(1,0,0,250)","(1,0,2,250)","(1,0,,500)","(1,1,1,250)","(1,1,3,250)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)","(1,,,1000)"} - {"(2,0,0,250)","(2,0,2,250)","(2,0,,500)","(2,1,1,250)","(2,1,3,250)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)","(2,,,1000)"} -(2 rows) - --- Grouping on text columns -select sum(ten) from onek group by two, rollup(four::text) order by 1; - sum ------- - 1000 - 1000 - 1250 - 1250 - 2000 - 2500 -(6 rows) - -select sum(ten) from onek group by rollup(four::text), two order by 1; - sum ------- - 1000 - 1000 - 1250 - 1250 - 2000 - 2500 -(6 rows) - --- hashing support -set enable_hashagg = true; --- failure cases -select count(*) from gstest4 group by rollup(unhashable_col,unsortable_col); -ERROR: could not implement GROUP BY -DETAIL: Some of the datatypes only support hashing, while others only support sorting. -select array_agg(v order by v) from gstest4 group by grouping sets ((id,unsortable_col),(id)); -ERROR: could not implement GROUP BY -DETAIL: Some of the datatypes only support hashing, while others only support sorting. --- simple cases -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | | 1 | 60 | 5 | 14 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 4 | | 1 | 37 | 2 | 19 - | 1 | 2 | 58 | 4 | 19 - | 2 | 2 | 25 | 2 | 13 - | 3 | 2 | 45 | 3 | 16 - | 4 | 2 | 17 | 1 | 17 -(8 rows) - -explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; - QUERY PLAN --------------------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), "*VALUES*".column1, "*VALUES*".column2 - -> HashAggregate - Hash Key: "*VALUES*".column1 - Hash Key: "*VALUES*".column2 - -> Values Scan on "*VALUES*" -(6 rows) - -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by cube(a,b) order by 3,1,2; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - 1 | | 1 | 60 | 5 | 14 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 4 | | 1 | 37 | 2 | 19 - | 1 | 2 | 58 | 4 | 19 - | 2 | 2 | 25 | 2 | 13 - | 3 | 2 | 45 | 3 | 16 - | 4 | 2 | 17 | 1 | 17 - | | 3 | 145 | 10 | 19 -(16 rows) - -explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by cube(a,b) order by 3,1,2; - QUERY PLAN --------------------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), "*VALUES*".column1, "*VALUES*".column2 - -> MixedAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: "*VALUES*".column1 - Hash Key: "*VALUES*".column2 - Group Key: () - -> Values Scan on "*VALUES*" -(8 rows) - --- shouldn't try and hash -explain (costs off) - select a, b, grouping(a,b), array_agg(v order by v) - from gstest1 group by cube(a,b); - QUERY PLAN ----------------------------------------------------------- - GroupAggregate - Group Key: "*VALUES*".column1, "*VALUES*".column2 - Group Key: "*VALUES*".column1 - Group Key: () - Sort Key: "*VALUES*".column2 - Group Key: "*VALUES*".column2 - -> Sort - Sort Key: "*VALUES*".column1, "*VALUES*".column2 - -> Values Scan on "*VALUES*" -(9 rows) - --- unsortable cases -select unsortable_col, count(*) - from gstest4 group by grouping sets ((unsortable_col),(unsortable_col)) - order by unsortable_col::text; - unsortable_col | count -----------------+------- - 1 | 4 - 1 | 4 - 2 | 4 - 2 | 4 -(4 rows) - --- mixed hashable/sortable cases -select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) - order by 3, 5; - unhashable_col | unsortable_col | grouping | count | sum -----------------+----------------+----------+-------+----- - 0000 | | 1 | 2 | 17 - 0001 | | 1 | 2 | 34 - 0010 | | 1 | 2 | 68 - 0011 | | 1 | 2 | 136 - | 2 | 2 | 4 | 60 - | 1 | 2 | 4 | 195 -(6 rows) - -explain (costs off) - select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) - order by 3,5; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: (GROUPING(unhashable_col, unsortable_col)), (sum(v)) - -> MixedAggregate - Hash Key: unsortable_col - Group Key: unhashable_col - -> Sort - Sort Key: unhashable_col - -> Seq Scan on gstest4 -(8 rows) - -select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) - order by 3,5; - unhashable_col | unsortable_col | grouping | count | sum -----------------+----------------+----------+-------+----- - 0000 | | 1 | 1 | 1 - 0001 | | 1 | 1 | 2 - 0010 | | 1 | 1 | 4 - 0011 | | 1 | 1 | 8 - 0000 | | 1 | 1 | 16 - 0001 | | 1 | 1 | 32 - 0010 | | 1 | 1 | 64 - 0011 | | 1 | 1 | 128 - | 1 | 2 | 1 | 1 - | 1 | 2 | 1 | 2 - | 2 | 2 | 1 | 4 - | 2 | 2 | 1 | 8 - | 2 | 2 | 1 | 16 - | 2 | 2 | 1 | 32 - | 1 | 2 | 1 | 64 - | 1 | 2 | 1 | 128 -(16 rows) - -explain (costs off) - select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) - order by 3,5; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: (GROUPING(unhashable_col, unsortable_col)), (sum(v)) - -> MixedAggregate - Hash Key: v, unsortable_col - Group Key: v, unhashable_col - -> Sort - Sort Key: v, unhashable_col - -> Seq Scan on gstest4 -(8 rows) - --- empty input: first is 0 rows, second 1, third 3 etc. -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); - a | b | sum | count ----+---+-----+------- -(0 rows) - -explain (costs off) - select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); - QUERY PLAN --------------------------------- - HashAggregate - Hash Key: a, b - Hash Key: a - -> Seq Scan on gstest_empty -(4 rows) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),()); - a | b | sum | count ----+---+-----+------- - | | | 0 -(1 row) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); - a | b | sum | count ----+---+-----+------- - | | | 0 - | | | 0 - | | | 0 -(3 rows) - -explain (costs off) - select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); - QUERY PLAN --------------------------------- - MixedAggregate - Hash Key: a, b - Group Key: () - Group Key: () - Group Key: () - -> Seq Scan on gstest_empty -(6 rows) - -select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); - sum | count ------+------- - | 0 - | 0 - | 0 -(3 rows) - -explain (costs off) - select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); - QUERY PLAN --------------------------------- - Aggregate - Group Key: () - Group Key: () - Group Key: () - -> Seq Scan on gstest_empty -(5 rows) - --- check that functionally dependent cols are not nulled -select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - a | d | grouping ----+---+---------- - 1 | 1 | 1 - 2 | 2 | 1 - 1 | 1 | 2 - 2 | 2 | 2 -(4 rows) - -explain (costs off) - select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - QUERY PLAN ---------------------------- - HashAggregate - Hash Key: a, b - Hash Key: a, c - -> Seq Scan on gstest3 -(4 rows) - --- simple rescan tests -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by grouping sets (a,b) - order by 1, 2, 3; - a | b | sum ----+---+----- - 1 | | 3 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 -(5 rows) - -explain (costs off) - select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by grouping sets (a,b) - order by 3, 1, 2; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: (sum("*VALUES*".column1)), gstest_data.a, gstest_data.b - -> HashAggregate - Hash Key: gstest_data.a - Hash Key: gstest_data.b - -> Nested Loop - -> Values Scan on "*VALUES*" - -> Function Scan on gstest_data -(8 rows) - -select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 3: lateral (select a, b, sum(v.x) from gstest_data(v.x) ... - ^ -explain (costs off) - select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 4: lateral (select a, b, sum(v.x) from gstest_data(v.x... - ^ --- Tests for chained aggregates -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - | | 3 | 21 | 2 | 11 - | | 3 | 21 | 2 | 11 - | | 3 | 25 | 2 | 13 - | | 3 | 25 | 2 | 13 - | | 3 | 14 | 1 | 14 - | | 3 | 14 | 1 | 14 - | | 3 | 15 | 1 | 15 - | | 3 | 15 | 1 | 15 - | | 3 | 16 | 1 | 16 - | | 3 | 16 | 1 | 16 - | | 3 | 17 | 1 | 17 - | | 3 | 17 | 1 | 17 - | | 3 | 37 | 2 | 19 - | | 3 | 37 | 2 | 19 -(21 rows) - -explain (costs off) - select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - QUERY PLAN -------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), (max("*VALUES*".column3)) - -> HashAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: ("*VALUES*".column1 + 1), ("*VALUES*".column2 + 1) - Hash Key: ("*VALUES*".column1 + 2), ("*VALUES*".column2 + 2) - -> Values Scan on "*VALUES*" -(7 rows) - -select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - a | b | sum | rsum ----+---+-----+------ - 1 | 1 | 8 | 8 - 1 | 2 | 2 | 10 - 1 | | 10 | 20 - 2 | 2 | 2 | 22 - 2 | | 2 | 24 - | 1 | 8 | 32 - | 2 | 4 | 36 - | | 12 | 48 -(8 rows) - -explain (costs off) - select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - QUERY PLAN ---------------------------------------------- - Sort - Sort Key: (sum((sum(c))) OVER (?)), a, b - -> WindowAgg - -> Sort - Sort Key: a, b - -> MixedAggregate - Hash Key: a, b - Hash Key: a - Hash Key: b - Group Key: () - -> Seq Scan on gstest2 -(11 rows) - -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 - | | 9 -(12 rows) - -explain (costs off) - select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - QUERY PLAN ------------------------------------------------- - Sort - Sort Key: gstest_data.a, gstest_data.b - -> MixedAggregate - Hash Key: gstest_data.a, gstest_data.b - Hash Key: gstest_data.a - Hash Key: gstest_data.b - Group Key: () - -> Nested Loop - -> Values Scan on "*VALUES*" - -> Function Scan on gstest_data -(10 rows) - --- Verify that we correctly handle the child node returning a --- non-minimal slot, which happens if the input is pre-sorted, --- e.g. due to an index scan. -BEGIN; -SET LOCAL enable_hashagg = false; -EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - QUERY PLAN ---------------------------------------- - Sort - Sort Key: a, b - -> GroupAggregate - Group Key: a - Group Key: () - Sort Key: b - Group Key: b - -> Sort - Sort Key: a - -> Seq Scan on gstest3 -(10 rows) - -SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - a | b | count | max | max ----+---+-------+-----+----- - 1 | | 1 | 1 | 1 - 2 | | 1 | 2 | 2 - | 1 | 1 | 1 | 1 - | 2 | 1 | 2 | 2 - | | 2 | 2 | 2 -(5 rows) - -SET LOCAL enable_seqscan = false; -EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: a, b - -> GroupAggregate - Group Key: a - Group Key: () - Sort Key: b - Group Key: b - -> Index Scan using gstest3_pkey on gstest3 -(8 rows) - -SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - a | b | count | max | max ----+---+-------+-----+----- - 1 | | 1 | 1 | 1 - 2 | | 1 | 2 | 2 - | 1 | 1 | 1 | 1 - | 2 | 1 | 2 | 2 - | | 2 | 2 | 2 -(5 rows) - -COMMIT; --- More rescan tests -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | 0 | 50 - 1 | 1 | 0 | 2 | 50 - 1 | 1 | 0 | 4 | 50 - 1 | 1 | 0 | 6 | 50 - 1 | 1 | 0 | 8 | 50 - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | 1 | 50 - 1 | 1 | 1 | 3 | 50 - 1 | 1 | 1 | 5 | 50 - 1 | 1 | 1 | 7 | 50 - 1 | 1 | 1 | 9 | 50 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | 0 | 50 - 1 | 1 | 2 | 2 | 50 - 1 | 1 | 2 | 4 | 50 - 1 | 1 | 2 | 6 | 50 - 1 | 1 | 2 | 8 | 50 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | 1 | 50 - 1 | 1 | 3 | 3 | 50 - 1 | 1 | 3 | 5 | 50 - 1 | 1 | 3 | 7 | 50 - 1 | 1 | 3 | 9 | 50 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 1 | 1 | | | 1000 - 2 | 2 | 0 | 0 | 50 - 2 | 2 | 0 | 2 | 50 - 2 | 2 | 0 | 4 | 50 - 2 | 2 | 0 | 6 | 50 - 2 | 2 | 0 | 8 | 50 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | 1 | 50 - 2 | 2 | 1 | 3 | 50 - 2 | 2 | 1 | 5 | 50 - 2 | 2 | 1 | 7 | 50 - 2 | 2 | 1 | 9 | 50 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | 0 | 50 - 2 | 2 | 2 | 2 | 50 - 2 | 2 | 2 | 4 | 50 - 2 | 2 | 2 | 6 | 50 - 2 | 2 | 2 | 8 | 50 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | 1 | 50 - 2 | 2 | 3 | 3 | 50 - 2 | 2 | 3 | 5 | 50 - 2 | 2 | 3 | 7 | 50 - 2 | 2 | 3 | 9 | 50 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 - 2 | 2 | | | 1000 -(70 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ------------------------------------------------------------------------------------------------------------------------------------------------------- - {"(1,0,0,250)","(1,0,2,250)","(1,0,,500)","(1,1,1,250)","(1,1,3,250)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)","(1,,,1000)"} - {"(2,0,0,250)","(2,0,2,250)","(2,0,,500)","(2,1,1,250)","(2,1,3,250)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)","(2,,,1000)"} -(2 rows) - --- Rescan logic changes when there are no empty grouping sets, so test --- that too: -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by grouping sets(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 -(28 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by grouping sets(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ---------------------------------------------------------------------------------- - {"(1,0,,500)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)"} - {"(2,0,,500)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)"} -(2 rows) - --- test the knapsack -set enable_indexscan = false; -set work_mem = '64kB'; -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Group Key: unique1 - Sort Key: twothousand - Group Key: twothousand - Sort Key: thousand - Group Key: thousand - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(13 rows) - -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Group Key: unique1 - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(9 rows) - -set work_mem = '384kB'; -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Hash Key: thousand - Group Key: unique1 - Sort Key: twothousand - Group Key: twothousand - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(12 rows) - --- check collation-sensitive matching between grouping expressions --- (similar to a check for aggregates, but there are additional code --- paths for GROUPING, so check again here) -select v||'a', case grouping(v||'a') when 1 then 1 else 0 end, count(*) - from unnest(array[1,1], array['a','b']) u(i,v) - group by rollup(i, v||'a') order by 1,3; - ?column? | case | count -----------+------+------- - aa | 0 | 1 - ba | 0 | 1 - | 1 | 2 - | 1 | 2 -(4 rows) - -select v||'a', case when grouping(v||'a') = 1 then 1 else 0 end, count(*) - from unnest(array[1,1], array['a','b']) u(i,v) - group by rollup(i, v||'a') order by 1,3; - ?column? | case | count -----------+------+------- - aa | 0 | 1 - ba | 0 | 1 - | 1 | 2 - | 1 | 2 -(4 rows) - --- Bug #16784 -create table bug_16784(i int, j int); -analyze bug_16784; -alter table bug_16784 set (autovacuum_enabled = 'false'); -update pg_class set reltuples = 10 where relname='bug_16784'; -insert into bug_16784 select g/10, g from generate_series(1,40) g; -set work_mem='64kB'; -set enable_sort = false; -select * from - (values (1),(2)) v(a), - lateral (select a, i, j, count(*) from - bug_16784 group by cube(i,j)) s - order by v.a, i, j; - a | a | i | j | count ----+---+---+----+------- - 1 | 1 | 0 | 1 | 1 - 1 | 1 | 0 | 2 | 1 - 1 | 1 | 0 | 3 | 1 - 1 | 1 | 0 | 4 | 1 - 1 | 1 | 0 | 5 | 1 - 1 | 1 | 0 | 6 | 1 - 1 | 1 | 0 | 7 | 1 - 1 | 1 | 0 | 8 | 1 - 1 | 1 | 0 | 9 | 1 - 1 | 1 | 0 | | 9 - 1 | 1 | 1 | 10 | 1 - 1 | 1 | 1 | 11 | 1 - 1 | 1 | 1 | 12 | 1 - 1 | 1 | 1 | 13 | 1 - 1 | 1 | 1 | 14 | 1 - 1 | 1 | 1 | 15 | 1 - 1 | 1 | 1 | 16 | 1 - 1 | 1 | 1 | 17 | 1 - 1 | 1 | 1 | 18 | 1 - 1 | 1 | 1 | 19 | 1 - 1 | 1 | 1 | | 10 - 1 | 1 | 2 | 20 | 1 - 1 | 1 | 2 | 21 | 1 - 1 | 1 | 2 | 22 | 1 - 1 | 1 | 2 | 23 | 1 - 1 | 1 | 2 | 24 | 1 - 1 | 1 | 2 | 25 | 1 - 1 | 1 | 2 | 26 | 1 - 1 | 1 | 2 | 27 | 1 - 1 | 1 | 2 | 28 | 1 - 1 | 1 | 2 | 29 | 1 - 1 | 1 | 2 | | 10 - 1 | 1 | 3 | 30 | 1 - 1 | 1 | 3 | 31 | 1 - 1 | 1 | 3 | 32 | 1 - 1 | 1 | 3 | 33 | 1 - 1 | 1 | 3 | 34 | 1 - 1 | 1 | 3 | 35 | 1 - 1 | 1 | 3 | 36 | 1 - 1 | 1 | 3 | 37 | 1 - 1 | 1 | 3 | 38 | 1 - 1 | 1 | 3 | 39 | 1 - 1 | 1 | 3 | | 10 - 1 | 1 | 4 | 40 | 1 - 1 | 1 | 4 | | 1 - 1 | 1 | | 1 | 1 - 1 | 1 | | 2 | 1 - 1 | 1 | | 3 | 1 - 1 | 1 | | 4 | 1 - 1 | 1 | | 5 | 1 - 1 | 1 | | 6 | 1 - 1 | 1 | | 7 | 1 - 1 | 1 | | 8 | 1 - 1 | 1 | | 9 | 1 - 1 | 1 | | 10 | 1 - 1 | 1 | | 11 | 1 - 1 | 1 | | 12 | 1 - 1 | 1 | | 13 | 1 - 1 | 1 | | 14 | 1 - 1 | 1 | | 15 | 1 - 1 | 1 | | 16 | 1 - 1 | 1 | | 17 | 1 - 1 | 1 | | 18 | 1 - 1 | 1 | | 19 | 1 - 1 | 1 | | 20 | 1 - 1 | 1 | | 21 | 1 - 1 | 1 | | 22 | 1 - 1 | 1 | | 23 | 1 - 1 | 1 | | 24 | 1 - 1 | 1 | | 25 | 1 - 1 | 1 | | 26 | 1 - 1 | 1 | | 27 | 1 - 1 | 1 | | 28 | 1 - 1 | 1 | | 29 | 1 - 1 | 1 | | 30 | 1 - 1 | 1 | | 31 | 1 - 1 | 1 | | 32 | 1 - 1 | 1 | | 33 | 1 - 1 | 1 | | 34 | 1 - 1 | 1 | | 35 | 1 - 1 | 1 | | 36 | 1 - 1 | 1 | | 37 | 1 - 1 | 1 | | 38 | 1 - 1 | 1 | | 39 | 1 - 1 | 1 | | 40 | 1 - 1 | 1 | | | 40 - 2 | 2 | 0 | 1 | 1 - 2 | 2 | 0 | 2 | 1 - 2 | 2 | 0 | 3 | 1 - 2 | 2 | 0 | 4 | 1 - 2 | 2 | 0 | 5 | 1 - 2 | 2 | 0 | 6 | 1 - 2 | 2 | 0 | 7 | 1 - 2 | 2 | 0 | 8 | 1 - 2 | 2 | 0 | 9 | 1 - 2 | 2 | 0 | | 9 - 2 | 2 | 1 | 10 | 1 - 2 | 2 | 1 | 11 | 1 - 2 | 2 | 1 | 12 | 1 - 2 | 2 | 1 | 13 | 1 - 2 | 2 | 1 | 14 | 1 - 2 | 2 | 1 | 15 | 1 - 2 | 2 | 1 | 16 | 1 - 2 | 2 | 1 | 17 | 1 - 2 | 2 | 1 | 18 | 1 - 2 | 2 | 1 | 19 | 1 - 2 | 2 | 1 | | 10 - 2 | 2 | 2 | 20 | 1 - 2 | 2 | 2 | 21 | 1 - 2 | 2 | 2 | 22 | 1 - 2 | 2 | 2 | 23 | 1 - 2 | 2 | 2 | 24 | 1 - 2 | 2 | 2 | 25 | 1 - 2 | 2 | 2 | 26 | 1 - 2 | 2 | 2 | 27 | 1 - 2 | 2 | 2 | 28 | 1 - 2 | 2 | 2 | 29 | 1 - 2 | 2 | 2 | | 10 - 2 | 2 | 3 | 30 | 1 - 2 | 2 | 3 | 31 | 1 - 2 | 2 | 3 | 32 | 1 - 2 | 2 | 3 | 33 | 1 - 2 | 2 | 3 | 34 | 1 - 2 | 2 | 3 | 35 | 1 - 2 | 2 | 3 | 36 | 1 - 2 | 2 | 3 | 37 | 1 - 2 | 2 | 3 | 38 | 1 - 2 | 2 | 3 | 39 | 1 - 2 | 2 | 3 | | 10 - 2 | 2 | 4 | 40 | 1 - 2 | 2 | 4 | | 1 - 2 | 2 | | 1 | 1 - 2 | 2 | | 2 | 1 - 2 | 2 | | 3 | 1 - 2 | 2 | | 4 | 1 - 2 | 2 | | 5 | 1 - 2 | 2 | | 6 | 1 - 2 | 2 | | 7 | 1 - 2 | 2 | | 8 | 1 - 2 | 2 | | 9 | 1 - 2 | 2 | | 10 | 1 - 2 | 2 | | 11 | 1 - 2 | 2 | | 12 | 1 - 2 | 2 | | 13 | 1 - 2 | 2 | | 14 | 1 - 2 | 2 | | 15 | 1 - 2 | 2 | | 16 | 1 - 2 | 2 | | 17 | 1 - 2 | 2 | | 18 | 1 - 2 | 2 | | 19 | 1 - 2 | 2 | | 20 | 1 - 2 | 2 | | 21 | 1 - 2 | 2 | | 22 | 1 - 2 | 2 | | 23 | 1 - 2 | 2 | | 24 | 1 - 2 | 2 | | 25 | 1 - 2 | 2 | | 26 | 1 - 2 | 2 | | 27 | 1 - 2 | 2 | | 28 | 1 - 2 | 2 | | 29 | 1 - 2 | 2 | | 30 | 1 - 2 | 2 | | 31 | 1 - 2 | 2 | | 32 | 1 - 2 | 2 | | 33 | 1 - 2 | 2 | | 34 | 1 - 2 | 2 | | 35 | 1 - 2 | 2 | | 36 | 1 - 2 | 2 | | 37 | 1 - 2 | 2 | | 38 | 1 - 2 | 2 | | 39 | 1 - 2 | 2 | | 40 | 1 - 2 | 2 | | | 40 -(172 rows) - --- --- Compare results between plans using sorting and plans using hash --- aggregation. Force spilling in both cases by setting work_mem low --- and altering the statistics. --- -create table gs_data_1 as -select g%1000 as g1000, g%100 as g100, g%10 as g10, g - from generate_series(0,1999) g; -analyze gs_data_1; -alter table gs_data_1 set (autovacuum_enabled = 'false'); -update pg_class set reltuples = 10 where relname='gs_data_1'; -set work_mem='64kB'; --- Produce results with sorting. -set enable_sort = true; -set enable_hashagg = false; -set jit_above_cost = 0; -explain (costs off) -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); - QUERY PLAN ------------------------------------- - GroupAggregate - Group Key: g1000, g100, g10 - Group Key: g1000, g100 - Group Key: g1000 - Group Key: () - Sort Key: g100, g10 - Group Key: g100, g10 - Group Key: g100 - Sort Key: g10, g1000 - Group Key: g10, g1000 - Group Key: g10 - -> Sort - Sort Key: g1000, g100, g10 - -> Seq Scan on gs_data_1 -(14 rows) - -create table gs_group_1 as -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); --- Produce results with hash aggregation. -set enable_hashagg = true; -set enable_sort = false; -explain (costs off) -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); - QUERY PLAN ------------------------------- - MixedAggregate - Hash Key: g1000, g100, g10 - Hash Key: g1000, g100 - Hash Key: g1000 - Hash Key: g100, g10 - Hash Key: g100 - Hash Key: g10, g1000 - Hash Key: g10 - Group Key: () - -> Seq Scan on gs_data_1 -(10 rows) - -create table gs_hash_1 as -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); -set enable_sort = true; -set work_mem to default; --- Compare results -(select * from gs_hash_1 except select * from gs_group_1) - union all -(select * from gs_group_1 except select * from gs_hash_1); - g100 | g10 | sum | count | max -------+-----+-----+-------+----- -(0 rows) - -drop table gs_group_1; -drop table gs_hash_1; --- GROUP BY DISTINCT --- "normal" behavior... -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by all rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | 2 | - 1 | | 3 - 1 | | 3 - 1 | | - 1 | | - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 4 | | - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | 8 | - 7 | | 9 - 7 | | 9 - 7 | | - 7 | | - 7 | | - | | -(25 rows) - --- ...which is also the default -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | 2 | - 1 | | 3 - 1 | | 3 - 1 | | - 1 | | - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 4 | | - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | 8 | - 7 | | 9 - 7 | | 9 - 7 | | - 7 | | - 7 | | - | | -(25 rows) - --- "group by distinct" behavior... -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by distinct rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | | 3 - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | | 9 - 7 | | - | | -(13 rows) - --- ...which is not the same as "select distinct" -select distinct a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | | 3 - 1 | | - 4 | | 6 - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | | 9 - 7 | | - | | -(11 rows) - --- end +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/drop_operator.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/drop_operator.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/drop_operator.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/drop_operator.out 2021-10-30 22:04:14 -0600 @@ -1,61 +1,2 @@ -CREATE OPERATOR === ( - PROCEDURE = int8eq, - LEFTARG = bigint, - RIGHTARG = bigint, - COMMUTATOR = === -); -CREATE OPERATOR !== ( - PROCEDURE = int8ne, - LEFTARG = bigint, - RIGHTARG = bigint, - NEGATOR = ===, - COMMUTATOR = !== -); -DROP OPERATOR !==(bigint, bigint); -SELECT ctid, oprcom -FROM pg_catalog.pg_operator fk -WHERE oprcom != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); - ctid | oprcom -------+-------- -(0 rows) - -SELECT ctid, oprnegate -FROM pg_catalog.pg_operator fk -WHERE oprnegate != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); - ctid | oprnegate -------+----------- -(0 rows) - -DROP OPERATOR ===(bigint, bigint); -CREATE OPERATOR <| ( - PROCEDURE = int8lt, - LEFTARG = bigint, - RIGHTARG = bigint -); -CREATE OPERATOR |> ( - PROCEDURE = int8gt, - LEFTARG = bigint, - RIGHTARG = bigint, - NEGATOR = <|, - COMMUTATOR = <| -); -DROP OPERATOR |>(bigint, bigint); -SELECT ctid, oprcom -FROM pg_catalog.pg_operator fk -WHERE oprcom != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); - ctid | oprcom -------+-------- -(0 rows) - -SELECT ctid, oprnegate -FROM pg_catalog.pg_operator fk -WHERE oprnegate != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); - ctid | oprnegate -------+----------- -(0 rows) - -DROP OPERATOR <|(bigint, bigint); +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/password.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/password.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/password.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/password.out 2021-10-30 22:04:14 -0600 @@ -1,144 +1,2 @@ --- --- Tests for password types --- --- Tests for GUC password_encryption -SET password_encryption = 'novalue'; -- error -ERROR: invalid value for parameter "password_encryption": "novalue" -HINT: Available values: md5, scram-sha-256. -SET password_encryption = true; -- error -ERROR: invalid value for parameter "password_encryption": "true" -HINT: Available values: md5, scram-sha-256. -SET password_encryption = 'md5'; -- ok -SET password_encryption = 'scram-sha-256'; -- ok --- consistency of password entries -SET password_encryption = 'md5'; -CREATE ROLE regress_passwd1 PASSWORD 'role_pwd1'; -CREATE ROLE regress_passwd2 PASSWORD 'role_pwd2'; -SET password_encryption = 'scram-sha-256'; -CREATE ROLE regress_passwd3 PASSWORD 'role_pwd3'; -CREATE ROLE regress_passwd4 PASSWORD NULL; --- check list of created entries --- --- The scram secret will look something like: --- SCRAM-SHA-256$4096:E4HxLGtnRzsYwg==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo= --- --- Since the salt is random, the exact value stored will be different on every test --- run. Use a regular expression to mask the changing parts. -SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') as rolpassword_masked - FROM pg_authid - WHERE rolname LIKE 'regress_passwd%' - ORDER BY rolname, rolpassword; - rolname | rolpassword_masked ------------------+--------------------------------------------------- - regress_passwd1 | md5783277baca28003b33453252be4dbb34 - regress_passwd2 | md54044304ba511dd062133eb5b4b84a2a3 - regress_passwd3 | SCRAM-SHA-256$4096:$: - regress_passwd4 | -(4 rows) - --- Rename a role -ALTER ROLE regress_passwd2 RENAME TO regress_passwd2_new; -NOTICE: MD5 password cleared because of role rename --- md5 entry should have been removed -SELECT rolname, rolpassword - FROM pg_authid - WHERE rolname LIKE 'regress_passwd2_new' - ORDER BY rolname, rolpassword; - rolname | rolpassword ----------------------+------------- - regress_passwd2_new | -(1 row) - -ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2; --- Change passwords with ALTER USER. With plaintext or already-encrypted --- passwords. -SET password_encryption = 'md5'; --- encrypt with MD5 -ALTER ROLE regress_passwd2 PASSWORD 'foo'; --- already encrypted, use as they are -ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70'; -ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo='; -SET password_encryption = 'scram-sha-256'; --- create SCRAM secret -ALTER ROLE regress_passwd4 PASSWORD 'foo'; --- already encrypted with MD5, use as it is -CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023'; --- This looks like a valid SCRAM-SHA-256 secret, but it is not --- so it should be hashed with SCRAM-SHA-256. -CREATE ROLE regress_passwd6 PASSWORD 'SCRAM-SHA-256$1234'; --- These may look like valid MD5 secrets, but they are not, so they --- should be hashed with SCRAM-SHA-256. --- trailing garbage at the end -CREATE ROLE regress_passwd7 PASSWORD 'md5012345678901234567890123456789zz'; --- invalid length -CREATE ROLE regress_passwd8 PASSWORD 'md501234567890123456789012345678901zz'; -SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') as rolpassword_masked - FROM pg_authid - WHERE rolname LIKE 'regress_passwd%' - ORDER BY rolname, rolpassword; - rolname | rolpassword_masked ------------------+--------------------------------------------------- - regress_passwd1 | md5cd3578025fe2c3d7ed1b9a9b26238b70 - regress_passwd2 | md5dfa155cadd5f4ad57860162f3fab9cdb - regress_passwd3 | SCRAM-SHA-256$4096:$: - regress_passwd4 | SCRAM-SHA-256$4096:$: - regress_passwd5 | md5e73a4b11df52a6068f8b39f90be36023 - regress_passwd6 | SCRAM-SHA-256$4096:$: - regress_passwd7 | SCRAM-SHA-256$4096:$: - regress_passwd8 | SCRAM-SHA-256$4096:$: -(8 rows) - --- An empty password is not allowed, in any form -CREATE ROLE regress_passwd_empty PASSWORD ''; -NOTICE: empty string is not a valid password, clearing password -ALTER ROLE regress_passwd_empty PASSWORD 'md585939a5ce845f1a1b620742e3c659e0a'; -NOTICE: empty string is not a valid password, clearing password -ALTER ROLE regress_passwd_empty PASSWORD 'SCRAM-SHA-256$4096:hpFyHTUsSWcR7O9P$LgZFIt6Oqdo27ZFKbZ2nV+vtnYM995pDh9ca6WSi120=:qVV5NeluNfUPkwm7Vqat25RjSPLkGeoZBQs6wVv+um4='; -NOTICE: empty string is not a valid password, clearing password -SELECT rolpassword FROM pg_authid WHERE rolname='regress_passwd_empty'; - rolpassword -------------- - -(1 row) - --- Test with invalid stored and server keys. --- --- The first is valid, to act as a control. The others have too long --- stored/server keys. They will be re-hashed. -CREATE ROLE regress_passwd_sha_len0 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96Rqw=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZI='; -CREATE ROLE regress_passwd_sha_len1 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96RqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZI='; -CREATE ROLE regress_passwd_sha_len2 PASSWORD 'SCRAM-SHA-256$4096:A6xHKoH/494E941doaPOYg==$Ky+A30sewHIH3VHQLRN9vYsuzlgNyGNKCh37dy96Rqw=:COPdlNiIkrsacU5QoxydEuOH6e/KfiipeETb/bPw8ZIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='; --- Check that the invalid secrets were re-hashed. A re-hashed secret --- should not contain the original salt. -SELECT rolname, rolpassword not like '%A6xHKoH/494E941doaPOYg==%' as is_rolpassword_rehashed - FROM pg_authid - WHERE rolname LIKE 'regress_passwd_sha_len%' - ORDER BY rolname; - rolname | is_rolpassword_rehashed --------------------------+------------------------- - regress_passwd_sha_len0 | f - regress_passwd_sha_len1 | t - regress_passwd_sha_len2 | t -(3 rows) - -DROP ROLE regress_passwd1; -DROP ROLE regress_passwd2; -DROP ROLE regress_passwd3; -DROP ROLE regress_passwd4; -DROP ROLE regress_passwd5; -DROP ROLE regress_passwd6; -DROP ROLE regress_passwd7; -DROP ROLE regress_passwd8; -DROP ROLE regress_passwd_empty; -DROP ROLE regress_passwd_sha_len0; -DROP ROLE regress_passwd_sha_len1; -DROP ROLE regress_passwd_sha_len2; --- all entries should have been removed -SELECT rolname, rolpassword - FROM pg_authid - WHERE rolname LIKE 'regress_passwd%' - ORDER BY rolname, rolpassword; - rolname | rolpassword ----------+------------- -(0 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/identity.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/identity.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/identity.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/identity.out 2021-10-30 22:04:14 -0600 @@ -1,562 +1,2 @@ --- sanity check of system catalog -SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd'); - attrelid | attname | attidentity -----------+---------+------------- -(0 rows) - -CREATE TABLE itest1 (a int generated by default as identity, b text); -CREATE TABLE itest2 (a bigint generated always as identity, b text); -CREATE TABLE itest3 (a smallint generated by default as identity (start with 7 increment by 5), b text); -ALTER TABLE itest3 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error -ERROR: column "a" of relation "itest3" is already an identity column -SELECT table_name, column_name, column_default, is_nullable, is_identity, identity_generation, identity_start, identity_increment, identity_maximum, identity_minimum, identity_cycle FROM information_schema.columns WHERE table_name LIKE 'itest_' ORDER BY 1, 2; - table_name | column_name | column_default | is_nullable | is_identity | identity_generation | identity_start | identity_increment | identity_maximum | identity_minimum | identity_cycle -------------+-------------+----------------+-------------+-------------+---------------------+----------------+--------------------+---------------------+------------------+---------------- - itest1 | a | | NO | YES | BY DEFAULT | 1 | 1 | 2147483647 | 1 | NO - itest1 | b | | YES | NO | | | | | | NO - itest2 | a | | NO | YES | ALWAYS | 1 | 1 | 9223372036854775807 | 1 | NO - itest2 | b | | YES | NO | | | | | | NO - itest3 | a | | NO | YES | BY DEFAULT | 7 | 5 | 32767 | 1 | NO - itest3 | b | | YES | NO | | | | | | NO -(6 rows) - --- internal sequences should not be shown here -SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE 'itest%'; - sequence_name ---------------- -(0 rows) - -SELECT pg_get_serial_sequence('itest1', 'a'); - pg_get_serial_sequence ------------------------- - public.itest1_a_seq -(1 row) - -\d itest1_a_seq - Sequence "public.itest1_a_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Sequence for identity column: public.itest1.a - -CREATE TABLE itest4 (a int, b text); -ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL -ERROR: column "a" of relation "itest4" must be declared NOT NULL before identity can be added -ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL; -ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- ok -ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; -- error, disallowed -ERROR: column "a" of relation "itest4" is an identity column -ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, already set -ERROR: column "a" of relation "itest4" is already an identity column -ALTER TABLE itest4 ALTER COLUMN b ADD GENERATED ALWAYS AS IDENTITY; -- error, wrong data type -ERROR: identity column type must be smallint, integer, or bigint --- for later -ALTER TABLE itest4 ALTER COLUMN b SET DEFAULT ''; --- invalid column type -CREATE TABLE itest_err_1 (a text generated by default as identity); -ERROR: identity column type must be smallint, integer, or bigint --- duplicate identity -CREATE TABLE itest_err_2 (a int generated always as identity generated by default as identity); -ERROR: multiple identity specifications for column "a" of table "itest_err_2" -LINE 1: ...E itest_err_2 (a int generated always as identity generated ... - ^ --- cannot have default and identity -CREATE TABLE itest_err_3 (a int default 5 generated by default as identity); -ERROR: both default and identity specified for column "a" of table "itest_err_3" -LINE 1: CREATE TABLE itest_err_3 (a int default 5 generated by defau... - ^ --- cannot combine serial and identity -CREATE TABLE itest_err_4 (a serial generated by default as identity); -ERROR: both default and identity specified for column "a" of table "itest_err_4" -INSERT INTO itest1 DEFAULT VALUES; -INSERT INTO itest1 DEFAULT VALUES; -INSERT INTO itest2 DEFAULT VALUES; -INSERT INTO itest2 DEFAULT VALUES; -INSERT INTO itest3 DEFAULT VALUES; -INSERT INTO itest3 DEFAULT VALUES; -INSERT INTO itest4 DEFAULT VALUES; -INSERT INTO itest4 DEFAULT VALUES; -SELECT * FROM itest1; - a | b ----+--- - 1 | - 2 | -(2 rows) - -SELECT * FROM itest2; - a | b ----+--- - 1 | - 2 | -(2 rows) - -SELECT * FROM itest3; - a | b -----+--- - 7 | - 12 | -(2 rows) - -SELECT * FROM itest4; - a | b ----+--- - 1 | - 2 | -(2 rows) - --- VALUES RTEs -CREATE TABLE itest5 (a int generated always as identity, b text); -INSERT INTO itest5 VALUES (1, 'a'); -- error -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO itest5 VALUES (DEFAULT, 'a'); -- ok -INSERT INTO itest5 VALUES (2, 'b'), (3, 'c'); -- error -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO itest5 VALUES (DEFAULT, 'b'), (3, 'c'); -- error -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO itest5 VALUES (2, 'b'), (DEFAULT, 'c'); -- error -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO itest5 VALUES (DEFAULT, 'b'), (DEFAULT, 'c'); -- ok -INSERT INTO itest5 OVERRIDING SYSTEM VALUE VALUES (-1, 'aa'); -INSERT INTO itest5 OVERRIDING SYSTEM VALUE VALUES (-2, 'bb'), (-3, 'cc'); -INSERT INTO itest5 OVERRIDING SYSTEM VALUE VALUES (DEFAULT, 'dd'), (-4, 'ee'); -INSERT INTO itest5 OVERRIDING SYSTEM VALUE VALUES (-5, 'ff'), (DEFAULT, 'gg'); -INSERT INTO itest5 OVERRIDING SYSTEM VALUE VALUES (DEFAULT, 'hh'), (DEFAULT, 'ii'); -INSERT INTO itest5 OVERRIDING USER VALUE VALUES (-1, 'aaa'); -INSERT INTO itest5 OVERRIDING USER VALUE VALUES (-2, 'bbb'), (-3, 'ccc'); -INSERT INTO itest5 OVERRIDING USER VALUE VALUES (DEFAULT, 'ddd'), (-4, 'eee'); -INSERT INTO itest5 OVERRIDING USER VALUE VALUES (-5, 'fff'), (DEFAULT, 'ggg'); -INSERT INTO itest5 OVERRIDING USER VALUE VALUES (DEFAULT, 'hhh'), (DEFAULT, 'iii'); -SELECT * FROM itest5; - a | b -----+----- - 1 | a - 2 | b - 3 | c - -1 | aa - -2 | bb - -3 | cc - 4 | dd - -4 | ee - -5 | ff - 5 | gg - 6 | hh - 7 | ii - 8 | aaa - 9 | bbb - 10 | ccc - 11 | ddd - 12 | eee - 13 | fff - 14 | ggg - 15 | hhh - 16 | iii -(21 rows) - -DROP TABLE itest5; -INSERT INTO itest3 VALUES (DEFAULT, 'a'); -INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c'); -SELECT * FROM itest3; - a | b -----+--- - 7 | - 12 | - 17 | a - 22 | b - 27 | c -(5 rows) - --- OVERRIDING tests --- GENERATED BY DEFAULT --- This inserts the row as presented: -INSERT INTO itest1 VALUES (10, 'xyz'); --- With GENERATED BY DEFAULT, OVERRIDING SYSTEM VALUE is not allowed --- by the standard, but we allow it as a no-op, since it is of use if --- there are multiple identity columns in a table, which is also an --- extension. -INSERT INTO itest1 OVERRIDING SYSTEM VALUE VALUES (20, 'xyz'); --- This ignores the 30 and uses the sequence value instead: -INSERT INTO itest1 OVERRIDING USER VALUE VALUES (30, 'xyz'); -SELECT * FROM itest1; - a | b -----+----- - 1 | - 2 | - 10 | xyz - 20 | xyz - 3 | xyz -(5 rows) - --- GENERATED ALWAYS --- This is an error: -INSERT INTO itest2 VALUES (10, 'xyz'); -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. --- This inserts the row as presented: -INSERT INTO itest2 OVERRIDING SYSTEM VALUE VALUES (20, 'xyz'); --- This ignores the 30 and uses the sequence value instead: -INSERT INTO itest2 OVERRIDING USER VALUE VALUES (30, 'xyz'); -SELECT * FROM itest2; - a | b -----+----- - 1 | - 2 | - 20 | xyz - 3 | xyz -(4 rows) - --- UPDATE tests --- GENERATED BY DEFAULT is not restricted. -UPDATE itest1 SET a = 101 WHERE a = 1; -UPDATE itest1 SET a = DEFAULT WHERE a = 2; -SELECT * FROM itest1; - a | b ------+----- - 10 | xyz - 20 | xyz - 3 | xyz - 101 | - 4 | -(5 rows) - --- GENERATED ALWAYS allows only DEFAULT. -UPDATE itest2 SET a = 101 WHERE a = 1; -- error -ERROR: column "a" can only be updated to DEFAULT -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -UPDATE itest2 SET a = DEFAULT WHERE a = 2; -- ok -SELECT * FROM itest2; - a | b -----+----- - 1 | - 20 | xyz - 3 | xyz - 4 | -(4 rows) - --- COPY tests -CREATE TABLE itest9 (a int GENERATED ALWAYS AS IDENTITY, b text, c bigint); -COPY itest9 FROM stdin; -COPY itest9 (b, c) FROM stdin; -SELECT * FROM itest9 ORDER BY c; - a | b | c ------+------+----- - 100 | foo | 200 - 101 | bar | 201 - 1 | foo2 | 202 - 2 | bar2 | 203 -(4 rows) - --- DROP IDENTITY tests -ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; -ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; -- error -ERROR: column "a" of relation "itest4" is not an identity column -ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY IF EXISTS; -- noop -NOTICE: column "a" of relation "itest4" is not an identity column, skipping -INSERT INTO itest4 DEFAULT VALUES; -- fails because NOT NULL is not dropped -ERROR: null value in column "a" of relation "itest4" violates not-null constraint -DETAIL: Failing row contains (null, ). -ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; -INSERT INTO itest4 DEFAULT VALUES; -SELECT * FROM itest4; - a | b ----+--- - 1 | - 2 | - | -(3 rows) - --- check that sequence is removed -SELECT sequence_name FROM itest4_a_seq; -ERROR: relation "itest4_a_seq" does not exist -LINE 1: SELECT sequence_name FROM itest4_a_seq; - ^ --- test views -CREATE TABLE itest10 (a int generated by default as identity, b text); -CREATE TABLE itest11 (a int generated always as identity, b text); -CREATE VIEW itestv10 AS SELECT * FROM itest10; -CREATE VIEW itestv11 AS SELECT * FROM itest11; -INSERT INTO itestv10 DEFAULT VALUES; -INSERT INTO itestv10 DEFAULT VALUES; -INSERT INTO itestv11 DEFAULT VALUES; -INSERT INTO itestv11 DEFAULT VALUES; -SELECT * FROM itestv10; - a | b ----+--- - 1 | - 2 | -(2 rows) - -SELECT * FROM itestv11; - a | b ----+--- - 1 | - 2 | -(2 rows) - -INSERT INTO itestv10 VALUES (10, 'xyz'); -INSERT INTO itestv10 OVERRIDING USER VALUE VALUES (11, 'xyz'); -SELECT * FROM itestv10; - a | b -----+----- - 1 | - 2 | - 10 | xyz - 3 | xyz -(4 rows) - -INSERT INTO itestv11 VALUES (10, 'xyz'); -ERROR: cannot insert a non-DEFAULT value into column "a" -DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO itestv11 OVERRIDING SYSTEM VALUE VALUES (11, 'xyz'); -SELECT * FROM itestv11; - a | b -----+----- - 1 | - 2 | - 11 | xyz -(3 rows) - -DROP VIEW itestv10, itestv11; --- ADD COLUMN -CREATE TABLE itest13 (a int); --- add column to empty table -ALTER TABLE itest13 ADD COLUMN b int GENERATED BY DEFAULT AS IDENTITY; -INSERT INTO itest13 VALUES (1), (2), (3); --- add column to populated table -ALTER TABLE itest13 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY; -SELECT * FROM itest13; - a | b | c ----+---+--- - 1 | 1 | 1 - 2 | 2 | 2 - 3 | 3 | 3 -(3 rows) - --- various ALTER COLUMN tests --- fail, not allowed for identity columns -ALTER TABLE itest1 ALTER COLUMN a SET DEFAULT 1; -ERROR: column "a" of relation "itest1" is an identity column --- fail, not allowed, already has a default -CREATE TABLE itest5 (a serial, b text); -ALTER TABLE itest5 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -ERROR: column "a" of relation "itest5" already has a default value -ALTER TABLE itest3 ALTER COLUMN a TYPE int; -SELECT seqtypid::regtype FROM pg_sequence WHERE seqrelid = 'itest3_a_seq'::regclass; - seqtypid ----------- - integer -(1 row) - -\d itest3 - Table "public.itest3" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+---------------------------------- - a | integer | | not null | generated by default as identity - b | text | | | - -ALTER TABLE itest3 ALTER COLUMN a TYPE text; -- error -ERROR: identity column type must be smallint, integer, or bigint --- kinda silly to change property in the same command, but it should work -ALTER TABLE itest3 - ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY, - ALTER COLUMN c SET GENERATED ALWAYS; -\d itest3 - Table "public.itest3" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+---------------------------------- - a | integer | | not null | generated by default as identity - b | text | | | - c | integer | | not null | generated always as identity - --- ALTER COLUMN ... SET -CREATE TABLE itest6 (a int GENERATED ALWAYS AS IDENTITY, b text); -INSERT INTO itest6 DEFAULT VALUES; -ALTER TABLE itest6 ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART; -INSERT INTO itest6 DEFAULT VALUES; -INSERT INTO itest6 DEFAULT VALUES; -SELECT * FROM itest6; - a | b ------+--- - 1 | - 100 | - 102 | -(3 rows) - -SELECT table_name, column_name, is_identity, identity_generation FROM information_schema.columns WHERE table_name = 'itest6' ORDER BY 1, 2; - table_name | column_name | is_identity | identity_generation -------------+-------------+-------------+--------------------- - itest6 | a | YES | BY DEFAULT - itest6 | b | NO | -(2 rows) - -ALTER TABLE itest6 ALTER COLUMN b SET INCREMENT BY 2; -- fail, not identity -ERROR: column "b" of relation "itest6" is not an identity column --- prohibited direct modification of sequence -ALTER SEQUENCE itest6_a_seq OWNED BY NONE; -ERROR: cannot change ownership of identity sequence -DETAIL: Sequence "itest6_a_seq" is linked to table "itest6". --- inheritance -CREATE TABLE itest7 (a int GENERATED ALWAYS AS IDENTITY); -INSERT INTO itest7 DEFAULT VALUES; -SELECT * FROM itest7; - a ---- - 1 -(1 row) - --- identity property is not inherited -CREATE TABLE itest7a (b text) INHERITS (itest7); --- make column identity in child table -CREATE TABLE itest7b (a int); -CREATE TABLE itest7c (a int GENERATED ALWAYS AS IDENTITY) INHERITS (itest7b); -NOTICE: merging column "a" with inherited definition -INSERT INTO itest7c DEFAULT VALUES; -SELECT * FROM itest7c; - a ---- - 1 -(1 row) - -CREATE TABLE itest7d (a int not null); -CREATE TABLE itest7e () INHERITS (itest7d); -ALTER TABLE itest7d ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -ALTER TABLE itest7d ADD COLUMN b int GENERATED ALWAYS AS IDENTITY; -- error -ERROR: cannot recursively add identity column to table that has child tables -SELECT table_name, column_name, is_nullable, is_identity, identity_generation FROM information_schema.columns WHERE table_name LIKE 'itest7%' ORDER BY 1, 2; - table_name | column_name | is_nullable | is_identity | identity_generation -------------+-------------+-------------+-------------+--------------------- - itest7 | a | NO | YES | ALWAYS - itest7a | a | NO | NO | - itest7a | b | YES | NO | - itest7b | a | YES | NO | - itest7c | a | NO | YES | ALWAYS - itest7d | a | NO | YES | ALWAYS - itest7e | a | NO | NO | -(7 rows) - --- These ALTER TABLE variants will not recurse. -ALTER TABLE itest7 ALTER COLUMN a SET GENERATED BY DEFAULT; -ALTER TABLE itest7 ALTER COLUMN a RESTART; -ALTER TABLE itest7 ALTER COLUMN a DROP IDENTITY; --- privileges -CREATE USER regress_identity_user1; -CREATE TABLE itest8 (a int GENERATED ALWAYS AS IDENTITY, b text); -GRANT SELECT, INSERT ON itest8 TO regress_identity_user1; -SET ROLE regress_identity_user1; -INSERT INTO itest8 DEFAULT VALUES; -SELECT * FROM itest8; - a | b ----+--- - 1 | -(1 row) - -RESET ROLE; -DROP TABLE itest8; -DROP USER regress_identity_user1; --- multiple steps in ALTER TABLE -CREATE TABLE itest8 (f1 int); -ALTER TABLE itest8 - ADD COLUMN f2 int NOT NULL, - ALTER COLUMN f2 ADD GENERATED ALWAYS AS IDENTITY; -ALTER TABLE itest8 - ADD COLUMN f3 int NOT NULL, - ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY, - ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT 10; -ALTER TABLE itest8 - ADD COLUMN f4 int; -ALTER TABLE itest8 - ALTER COLUMN f4 SET NOT NULL, - ALTER COLUMN f4 ADD GENERATED ALWAYS AS IDENTITY, - ALTER COLUMN f4 SET DATA TYPE bigint; -ALTER TABLE itest8 - ADD COLUMN f5 int GENERATED ALWAYS AS IDENTITY; -ALTER TABLE itest8 - ALTER COLUMN f5 DROP IDENTITY, - ALTER COLUMN f5 DROP NOT NULL, - ALTER COLUMN f5 SET DATA TYPE bigint; -INSERT INTO itest8 VALUES(0), (1); --- This does not work when the table isn't empty. That's intentional, --- since ADD GENERATED should only affect later insertions: -ALTER TABLE itest8 - ADD COLUMN f22 int NOT NULL, - ALTER COLUMN f22 ADD GENERATED ALWAYS AS IDENTITY; -ERROR: column "f22" of relation "itest8" contains null values -TABLE itest8; - f1 | f2 | f3 | f4 | f5 -----+----+----+----+---- - 0 | 1 | 1 | 1 | - 1 | 2 | 11 | 2 | -(2 rows) - -\d+ itest8 - Table "public.itest8" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+----------------------------------+---------+--------------+------------- - f1 | integer | | | | plain | | - f2 | integer | | not null | generated always as identity | plain | | - f3 | integer | | not null | generated by default as identity | plain | | - f4 | bigint | | not null | generated always as identity | plain | | - f5 | bigint | | | | plain | | - -\d itest8_f2_seq - Sequence "public.itest8_f2_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Sequence for identity column: public.itest8.f2 - -\d itest8_f3_seq - Sequence "public.itest8_f3_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 10 | no | 1 -Sequence for identity column: public.itest8.f3 - -\d itest8_f4_seq - Sequence "public.itest8_f4_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ---------+-------+---------+---------------------+-----------+---------+------- - bigint | 1 | 1 | 9223372036854775807 | 1 | no | 1 -Sequence for identity column: public.itest8.f4 - -\d itest8_f5_seq -DROP TABLE itest8; --- typed tables (currently not supported) -CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint); -CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error -ERROR: identity columns are not supported on typed tables -DROP TYPE itest_type CASCADE; --- table partitions (currently not supported) -CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1); -CREATE TABLE itest_child PARTITION OF itest_parent ( - f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY -) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error -ERROR: identity columns are not supported on partitions -DROP TABLE itest_parent; --- test that sequence of half-dropped serial column is properly ignored -CREATE TABLE itest14 (id serial); -ALTER TABLE itest14 ALTER id DROP DEFAULT; -ALTER TABLE itest14 ALTER id ADD GENERATED BY DEFAULT AS IDENTITY; -INSERT INTO itest14 (id) VALUES (DEFAULT); --- Identity columns must be NOT NULL (cf bug #16913) -CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NULL); -- fail -ERROR: conflicting NULL/NOT NULL declarations for column "id" of table "itest15" -LINE 1: ...ABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NULL); - ^ -CREATE TABLE itest15 (id integer NULL GENERATED ALWAYS AS IDENTITY); -- fail -ERROR: conflicting NULL/NOT NULL declarations for column "id" of table "itest15" -LINE 1: CREATE TABLE itest15 (id integer NULL GENERATED ALWAYS AS ID... - ^ -CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NOT NULL); -DROP TABLE itest15; -CREATE TABLE itest15 (id integer NOT NULL GENERATED ALWAYS AS IDENTITY); -DROP TABLE itest15; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/generated.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/generated.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/generated.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/generated.out 2021-10-30 22:04:14 -0600 @@ -1,1065 +1,2 @@ --- sanity check of system catalog -SELECT attrelid, attname, attgenerated FROM pg_attribute WHERE attgenerated NOT IN ('', 's'); - attrelid | attname | attgenerated -----------+---------+-------------- -(0 rows) - -CREATE TABLE gtest0 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (55) STORED); -CREATE TABLE gtest1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); -SELECT table_name, column_name, column_default, is_nullable, is_generated, generation_expression FROM information_schema.columns WHERE table_name LIKE 'gtest_' ORDER BY 1, 2; - table_name | column_name | column_default | is_nullable | is_generated | generation_expression -------------+-------------+----------------+-------------+--------------+----------------------- - gtest0 | a | | NO | NEVER | - gtest0 | b | | YES | ALWAYS | 55 - gtest1 | a | | NO | NEVER | - gtest1 | b | | YES | ALWAYS | (a * 2) -(4 rows) - -SELECT table_name, column_name, dependent_column FROM information_schema.column_column_usage ORDER BY 1, 2, 3; - table_name | column_name | dependent_column -------------+-------------+------------------ - gtest1 | a | b -(1 row) - -\d gtest1 - Table "public.gtest1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | not null | - b | integer | | | generated always as (a * 2) stored -Indexes: - "gtest1_pkey" PRIMARY KEY, btree (a) - --- duplicate generated -CREATE TABLE gtest_err_1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED GENERATED ALWAYS AS (a * 3) STORED); -ERROR: multiple generation clauses specified for column "b" of table "gtest_err_1" -LINE 1: ...ARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED GENERATED ... - ^ --- references to other generated columns, including self-references -CREATE TABLE gtest_err_2a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STORED); -ERROR: cannot use generated column "b" in column generation expression -LINE 1: ...2a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STO... - ^ -DETAIL: A generated column cannot reference another generated column. -CREATE TABLE gtest_err_2b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STORED); -ERROR: cannot use generated column "b" in column generation expression -LINE 1: ...AYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STO... - ^ -DETAIL: A generated column cannot reference another generated column. --- a whole-row var is a self-reference on steroids, so disallow that too -CREATE TABLE gtest_err_2c (a int PRIMARY KEY, - b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) STORED); -ERROR: cannot use whole-row variable in column generation expression -LINE 2: b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) STOR... - ^ -DETAIL: This would cause the generated column to depend on its own value. --- invalid reference -CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED); -ERROR: column "c" does not exist -LINE 1: ..._3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STO... - ^ --- generation expression must be immutable -CREATE TABLE gtest_err_4 (a int PRIMARY KEY, b double precision GENERATED ALWAYS AS (random()) STORED); -ERROR: generation expression is not immutable --- cannot have default/identity and generated -CREATE TABLE gtest_err_5a (a int PRIMARY KEY, b int DEFAULT 5 GENERATED ALWAYS AS (a * 2) STORED); -ERROR: both default and generation expression specified for column "b" of table "gtest_err_5a" -LINE 1: ... gtest_err_5a (a int PRIMARY KEY, b int DEFAULT 5 GENERATED ... - ^ -CREATE TABLE gtest_err_5b (a int PRIMARY KEY, b int GENERATED ALWAYS AS identity GENERATED ALWAYS AS (a * 2) STORED); -ERROR: both identity and generation expression specified for column "b" of table "gtest_err_5b" -LINE 1: ...t PRIMARY KEY, b int GENERATED ALWAYS AS identity GENERATED ... - ^ --- reference to system column not allowed in generated column --- (except tableoid, which we test below) -CREATE TABLE gtest_err_6a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37) STORED); -ERROR: cannot use system column "xmin" in column generation expression -LINE 1: ...a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37... - ^ --- various prohibited constructs -CREATE TABLE gtest_err_7a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (avg(a)) STORED); -ERROR: aggregate functions are not allowed in column generation expressions -LINE 1: ...7a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (avg(a)) ST... - ^ -CREATE TABLE gtest_err_7b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (row_number() OVER (ORDER BY a)) STORED); -ERROR: window functions are not allowed in column generation expressions -LINE 1: ...7b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (row_number... - ^ -CREATE TABLE gtest_err_7c (a int PRIMARY KEY, b int GENERATED ALWAYS AS ((SELECT a)) STORED); -ERROR: cannot use subquery in column generation expression -LINE 1: ...7c (a int PRIMARY KEY, b int GENERATED ALWAYS AS ((SELECT a)... - ^ -CREATE TABLE gtest_err_7d (a int PRIMARY KEY, b int GENERATED ALWAYS AS (generate_series(1, a)) STORED); -ERROR: set-returning functions are not allowed in column generation expressions -LINE 1: ...7d (a int PRIMARY KEY, b int GENERATED ALWAYS AS (generate_s... - ^ --- GENERATED BY DEFAULT not allowed -CREATE TABLE gtest_err_8 (a int PRIMARY KEY, b int GENERATED BY DEFAULT AS (a * 2) STORED); -ERROR: for a generated column, GENERATED ALWAYS must be specified -LINE 1: ...E gtest_err_8 (a int PRIMARY KEY, b int GENERATED BY DEFAULT... - ^ -INSERT INTO gtest1 VALUES (1); -INSERT INTO gtest1 VALUES (2, DEFAULT); -- ok -INSERT INTO gtest1 VALUES (3, 33); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1 VALUES (3, 33), (4, 44); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1 VALUES (3, DEFAULT), (4, 44); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1 VALUES (3, 33), (4, DEFAULT); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1 VALUES (3, DEFAULT), (4, DEFAULT); -- ok -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 1 | 2 - 2 | 4 - 3 | 6 - 4 | 8 -(4 rows) - -DELETE FROM gtest1 WHERE a >= 3; -UPDATE gtest1 SET b = DEFAULT WHERE a = 1; -UPDATE gtest1 SET b = 11 WHERE a = 1; -- error -ERROR: column "b" can only be updated to DEFAULT -DETAIL: Column "b" is a generated column. -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 1 | 2 - 2 | 4 -(2 rows) - -SELECT a, b, b * 2 AS b2 FROM gtest1 ORDER BY a; - a | b | b2 ----+---+---- - 1 | 2 | 4 - 2 | 4 | 8 -(2 rows) - -SELECT a, b FROM gtest1 WHERE b = 4 ORDER BY a; - a | b ----+--- - 2 | 4 -(1 row) - --- test that overflow error happens on write -INSERT INTO gtest1 VALUES (2000000000); -ERROR: integer out of range -SELECT * FROM gtest1; - a | b ----+--- - 2 | 4 - 1 | 2 -(2 rows) - -DELETE FROM gtest1 WHERE a = 2000000000; --- test with joins -CREATE TABLE gtestx (x int, y int); -INSERT INTO gtestx VALUES (11, 1), (22, 2), (33, 3); -SELECT * FROM gtestx, gtest1 WHERE gtestx.y = gtest1.a; - x | y | a | b -----+---+---+--- - 11 | 1 | 1 | 2 - 22 | 2 | 2 | 4 -(2 rows) - -DROP TABLE gtestx; --- test UPDATE/DELETE quals -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 1 | 2 - 2 | 4 -(2 rows) - -UPDATE gtest1 SET a = 3 WHERE b = 4; -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 1 | 2 - 3 | 6 -(2 rows) - -DELETE FROM gtest1 WHERE b = 2; -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 3 | 6 -(1 row) - --- views -CREATE VIEW gtest1v AS SELECT * FROM gtest1; -SELECT * FROM gtest1v; - a | b ----+--- - 3 | 6 -(1 row) - -INSERT INTO gtest1v VALUES (4, 8); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1v VALUES (5, DEFAULT); -- ok -INSERT INTO gtest1v VALUES (6, 66), (7, 77); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1v VALUES (6, DEFAULT), (7, 77); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1v VALUES (6, 66), (7, DEFAULT); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1v VALUES (6, DEFAULT), (7, DEFAULT); -- ok -ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100; -INSERT INTO gtest1v VALUES (8, DEFAULT); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -INSERT INTO gtest1v VALUES (8, DEFAULT), (9, DEFAULT); -- error -ERROR: cannot insert a non-DEFAULT value into column "b" -DETAIL: Column "b" is a generated column. -SELECT * FROM gtest1v; - a | b ----+---- - 3 | 6 - 5 | 10 - 6 | 12 - 7 | 14 -(4 rows) - -DELETE FROM gtest1v WHERE a >= 5; -DROP VIEW gtest1v; --- CTEs -WITH foo AS (SELECT * FROM gtest1) SELECT * FROM foo; - a | b ----+--- - 3 | 6 -(1 row) - --- inheritance -CREATE TABLE gtest1_1 () INHERITS (gtest1); -SELECT * FROM gtest1_1; - a | b ----+--- -(0 rows) - -\d gtest1_1 - Table "public.gtest1_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | not null | - b | integer | | | generated always as (a * 2) stored -Inherits: gtest1 - -INSERT INTO gtest1_1 VALUES (4); -SELECT * FROM gtest1_1; - a | b ----+--- - 4 | 8 -(1 row) - -SELECT * FROM gtest1; - a | b ----+--- - 3 | 6 - 4 | 8 -(2 rows) - -CREATE TABLE gtest_normal (a int, b int); -CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal); -NOTICE: merging column "a" with inherited definition -NOTICE: merging column "b" with inherited definition -\d gtest_normal_child - Table "public.gtest_normal_child" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored -Inherits: gtest_normal - -INSERT INTO gtest_normal (a) VALUES (1); -INSERT INTO gtest_normal_child (a) VALUES (2); -SELECT * FROM gtest_normal; - a | b ----+--- - 1 | - 2 | 4 -(2 rows) - -CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); -ALTER TABLE gtest_normal_child2 INHERIT gtest_normal; -INSERT INTO gtest_normal_child2 (a) VALUES (3); -SELECT * FROM gtest_normal; - a | b ----+--- - 1 | - 2 | 4 - 3 | 9 -(3 rows) - --- test inheritance mismatches between parent and child -CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error -NOTICE: merging column "b" with inherited definition -ERROR: child column "b" specifies generation expression -HINT: Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table. -CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error -NOTICE: merging column "b" with inherited definition -ERROR: column "b" inherits from generated column but specifies default -CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error -NOTICE: merging column "b" with inherited definition -ERROR: column "b" inherits from generated column but specifies identity -CREATE TABLE gtestxx_1 (a int NOT NULL, b int); -ALTER TABLE gtestxx_1 INHERIT gtest1; -- error -ERROR: column "b" in child table must be a generated column -CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED); -ALTER TABLE gtestxx_2 INHERIT gtest1; -- error -ERROR: column "b" in child table has a conflicting generation expression -CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED); -ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok -CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL); -ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok --- test multiple inheritance mismatches -CREATE TABLE gtesty (x int, b int); -CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error -NOTICE: merging multiple inherited definitions of column "b" -ERROR: inherited column "b" has a generation conflict -DROP TABLE gtesty; -CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED); -CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error -NOTICE: merging multiple inherited definitions of column "b" -ERROR: column "b" inherits conflicting generation expressions -DROP TABLE gtesty; -CREATE TABLE gtesty (x int, b int DEFAULT 55); -CREATE TABLE gtest1_2 () INHERITS (gtest0, gtesty); -- error -NOTICE: merging multiple inherited definitions of column "b" -ERROR: inherited column "b" has a generation conflict -DROP TABLE gtesty; --- test stored update -CREATE TABLE gtest3 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); -INSERT INTO gtest3 (a) VALUES (1), (2), (3), (NULL); -SELECT * FROM gtest3 ORDER BY a; - a | b ----+--- - 1 | 3 - 2 | 6 - 3 | 9 - | -(4 rows) - -UPDATE gtest3 SET a = 22 WHERE a = 2; -SELECT * FROM gtest3 ORDER BY a; - a | b -----+---- - 1 | 3 - 3 | 9 - 22 | 66 - | -(4 rows) - -CREATE TABLE gtest3a (a text, b text GENERATED ALWAYS AS (a || '+' || a) STORED); -INSERT INTO gtest3a (a) VALUES ('a'), ('b'), ('c'), (NULL); -SELECT * FROM gtest3a ORDER BY a; - a | b ----+----- - a | a+a - b | b+b - c | c+c - | -(4 rows) - -UPDATE gtest3a SET a = 'bb' WHERE a = 'b'; -SELECT * FROM gtest3a ORDER BY a; - a | b -----+------- - a | a+a - bb | bb+bb - c | c+c - | -(4 rows) - --- COPY -TRUNCATE gtest1; -INSERT INTO gtest1 (a) VALUES (1), (2); -COPY gtest1 TO stdout; -1 -2 -COPY gtest1 (a, b) TO stdout; -ERROR: column "b" is a generated column -DETAIL: Generated columns cannot be used in COPY. -COPY gtest1 FROM stdin; -COPY gtest1 (a, b) FROM stdin; -ERROR: column "b" is a generated column -DETAIL: Generated columns cannot be used in COPY. -SELECT * FROM gtest1 ORDER BY a; - a | b ----+--- - 1 | 2 - 2 | 4 - 3 | 6 - 4 | 8 -(4 rows) - -TRUNCATE gtest3; -INSERT INTO gtest3 (a) VALUES (1), (2); -COPY gtest3 TO stdout; -1 -2 -COPY gtest3 (a, b) TO stdout; -ERROR: column "b" is a generated column -DETAIL: Generated columns cannot be used in COPY. -COPY gtest3 FROM stdin; -COPY gtest3 (a, b) FROM stdin; -ERROR: column "b" is a generated column -DETAIL: Generated columns cannot be used in COPY. -SELECT * FROM gtest3 ORDER BY a; - a | b ----+---- - 1 | 3 - 2 | 6 - 3 | 9 - 4 | 12 -(4 rows) - --- null values -CREATE TABLE gtest2 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (NULL) STORED); -INSERT INTO gtest2 VALUES (1); -SELECT * FROM gtest2; - a | b ----+--- - 1 | -(1 row) - --- simple column reference for varlena types -CREATE TABLE gtest_varlena (a varchar, b varchar GENERATED ALWAYS AS (a) STORED); -INSERT INTO gtest_varlena (a) VALUES('01234567890123456789'); -INSERT INTO gtest_varlena (a) VALUES(NULL); -SELECT * FROM gtest_varlena ORDER BY a; - a | b -----------------------+---------------------- - 01234567890123456789 | 01234567890123456789 - | -(2 rows) - -DROP TABLE gtest_varlena; --- composite types -CREATE TYPE double_int as (a int, b int); -CREATE TABLE gtest4 ( - a int, - b double_int GENERATED ALWAYS AS ((a * 2, a * 3)) STORED -); -INSERT INTO gtest4 VALUES (1), (6); -SELECT * FROM gtest4; - a | b ----+--------- - 1 | (2,3) - 6 | (12,18) -(2 rows) - -DROP TABLE gtest4; -DROP TYPE double_int; --- using tableoid is allowed -CREATE TABLE gtest_tableoid ( - a int PRIMARY KEY, - b bool GENERATED ALWAYS AS (tableoid = 'gtest_tableoid'::regclass) STORED -); -INSERT INTO gtest_tableoid VALUES (1), (2); -ALTER TABLE gtest_tableoid ADD COLUMN - c regclass GENERATED ALWAYS AS (tableoid) STORED; -SELECT * FROM gtest_tableoid; - a | b | c ----+---+---------------- - 1 | t | gtest_tableoid - 2 | t | gtest_tableoid -(2 rows) - --- drop column behavior -CREATE TABLE gtest10 (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED); -ALTER TABLE gtest10 DROP COLUMN b; -\d gtest10 - Table "public.gtest10" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | -Indexes: - "gtest10_pkey" PRIMARY KEY, btree (a) - -CREATE TABLE gtest10a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); -ALTER TABLE gtest10a DROP COLUMN b; -INSERT INTO gtest10a (a) VALUES (1); --- privileges -CREATE USER regress_user11; -CREATE TABLE gtest11s (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED); -INSERT INTO gtest11s VALUES (1, 10), (2, 20); -GRANT SELECT (a, c) ON gtest11s TO regress_user11; -CREATE FUNCTION gf1(a int) RETURNS int AS $$ SELECT a * 3 $$ IMMUTABLE LANGUAGE SQL; -REVOKE ALL ON FUNCTION gf1(int) FROM PUBLIC; -CREATE TABLE gtest12s (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (gf1(b)) STORED); -INSERT INTO gtest12s VALUES (1, 10), (2, 20); -GRANT SELECT (a, c) ON gtest12s TO regress_user11; -SET ROLE regress_user11; -SELECT a, b FROM gtest11s; -- not allowed -ERROR: permission denied for table gtest11s -SELECT a, c FROM gtest11s; -- allowed - a | c ----+---- - 1 | 20 - 2 | 40 -(2 rows) - -SELECT gf1(10); -- not allowed -ERROR: permission denied for function gf1 -SELECT a, c FROM gtest12s; -- allowed - a | c ----+---- - 1 | 30 - 2 | 60 -(2 rows) - -RESET ROLE; -DROP TABLE gtest11s, gtest12s; -DROP FUNCTION gf1(int); -DROP USER regress_user11; --- check constraints -CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED CHECK (b < 50)); -INSERT INTO gtest20 (a) VALUES (10); -- ok -INSERT INTO gtest20 (a) VALUES (30); -- violates constraint -ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check" -DETAIL: Failing row contains (30, 60). -CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); -INSERT INTO gtest20a (a) VALUES (10); -INSERT INTO gtest20a (a) VALUES (30); -ALTER TABLE gtest20a ADD CHECK (b < 50); -- fails on existing row -ERROR: check constraint "gtest20a_b_check" of relation "gtest20a" is violated by some row -CREATE TABLE gtest20b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); -INSERT INTO gtest20b (a) VALUES (10); -INSERT INTO gtest20b (a) VALUES (30); -ALTER TABLE gtest20b ADD CONSTRAINT chk CHECK (b < 50) NOT VALID; -ALTER TABLE gtest20b VALIDATE CONSTRAINT chk; -- fails on existing row -ERROR: check constraint "chk" of relation "gtest20b" is violated by some row --- not-null constraints -CREATE TABLE gtest21a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif(a, 0)) STORED NOT NULL); -INSERT INTO gtest21a (a) VALUES (1); -- ok -INSERT INTO gtest21a (a) VALUES (0); -- violates constraint -ERROR: null value in column "b" of relation "gtest21a" violates not-null constraint -DETAIL: Failing row contains (0, null). -CREATE TABLE gtest21b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif(a, 0)) STORED); -ALTER TABLE gtest21b ALTER COLUMN b SET NOT NULL; -INSERT INTO gtest21b (a) VALUES (1); -- ok -INSERT INTO gtest21b (a) VALUES (0); -- violates constraint -ERROR: null value in column "b" of relation "gtest21b" violates not-null constraint -DETAIL: Failing row contains (0, null). -ALTER TABLE gtest21b ALTER COLUMN b DROP NOT NULL; -INSERT INTO gtest21b (a) VALUES (0); -- ok now --- index constraints -CREATE TABLE gtest22a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a / 2) STORED UNIQUE); -INSERT INTO gtest22a VALUES (2); -INSERT INTO gtest22a VALUES (3); -ERROR: duplicate key value violates unique constraint "gtest22a_b_key" -DETAIL: Key (b)=(1) already exists. -INSERT INTO gtest22a VALUES (4); -CREATE TABLE gtest22b (a int, b int GENERATED ALWAYS AS (a / 2) STORED, PRIMARY KEY (a, b)); -INSERT INTO gtest22b VALUES (2); -INSERT INTO gtest22b VALUES (2); -ERROR: duplicate key value violates unique constraint "gtest22b_pkey" -DETAIL: Key (a, b)=(2, 1) already exists. --- indexes -CREATE TABLE gtest22c (a int, b int GENERATED ALWAYS AS (a * 2) STORED); -CREATE INDEX gtest22c_b_idx ON gtest22c (b); -CREATE INDEX gtest22c_expr_idx ON gtest22c ((b * 3)); -CREATE INDEX gtest22c_pred_idx ON gtest22c (a) WHERE b > 0; -\d gtest22c - Table "public.gtest22c" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored -Indexes: - "gtest22c_b_idx" btree (b) - "gtest22c_expr_idx" btree ((b * 3)) - "gtest22c_pred_idx" btree (a) WHERE b > 0 - -INSERT INTO gtest22c VALUES (1), (2), (3); -SET enable_seqscan TO off; -SET enable_bitmapscan TO off; -EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b = 4; - QUERY PLAN ---------------------------------------------- - Index Scan using gtest22c_b_idx on gtest22c - Index Cond: (b = 4) -(2 rows) - -SELECT * FROM gtest22c WHERE b = 4; - a | b ----+--- - 2 | 4 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b * 3 = 6; - QUERY PLAN ------------------------------------------------- - Index Scan using gtest22c_expr_idx on gtest22c - Index Cond: ((b * 3) = 6) -(2 rows) - -SELECT * FROM gtest22c WHERE b * 3 = 6; - a | b ----+--- - 1 | 2 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0; - QUERY PLAN ------------------------------------------------- - Index Scan using gtest22c_pred_idx on gtest22c - Index Cond: (a = 1) -(2 rows) - -SELECT * FROM gtest22c WHERE a = 1 AND b > 0; - a | b ----+--- - 1 | 2 -(1 row) - -RESET enable_seqscan; -RESET enable_bitmapscan; --- foreign keys -CREATE TABLE gtest23a (x int PRIMARY KEY, y int); -INSERT INTO gtest23a VALUES (1, 11), (2, 22), (3, 33); -CREATE TABLE gtest23x (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON UPDATE CASCADE); -- error -ERROR: invalid ON UPDATE action for foreign key constraint containing generated column -CREATE TABLE gtest23x (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON DELETE SET NULL); -- error -ERROR: invalid ON DELETE action for foreign key constraint containing generated column -CREATE TABLE gtest23b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x)); -\d gtest23b - Table "public.gtest23b" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | not null | - b | integer | | | generated always as (a * 2) stored -Indexes: - "gtest23b_pkey" PRIMARY KEY, btree (a) -Foreign-key constraints: - "gtest23b_b_fkey" FOREIGN KEY (b) REFERENCES gtest23a(x) - -INSERT INTO gtest23b VALUES (1); -- ok -INSERT INTO gtest23b VALUES (5); -- error -ERROR: insert or update on table "gtest23b" violates foreign key constraint "gtest23b_b_fkey" -DETAIL: Key (b)=(10) is not present in table "gtest23a". -DROP TABLE gtest23b; -DROP TABLE gtest23a; -CREATE TABLE gtest23p (x int, y int GENERATED ALWAYS AS (x * 2) STORED, PRIMARY KEY (y)); -INSERT INTO gtest23p VALUES (1), (2), (3); -CREATE TABLE gtest23q (a int PRIMARY KEY, b int REFERENCES gtest23p (y)); -INSERT INTO gtest23q VALUES (1, 2); -- ok -INSERT INTO gtest23q VALUES (2, 5); -- error -ERROR: insert or update on table "gtest23q" violates foreign key constraint "gtest23q_b_fkey" -DETAIL: Key (b)=(5) is not present in table "gtest23p". --- domains -CREATE DOMAIN gtestdomain1 AS int CHECK (VALUE < 10); -CREATE TABLE gtest24 (a int PRIMARY KEY, b gtestdomain1 GENERATED ALWAYS AS (a * 2) STORED); -INSERT INTO gtest24 (a) VALUES (4); -- ok -INSERT INTO gtest24 (a) VALUES (6); -- error -ERROR: value for domain gtestdomain1 violates check constraint "gtestdomain1_check" --- typed tables (currently not supported) -CREATE TYPE gtest_type AS (f1 integer, f2 text, f3 bigint); -CREATE TABLE gtest28 OF gtest_type (f1 WITH OPTIONS GENERATED ALWAYS AS (f2 *2) STORED); -ERROR: generated columns are not supported on typed tables -DROP TYPE gtest_type CASCADE; --- table partitions (currently not supported) -CREATE TABLE gtest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1); -CREATE TABLE gtest_child PARTITION OF gtest_parent ( - f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED -) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error -ERROR: generated columns are not supported on partitions -DROP TABLE gtest_parent; --- partitioned table -CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f1); -CREATE TABLE gtest_child PARTITION OF gtest_parent FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1); -SELECT * FROM gtest_parent; - f1 | f2 | f3 -------------+----+---- - 07-15-2016 | 1 | 2 -(1 row) - -SELECT * FROM gtest_child; - f1 | f2 | f3 -------------+----+---- - 07-15-2016 | 1 | 2 -(1 row) - -DROP TABLE gtest_parent; --- generated columns in partition key (not allowed) -CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3); -ERROR: cannot use generated column in partition key -LINE 1: ...ENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3); - ^ -DETAIL: Column "f3" is a generated column. -CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3)); -ERROR: cannot use generated column in partition key -LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3)); - ^ -DETAIL: Column "f3" is a generated column. --- ALTER TABLE ... ADD COLUMN -CREATE TABLE gtest25 (a int PRIMARY KEY); -INSERT INTO gtest25 VALUES (3), (4); -ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 3) STORED; -SELECT * FROM gtest25 ORDER BY a; - a | b ----+---- - 3 | 9 - 4 | 12 -(2 rows) - -ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (b * 4) STORED; -- error -ERROR: cannot use generated column "b" in column generation expression -DETAIL: A generated column cannot reference another generated column. -ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (z * 4) STORED; -- error -ERROR: column "z" does not exist -ALTER TABLE gtest25 ADD COLUMN c int DEFAULT 42, - ADD COLUMN x int GENERATED ALWAYS AS (c * 4) STORED; -ALTER TABLE gtest25 ADD COLUMN d int DEFAULT 101; -ALTER TABLE gtest25 ALTER COLUMN d SET DATA TYPE float8, - ADD COLUMN y float8 GENERATED ALWAYS AS (d * 4) STORED; -SELECT * FROM gtest25 ORDER BY a; - a | b | c | x | d | y ----+----+----+-----+-----+----- - 3 | 9 | 42 | 168 | 101 | 404 - 4 | 12 | 42 | 168 | 101 | 404 -(2 rows) - -\d gtest25 - Table "public.gtest25" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+------------------------------------------------------ - a | integer | | not null | - b | integer | | | generated always as (a * 3) stored - c | integer | | | 42 - x | integer | | | generated always as (c * 4) stored - d | double precision | | | 101 - y | double precision | | | generated always as (d * 4::double precision) stored -Indexes: - "gtest25_pkey" PRIMARY KEY, btree (a) - --- ALTER TABLE ... ALTER COLUMN -CREATE TABLE gtest27 ( - a int, - b int, - x int GENERATED ALWAYS AS ((a + b) * 2) STORED -); -INSERT INTO gtest27 (a, b) VALUES (3, 7), (4, 11); -ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error -ERROR: cannot alter type of a column used by a generated column -DETAIL: Column "a" is used by generated column "x". -ALTER TABLE gtest27 ALTER COLUMN x TYPE numeric; -\d gtest27 - Table "public.gtest27" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+-------------------------------------------- - a | integer | | | - b | integer | | | - x | numeric | | | generated always as (((a + b) * 2)) stored - -SELECT * FROM gtest27; - a | b | x ----+----+---- - 3 | 7 | 20 - 4 | 11 | 30 -(2 rows) - -ALTER TABLE gtest27 ALTER COLUMN x TYPE boolean USING x <> 0; -- error -ERROR: generation expression for column "x" cannot be cast automatically to type boolean -ALTER TABLE gtest27 ALTER COLUMN x DROP DEFAULT; -- error -ERROR: column "x" of relation "gtest27" is a generated column -HINT: Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead. --- It's possible to alter the column types this way: -ALTER TABLE gtest27 - DROP COLUMN x, - ALTER COLUMN a TYPE bigint, - ALTER COLUMN b TYPE bigint, - ADD COLUMN x bigint GENERATED ALWAYS AS ((a + b) * 2) STORED; -\d gtest27 - Table "public.gtest27" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+------------------------------------------ - a | bigint | | | - b | bigint | | | - x | bigint | | | generated always as ((a + b) * 2) stored - --- Ideally you could just do this, but not today (and should x change type?): -ALTER TABLE gtest27 - ALTER COLUMN a TYPE float8, - ALTER COLUMN b TYPE float8; -- error -ERROR: cannot alter type of a column used by a generated column -DETAIL: Column "a" is used by generated column "x". -\d gtest27 - Table "public.gtest27" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+------------------------------------------ - a | bigint | | | - b | bigint | | | - x | bigint | | | generated always as ((a + b) * 2) stored - -SELECT * FROM gtest27; - a | b | x ----+----+---- - 3 | 7 | 20 - 4 | 11 | 30 -(2 rows) - --- ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION -CREATE TABLE gtest29 ( - a int, - b int GENERATED ALWAYS AS (a * 2) STORED -); -INSERT INTO gtest29 (a) VALUES (3), (4); -ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION; -- error -ERROR: column "a" of relation "gtest29" is not a stored generated column -ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice -NOTICE: column "a" of relation "gtest29" is not a stored generated column, skipping -ALTER TABLE gtest29 ALTER COLUMN b DROP EXPRESSION; -INSERT INTO gtest29 (a) VALUES (5); -INSERT INTO gtest29 (a, b) VALUES (6, 66); -SELECT * FROM gtest29; - a | b ----+---- - 3 | 6 - 4 | 8 - 5 | - 6 | 66 -(4 rows) - -\d gtest29 - Table "public.gtest29" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - --- check that dependencies between columns have also been removed -ALTER TABLE gtest29 DROP COLUMN a; -- should not drop b -\d gtest29 - Table "public.gtest29" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | integer | | | - --- with inheritance -CREATE TABLE gtest30 ( - a int, - b int GENERATED ALWAYS AS (a * 2) STORED -); -CREATE TABLE gtest30_1 () INHERITS (gtest30); -ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION; -\d gtest30 - Table "public.gtest30" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Number of child tables: 1 (Use \d+ to list them.) - -\d gtest30_1 - Table "public.gtest30_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Inherits: gtest30 - -DROP TABLE gtest30 CASCADE; -NOTICE: drop cascades to table gtest30_1 -CREATE TABLE gtest30 ( - a int, - b int GENERATED ALWAYS AS (a * 2) STORED -); -CREATE TABLE gtest30_1 () INHERITS (gtest30); -ALTER TABLE ONLY gtest30 ALTER COLUMN b DROP EXPRESSION; -- error -ERROR: ALTER TABLE / DROP EXPRESSION must be applied to child tables too -\d gtest30 - Table "public.gtest30" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored -Number of child tables: 1 (Use \d+ to list them.) - -\d gtest30_1 - Table "public.gtest30_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored -Inherits: gtest30 - -ALTER TABLE gtest30_1 ALTER COLUMN b DROP EXPRESSION; -- error -ERROR: cannot drop generation expression from inherited column --- triggers -CREATE TABLE gtest26 ( - a int PRIMARY KEY, - b int GENERATED ALWAYS AS (a * 2) STORED -); -CREATE FUNCTION gtest_trigger_func() RETURNS trigger - LANGUAGE plpgsql -AS $$ -BEGIN - IF tg_op IN ('DELETE', 'UPDATE') THEN - RAISE INFO '%: %: old = %', TG_NAME, TG_WHEN, OLD; - END IF; - IF tg_op IN ('INSERT', 'UPDATE') THEN - RAISE INFO '%: %: new = %', TG_NAME, TG_WHEN, NEW; - END IF; - IF tg_op = 'DELETE' THEN - RETURN OLD; - ELSE - RETURN NEW; - END IF; -END -$$; -CREATE TRIGGER gtest1 BEFORE DELETE OR UPDATE ON gtest26 - FOR EACH ROW - WHEN (OLD.b < 0) -- ok - EXECUTE PROCEDURE gtest_trigger_func(); -CREATE TRIGGER gtest2a BEFORE INSERT OR UPDATE ON gtest26 - FOR EACH ROW - WHEN (NEW.b < 0) -- error - EXECUTE PROCEDURE gtest_trigger_func(); -ERROR: BEFORE trigger's WHEN condition cannot reference NEW generated columns -LINE 3: WHEN (NEW.b < 0) - ^ -DETAIL: Column "b" is a generated column. -CREATE TRIGGER gtest2b BEFORE INSERT OR UPDATE ON gtest26 - FOR EACH ROW - WHEN (NEW.* IS NOT NULL) -- error - EXECUTE PROCEDURE gtest_trigger_func(); -ERROR: BEFORE trigger's WHEN condition cannot reference NEW generated columns -LINE 3: WHEN (NEW.* IS NOT NULL) - ^ -DETAIL: A whole-row reference is used and the table contains generated columns. -CREATE TRIGGER gtest2 BEFORE INSERT ON gtest26 - FOR EACH ROW - WHEN (NEW.a < 0) - EXECUTE PROCEDURE gtest_trigger_func(); -CREATE TRIGGER gtest3 AFTER DELETE OR UPDATE ON gtest26 - FOR EACH ROW - WHEN (OLD.b < 0) -- ok - EXECUTE PROCEDURE gtest_trigger_func(); -CREATE TRIGGER gtest4 AFTER INSERT OR UPDATE ON gtest26 - FOR EACH ROW - WHEN (NEW.b < 0) -- ok - EXECUTE PROCEDURE gtest_trigger_func(); -INSERT INTO gtest26 (a) VALUES (-2), (0), (3); -INFO: gtest2: BEFORE: new = (-2,) -INFO: gtest4: AFTER: new = (-2,-4) -SELECT * FROM gtest26 ORDER BY a; - a | b -----+---- - -2 | -4 - 0 | 0 - 3 | 6 -(3 rows) - -UPDATE gtest26 SET a = a * -2; -INFO: gtest1: BEFORE: old = (-2,-4) -INFO: gtest1: BEFORE: new = (4,) -INFO: gtest3: AFTER: old = (-2,-4) -INFO: gtest3: AFTER: new = (4,8) -INFO: gtest4: AFTER: old = (3,6) -INFO: gtest4: AFTER: new = (-6,-12) -SELECT * FROM gtest26 ORDER BY a; - a | b -----+----- - -6 | -12 - 0 | 0 - 4 | 8 -(3 rows) - -DELETE FROM gtest26 WHERE a = -6; -INFO: gtest1: BEFORE: old = (-6,-12) -INFO: gtest3: AFTER: old = (-6,-12) -SELECT * FROM gtest26 ORDER BY a; - a | b ----+--- - 0 | 0 - 4 | 8 -(2 rows) - -DROP TRIGGER gtest1 ON gtest26; -DROP TRIGGER gtest2 ON gtest26; -DROP TRIGGER gtest3 ON gtest26; --- Check that an UPDATE of "a" fires the trigger for UPDATE OF b, per --- SQL standard. -CREATE FUNCTION gtest_trigger_func3() RETURNS trigger - LANGUAGE plpgsql -AS $$ -BEGIN - RAISE NOTICE 'OK'; - RETURN NEW; -END -$$; -CREATE TRIGGER gtest11 BEFORE UPDATE OF b ON gtest26 - FOR EACH ROW - EXECUTE PROCEDURE gtest_trigger_func3(); -UPDATE gtest26 SET a = 1 WHERE a = 0; -NOTICE: OK -DROP TRIGGER gtest11 ON gtest26; -TRUNCATE gtest26; --- check that modifications of stored generated columns in triggers do --- not get propagated -CREATE FUNCTION gtest_trigger_func4() RETURNS trigger - LANGUAGE plpgsql -AS $$ -BEGIN - NEW.a = 10; - NEW.b = 300; - RETURN NEW; -END; -$$; -CREATE TRIGGER gtest12_01 BEFORE UPDATE ON gtest26 - FOR EACH ROW - EXECUTE PROCEDURE gtest_trigger_func(); -CREATE TRIGGER gtest12_02 BEFORE UPDATE ON gtest26 - FOR EACH ROW - EXECUTE PROCEDURE gtest_trigger_func4(); -CREATE TRIGGER gtest12_03 BEFORE UPDATE ON gtest26 - FOR EACH ROW - EXECUTE PROCEDURE gtest_trigger_func(); -INSERT INTO gtest26 (a) VALUES (1); -UPDATE gtest26 SET a = 11 WHERE a = 1; -INFO: gtest12_01: BEFORE: old = (1,2) -INFO: gtest12_01: BEFORE: new = (11,) -INFO: gtest12_03: BEFORE: old = (1,2) -INFO: gtest12_03: BEFORE: new = (10,) -SELECT * FROM gtest26 ORDER BY a; - a | b -----+---- - 10 | 20 -(1 row) - --- LIKE INCLUDING GENERATED and dropped column handling -CREATE TABLE gtest28a ( - a int, - b int, - c int, - x int GENERATED ALWAYS AS (b * 2) STORED -); -ALTER TABLE gtest28a DROP COLUMN a; -CREATE TABLE gtest28b (LIKE gtest28a INCLUDING GENERATED); -\d gtest28* - Table "public.gtest28a" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - b | integer | | | - c | integer | | | - x | integer | | | generated always as (b * 2) stored - - Table "public.gtest28b" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - b | integer | | | - c | integer | | | - x | integer | | | generated always as (b * 2) stored - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/join_hash.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/join_hash.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/join_hash.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/join_hash.out 2021-10-30 22:04:14 -0600 @@ -1,1015 +1,2 @@ --- --- exercises for the hash join code --- -begin; -set local min_parallel_table_scan_size = 0; -set local parallel_setup_cost = 0; -set local enable_hashjoin = on; --- Extract bucket and batch counts from an explain analyze plan. In --- general we can't make assertions about how many batches (or --- buckets) will be required because it can vary, but we can in some --- special cases and we can check for growth. -create or replace function find_hash(node json) -returns json language plpgsql -as -$$ -declare - x json; - child json; -begin - if node->>'Node Type' = 'Hash' then - return node; - else - for child in select json_array_elements(node->'Plans') - loop - x := find_hash(child); - if x is not null then - return x; - end if; - end loop; - return null; - end if; -end; -$$; -create or replace function hash_join_batches(query text) -returns table (original int, final int) language plpgsql -as -$$ -declare - whole_plan json; - hash_node json; -begin - for whole_plan in - execute 'explain (analyze, format ''json'') ' || query - loop - hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); - original := hash_node->>'Original Hash Batches'; - final := hash_node->>'Hash Batches'; - return next; - end loop; -end; -$$; --- Make a simple relation with well distributed keys and correctly --- estimated size. -create table simple as - select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table simple set (parallel_workers = 2); -analyze simple; --- Make a relation whose size we will under-estimate. We want stats --- to say 1000 rows, but actually there are 20,000 rows. -create table bigger_than_it_looks as - select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); -alter table bigger_than_it_looks set (parallel_workers = 2); -analyze bigger_than_it_looks; -update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; --- Make a relation whose size we underestimate and that also has a --- kind of skew that breaks our batching scheme. We want stats to say --- 2 rows, but actually there are 20,000 rows with the same key. -create table extremely_skewed (id int, t text); -alter table extremely_skewed set (autovacuum_enabled = 'false'); -alter table extremely_skewed set (parallel_workers = 2); -analyze extremely_skewed; -insert into extremely_skewed - select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - from generate_series(1, 20000); -update pg_class - set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 - where relname = 'extremely_skewed'; --- Make a relation with a couple of enormous tuples. -create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; -alter table wide set (parallel_workers = 2); --- The "optimal" case: the hash table fits in memory; we plan for 1 --- batch, we stick to that number, and peak memory usage stays within --- our work_mem budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '4MB'; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- The "good" case: batches required, but we plan the right number; we --- plan for some number of batches, and we stick to that number, and --- peak memory usage says within our work_mem budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- The "bad" case: during execution we need to increase number of --- batches; in this case we plan for 1 batch, and increase at least a --- couple of times, and peak memory usage stays within our work_mem --- budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on bigger_than_it_looks s -(6 rows) - -select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); - QUERY PLAN ------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on bigger_than_it_looks s -(9 rows) - -select count(*) from simple r join bigger_than_it_looks s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); - QUERY PLAN ---------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 1 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on bigger_than_it_looks s -(9 rows) - -select count(*) from simple r join bigger_than_it_looks s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- The "ugly" case: increasing the number of batches during execution --- doesn't help, so stop trying to fit in work_mem and hope for the --- best; in this case we plan for 1 batch, increases just once and --- then stop increasing because that didn't help at all, so we blow --- right through the work_mem budget and hope for the best... --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN --------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on extremely_skewed s -(6 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 2 -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN --------------------------------------------------------- - Aggregate - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on extremely_skewed s -(8 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 2 -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '128kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN ------------------------------------------------------------------------ - Finalize Aggregate - -> Gather - Workers Planned: 1 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on extremely_skewed s -(9 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 4 -(1 row) - -rollback to settings; --- A couple of other hash join tests unrelated to work_mem management. --- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local parallel_leader_participation = off; -select * from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - original | final -----------+------- - 1 | 1 -(1 row) - -rollback to settings; --- Exercise rescans. We'll turn off parallel_leader_participation so --- that we can check that instrumentation comes back correctly. -create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; -alter table join_foo set (parallel_workers = 0); -create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; -alter table join_bar set (parallel_workers = 2); --- multi-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Hash - -> Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; --- single-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Hash - -> Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - f -(1 row) - -rollback to settings; --- multi-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Parallel Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Parallel Hash - -> Parallel Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; --- single-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Parallel Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Parallel Hash - -> Parallel Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - f -(1 row) - -rollback to settings; --- A full outer join where every record is matched. --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r full outer join simple s using (id); - count -------- - 20000 -(1 row) - -rollback to settings; --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r full outer join simple s using (id); - count -------- - 20000 -(1 row) - -rollback to settings; --- An full outer join where every record is not matched. --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: ((0 - s.id) = r.id) - -> Seq Scan on simple s - -> Hash - -> Seq Scan on simple r -(6 rows) - -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - count -------- - 40000 -(1 row) - -rollback to settings; --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: ((0 - s.id) = r.id) - -> Seq Scan on simple s - -> Hash - -> Seq Scan on simple r -(6 rows) - -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - count -------- - 40000 -(1 row) - -rollback to settings; --- exercise special code paths for huge tuples (note use of non-strict --- expression and left join required to get the detoasted tuple into --- the hash table) --- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and --- sts_puttuple oversized tuple cases because it's multi-batch) -savepoint settings; -set max_parallel_workers_per_gather = 2; -set enable_parallel_hash = on; -set work_mem = '128kB'; -explain (costs off) - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); - QUERY PLAN ----------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Left Join - Hash Cond: (wide.id = wide_1.id) - -> Parallel Seq Scan on wide - -> Parallel Hash - -> Parallel Seq Scan on wide wide_1 -(9 rows) - -select length(max(s.t)) -from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); - length --------- - 320000 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; -rollback; --- Verify that hash key expressions reference the correct --- nodes. Hashjoin's hashkeys need to reference its outer plan, Hash's --- need to reference Hash's outer plan (which is below HashJoin's --- inner plan). It's not trivial to verify that the references are --- correct (we don't display the hashkeys themselves), but if the --- hashkeys contain subplan references, those will be displayed. Force --- subplans to appear just about everywhere. --- --- Bug report: --- https://www.postgresql.org/message-id/CAPpHfdvGVegF_TKKRiBrSmatJL2dR9uwFCuR%2BteQ_8tEXU8mxg%40mail.gmail.com --- -BEGIN; -SET LOCAL enable_sort = OFF; -- avoid mergejoins -SET LOCAL from_collapse_limit = 1; -- allows easy changing of join order -CREATE TABLE hjtest_1 (a text, b int, id int, c bool); -CREATE TABLE hjtest_2 (a bool, id int, b text, c int); -INSERT INTO hjtest_1(a, b, id, c) VALUES ('text', 2, 1, false); -- matches -INSERT INTO hjtest_1(a, b, id, c) VALUES ('text', 1, 2, false); -- fails id join condition -INSERT INTO hjtest_1(a, b, id, c) VALUES ('text', 20, 1, false); -- fails < 50 -INSERT INTO hjtest_1(a, b, id, c) VALUES ('text', 1, 1, false); -- fails (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) -INSERT INTO hjtest_2(a, id, b, c) VALUES (true, 1, 'another', 2); -- matches -INSERT INTO hjtest_2(a, id, b, c) VALUES (true, 3, 'another', 7); -- fails id join condition -INSERT INTO hjtest_2(a, id, b, c) VALUES (true, 1, 'another', 90); -- fails < 55 -INSERT INTO hjtest_2(a, id, b, c) VALUES (true, 1, 'another', 3); -- fails (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) -INSERT INTO hjtest_2(a, id, b, c) VALUES (true, 1, 'text', 1); -- fails hjtest_1.a <> hjtest_2.b; -EXPLAIN (COSTS OFF, VERBOSE) -SELECT hjtest_1.a a1, hjtest_2.a a2,hjtest_1.tableoid::regclass t1, hjtest_2.tableoid::regclass t2 -FROM hjtest_1, hjtest_2 -WHERE - hjtest_1.id = (SELECT 1 WHERE hjtest_2.id = 1) - AND (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) - AND (SELECT hjtest_1.b * 5) < 50 - AND (SELECT hjtest_2.c * 5) < 55 - AND hjtest_1.a <> hjtest_2.b; - QUERY PLAN ------------------------------------------------------------------------------------------------- - Hash Join - Output: hjtest_1.a, hjtest_2.a, (hjtest_1.tableoid)::regclass, (hjtest_2.tableoid)::regclass - Hash Cond: ((hjtest_1.id = (SubPlan 1)) AND ((SubPlan 2) = (SubPlan 3))) - Join Filter: (hjtest_1.a <> hjtest_2.b) - -> Seq Scan on public.hjtest_1 - Output: hjtest_1.a, hjtest_1.tableoid, hjtest_1.id, hjtest_1.b - Filter: ((SubPlan 4) < 50) - SubPlan 4 - -> Result - Output: (hjtest_1.b * 5) - -> Hash - Output: hjtest_2.a, hjtest_2.tableoid, hjtest_2.id, hjtest_2.c, hjtest_2.b - -> Seq Scan on public.hjtest_2 - Output: hjtest_2.a, hjtest_2.tableoid, hjtest_2.id, hjtest_2.c, hjtest_2.b - Filter: ((SubPlan 5) < 55) - SubPlan 5 - -> Result - Output: (hjtest_2.c * 5) - SubPlan 1 - -> Result - Output: 1 - One-Time Filter: (hjtest_2.id = 1) - SubPlan 3 - -> Result - Output: (hjtest_2.c * 5) - SubPlan 2 - -> Result - Output: (hjtest_1.b * 5) -(28 rows) - -SELECT hjtest_1.a a1, hjtest_2.a a2,hjtest_1.tableoid::regclass t1, hjtest_2.tableoid::regclass t2 -FROM hjtest_1, hjtest_2 -WHERE - hjtest_1.id = (SELECT 1 WHERE hjtest_2.id = 1) - AND (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) - AND (SELECT hjtest_1.b * 5) < 50 - AND (SELECT hjtest_2.c * 5) < 55 - AND hjtest_1.a <> hjtest_2.b; - a1 | a2 | t1 | t2 -------+----+----------+---------- - text | t | hjtest_1 | hjtest_2 -(1 row) - -EXPLAIN (COSTS OFF, VERBOSE) -SELECT hjtest_1.a a1, hjtest_2.a a2,hjtest_1.tableoid::regclass t1, hjtest_2.tableoid::regclass t2 -FROM hjtest_2, hjtest_1 -WHERE - hjtest_1.id = (SELECT 1 WHERE hjtest_2.id = 1) - AND (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) - AND (SELECT hjtest_1.b * 5) < 50 - AND (SELECT hjtest_2.c * 5) < 55 - AND hjtest_1.a <> hjtest_2.b; - QUERY PLAN ------------------------------------------------------------------------------------------------- - Hash Join - Output: hjtest_1.a, hjtest_2.a, (hjtest_1.tableoid)::regclass, (hjtest_2.tableoid)::regclass - Hash Cond: (((SubPlan 1) = hjtest_1.id) AND ((SubPlan 3) = (SubPlan 2))) - Join Filter: (hjtest_1.a <> hjtest_2.b) - -> Seq Scan on public.hjtest_2 - Output: hjtest_2.a, hjtest_2.tableoid, hjtest_2.id, hjtest_2.c, hjtest_2.b - Filter: ((SubPlan 5) < 55) - SubPlan 5 - -> Result - Output: (hjtest_2.c * 5) - -> Hash - Output: hjtest_1.a, hjtest_1.tableoid, hjtest_1.id, hjtest_1.b - -> Seq Scan on public.hjtest_1 - Output: hjtest_1.a, hjtest_1.tableoid, hjtest_1.id, hjtest_1.b - Filter: ((SubPlan 4) < 50) - SubPlan 4 - -> Result - Output: (hjtest_1.b * 5) - SubPlan 2 - -> Result - Output: (hjtest_1.b * 5) - SubPlan 1 - -> Result - Output: 1 - One-Time Filter: (hjtest_2.id = 1) - SubPlan 3 - -> Result - Output: (hjtest_2.c * 5) -(28 rows) - -SELECT hjtest_1.a a1, hjtest_2.a a2,hjtest_1.tableoid::regclass t1, hjtest_2.tableoid::regclass t2 -FROM hjtest_2, hjtest_1 -WHERE - hjtest_1.id = (SELECT 1 WHERE hjtest_2.id = 1) - AND (SELECT hjtest_1.b * 5) = (SELECT hjtest_2.c*5) - AND (SELECT hjtest_1.b * 5) < 50 - AND (SELECT hjtest_2.c * 5) < 55 - AND hjtest_1.a <> hjtest_2.b; - a1 | a2 | t1 | t2 -------+----+----------+---------- - text | t | hjtest_1 | hjtest_2 -(1 row) - -ROLLBACK; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin_bloom.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin_bloom.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin_bloom.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin_bloom.out 2021-10-30 22:04:15 -0600 @@ -1,428 +1,2 @@ -CREATE TABLE brintest_bloom (byteacol bytea, - charcol "char", - namecol name, - int8col bigint, - int2col smallint, - int4col integer, - textcol text, - oidcol oid, - float4col real, - float8col double precision, - macaddrcol macaddr, - inetcol inet, - cidrcol cidr, - bpcharcol character, - datecol date, - timecol time without time zone, - timestampcol timestamp without time zone, - timestamptzcol timestamp with time zone, - intervalcol interval, - timetzcol time with time zone, - numericcol numeric, - uuidcol uuid, - lsncol pg_lsn -) WITH (fillfactor=10); -INSERT INTO brintest_bloom SELECT - repeat(stringu1, 8)::bytea, - substr(stringu1, 1, 1)::"char", - stringu1::name, 142857 * tenthous, - thousand, - twothousand, - repeat(stringu1, 8), - unique1::oid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - inet '10.2.3.4/24' + tenthous, - cidr '10.2.3/24' + tenthous, - substr(stringu1, 1, 1)::bpchar, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20+02' + hundred * interval '15 seconds', - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 ORDER BY unique2 LIMIT 100; --- throw in some NULL's and different values -INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT - inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, - cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous -FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; --- test bloom specific index options --- ndistinct must be >= -1.0 -CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( - byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) -); -ERROR: value -1.1 out of bounds for option "n_distinct_per_range" -DETAIL: Valid values are between "-1.000000" and "2147483647.000000". --- false_positive_rate must be between 0.0001 and 0.25 -CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( - byteacol bytea_bloom_ops(false_positive_rate = 0.00009) -); -ERROR: value 0.00009 out of bounds for option "false_positive_rate" -DETAIL: Valid values are between "0.000100" and "0.250000". -CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( - byteacol bytea_bloom_ops(false_positive_rate = 0.26) -); -ERROR: value 0.26 out of bounds for option "false_positive_rate" -DETAIL: Valid values are between "0.000100" and "0.250000". -CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( - byteacol bytea_bloom_ops, - charcol char_bloom_ops, - namecol name_bloom_ops, - int8col int8_bloom_ops, - int2col int2_bloom_ops, - int4col int4_bloom_ops, - textcol text_bloom_ops, - oidcol oid_bloom_ops, - float4col float4_bloom_ops, - float8col float8_bloom_ops, - macaddrcol macaddr_bloom_ops, - inetcol inet_bloom_ops, - cidrcol inet_bloom_ops, - bpcharcol bpchar_bloom_ops, - datecol date_bloom_ops, - timecol time_bloom_ops, - timestampcol timestamp_bloom_ops, - timestamptzcol timestamptz_bloom_ops, - intervalcol interval_bloom_ops, - timetzcol timetz_bloom_ops, - numericcol numeric_bloom_ops, - uuidcol uuid_bloom_ops, - lsncol pg_lsn_bloom_ops -) with (pages_per_range = 1); -CREATE TABLE brinopers_bloom (colname name, typ text, - op text[], value text[], matches int[], - check (cardinality(op) = cardinality(value)), - check (cardinality(op) = cardinality(matches))); -INSERT INTO brinopers_bloom VALUES - ('byteacol', 'bytea', - '{=}', - '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', - '{1}'), - ('charcol', '"char"', - '{=}', - '{M}', - '{6}'), - ('namecol', 'name', - '{=}', - '{MAAAAA}', - '{2}'), - ('int2col', 'int2', - '{=}', - '{800}', - '{1}'), - ('int4col', 'int4', - '{=}', - '{800}', - '{1}'), - ('int8col', 'int8', - '{=}', - '{1257141600}', - '{1}'), - ('textcol', 'text', - '{=}', - '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', - '{1}'), - ('oidcol', 'oid', - '{=}', - '{8800}', - '{1}'), - ('float4col', 'float4', - '{=}', - '{1}', - '{4}'), - ('float8col', 'float8', - '{=}', - '{0}', - '{1}'), - ('macaddrcol', 'macaddr', - '{=}', - '{2c:00:2d:00:16:00}', - '{2}'), - ('inetcol', 'inet', - '{=}', - '{10.2.14.231/24}', - '{1}'), - ('inetcol', 'cidr', - '{=}', - '{fe80::6e40:8ff:fea9:8c46}', - '{1}'), - ('cidrcol', 'inet', - '{=}', - '{10.2.14/24}', - '{2}'), - ('cidrcol', 'inet', - '{=}', - '{fe80::6e40:8ff:fea9:8c46}', - '{1}'), - ('cidrcol', 'cidr', - '{=}', - '{10.2.14/24}', - '{2}'), - ('cidrcol', 'cidr', - '{=}', - '{fe80::6e40:8ff:fea9:8c46}', - '{1}'), - ('bpcharcol', 'bpchar', - '{=}', - '{W}', - '{6}'), - ('datecol', 'date', - '{=}', - '{2009-12-01}', - '{1}'), - ('timecol', 'time', - '{=}', - '{02:28:57}', - '{1}'), - ('timestampcol', 'timestamp', - '{=}', - '{1964-03-24 19:26:45}', - '{1}'), - ('timestamptzcol', 'timestamptz', - '{=}', - '{1972-10-19 09:00:00-07}', - '{1}'), - ('intervalcol', 'interval', - '{=}', - '{1 mons 13 days 12:24}', - '{1}'), - ('timetzcol', 'timetz', - '{=}', - '{01:35:50+02}', - '{2}'), - ('numericcol', 'numeric', - '{=}', - '{2268164.347826086956521739130434782609}', - '{1}'), - ('uuidcol', 'uuid', - '{=}', - '{52225222-5222-5222-5222-522252225222}', - '{1}'), - ('lsncol', 'pg_lsn', - '{=, IS, IS NOT}', - '{44/455222, NULL, NULL}', - '{1, 25, 100}'); -DO $x$ -DECLARE - r record; - r2 record; - cond text; - idx_ctids tid[]; - ss_ctids tid[]; - count int; - plan_ok bool; - plan_line text; -BEGIN - FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP - - -- prepare the condition - IF r.value IS NULL THEN - cond := format('%I %s %L', r.colname, r.oper, r.value); - ELSE - cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); - END IF; - - -- run the query using the brin index - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get bitmap indexscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) - INTO idx_ctids; - - -- run the query using a seqscan - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get seqscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) - INTO ss_ctids; - - -- make sure both return the same results - count := array_length(idx_ctids, 1); - - IF NOT (count = array_length(ss_ctids, 1) AND - idx_ctids @> ss_ctids AND - idx_ctids <@ ss_ctids) THEN - -- report the results of each scan to make the differences obvious - RAISE WARNING 'something not right in %: count %', r, count; - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP - RAISE NOTICE 'seqscan: %', r2; - END LOOP; - - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP - RAISE NOTICE 'bitmapscan: %', r2; - END LOOP; - END IF; - - -- make sure we found expected number of matches - IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; - END LOOP; -END; -$x$; -RESET enable_seqscan; -RESET enable_bitmapscan; -INSERT INTO brintest_bloom SELECT - repeat(stringu1, 42)::bytea, - substr(stringu1, 1, 1)::"char", - stringu1::name, 142857 * tenthous, - thousand, - twothousand, - repeat(stringu1, 42), - unique1::oid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - inet '10.2.3.4' + tenthous, - cidr '10.2.3/24' + tenthous, - substr(stringu1, 1, 1)::bpchar, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20' + hundred * interval '15 seconds', - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; -SELECT brin_desummarize_range('brinidx_bloom', 0); - brin_desummarize_range ------------------------- - -(1 row) - -VACUUM brintest_bloom; -- force a summarization cycle in brinidx -UPDATE brintest_bloom SET int8col = int8col * int4col; -UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; --- Tests for brin_summarize_new_values -SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index -ERROR: "brintest_bloom" is not an index -SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index -ERROR: "tenk1_unique1" is not a BRIN index -SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected - brin_summarize_new_values ---------------------------- - 0 -(1 row) - --- Tests for brin_desummarize_range -SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range -ERROR: block number out of range: -1 -SELECT brin_desummarize_range('brinidx_bloom', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx_bloom', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx_bloom', 100000000); - brin_desummarize_range ------------------------- - -(1 row) - --- Test brin_summarize_range -CREATE TABLE brin_summarize_bloom ( - value int -) WITH (fillfactor=10, autovacuum_enabled=false); -CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); --- Fill a few pages -DO $$ -DECLARE curtid tid; -BEGIN - LOOP - INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; - EXIT WHEN curtid > tid '(2, 0)'; - END LOOP; -END; -$$; --- summarize one range -SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); - brin_summarize_range ----------------------- - 0 -(1 row) - --- nothing: already summarized -SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); - brin_summarize_range ----------------------- - 0 -(1 row) - --- summarize one range -SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); - brin_summarize_range ----------------------- - 1 -(1 row) - --- nothing: page doesn't exist in table -SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); - brin_summarize_range ----------------------- - 0 -(1 row) - --- invalid block number values -SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); -ERROR: block number out of range: -1 -SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); -ERROR: block number out of range: 4294967296 --- test brin cost estimates behave sanely based on correlation of values -CREATE TABLE brin_test_bloom (a INT, b INT); -INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); -CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); -CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); -VACUUM ANALYZE brin_test_bloom; --- Ensure brin index is used when columns are perfectly correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; - QUERY PLAN --------------------------------------------------- - Bitmap Heap Scan on brin_test_bloom - Recheck Cond: (a = 1) - -> Bitmap Index Scan on brin_test_bloom_a_idx - Index Cond: (a = 1) -(4 rows) - --- Ensure brin index is not used when values are not correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; - QUERY PLAN ------------------------------ - Seq Scan on brin_test_bloom - Filter: (b = 1) -(2 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin_multi.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin_multi.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/brin_multi.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/brin_multi.out 2021-10-30 22:04:15 -0600 @@ -1,459 +1,2 @@ -CREATE TABLE brintest_multi ( - int8col bigint, - int2col smallint, - int4col integer, - oidcol oid, - tidcol tid, - float4col real, - float8col double precision, - macaddrcol macaddr, - macaddr8col macaddr8, - inetcol inet, - cidrcol cidr, - datecol date, - timecol time without time zone, - timestampcol timestamp without time zone, - timestamptzcol timestamp with time zone, - intervalcol interval, - timetzcol time with time zone, - numericcol numeric, - uuidcol uuid, - lsncol pg_lsn -) WITH (fillfactor=10); -INSERT INTO brintest_multi SELECT - 142857 * tenthous, - thousand, - twothousand, - unique1::oid, - format('(%s,%s)', tenthous, twenty)::tid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - substr(md5(unique1::text), 1, 16)::macaddr8, - inet '10.2.3.4/24' + tenthous, - cidr '10.2.3/24' + tenthous, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20+02' + hundred * interval '15 seconds', - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 ORDER BY unique2 LIMIT 100; --- throw in some NULL's and different values -INSERT INTO brintest_multi (inetcol, cidrcol) SELECT - inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, - cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous -FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; --- test minmax-multi specific index options --- number of values must be >= 16 -CREATE INDEX brinidx_multi ON brintest_multi USING brin ( - int8col int8_minmax_multi_ops(values_per_range = 7) -); -ERROR: value 7 out of bounds for option "values_per_range" -DETAIL: Valid values are between "8" and "256". --- number of values must be <= 256 -CREATE INDEX brinidx_multi ON brintest_multi USING brin ( - int8col int8_minmax_multi_ops(values_per_range = 257) -); -ERROR: value 257 out of bounds for option "values_per_range" -DETAIL: Valid values are between "8" and "256". --- first create an index with a single page range, to force compaction --- due to exceeding the number of values per summary -CREATE INDEX brinidx_multi ON brintest_multi USING brin ( - int8col int8_minmax_multi_ops, - int2col int2_minmax_multi_ops, - int4col int4_minmax_multi_ops, - oidcol oid_minmax_multi_ops, - tidcol tid_minmax_multi_ops, - float4col float4_minmax_multi_ops, - float8col float8_minmax_multi_ops, - macaddrcol macaddr_minmax_multi_ops, - macaddr8col macaddr8_minmax_multi_ops, - inetcol inet_minmax_multi_ops, - cidrcol inet_minmax_multi_ops, - datecol date_minmax_multi_ops, - timecol time_minmax_multi_ops, - timestampcol timestamp_minmax_multi_ops, - timestamptzcol timestamptz_minmax_multi_ops, - intervalcol interval_minmax_multi_ops, - timetzcol timetz_minmax_multi_ops, - numericcol numeric_minmax_multi_ops, - uuidcol uuid_minmax_multi_ops, - lsncol pg_lsn_minmax_multi_ops -); -DROP INDEX brinidx_multi; -CREATE INDEX brinidx_multi ON brintest_multi USING brin ( - int8col int8_minmax_multi_ops, - int2col int2_minmax_multi_ops, - int4col int4_minmax_multi_ops, - oidcol oid_minmax_multi_ops, - tidcol tid_minmax_multi_ops, - float4col float4_minmax_multi_ops, - float8col float8_minmax_multi_ops, - macaddrcol macaddr_minmax_multi_ops, - macaddr8col macaddr8_minmax_multi_ops, - inetcol inet_minmax_multi_ops, - cidrcol inet_minmax_multi_ops, - datecol date_minmax_multi_ops, - timecol time_minmax_multi_ops, - timestampcol timestamp_minmax_multi_ops, - timestamptzcol timestamptz_minmax_multi_ops, - intervalcol interval_minmax_multi_ops, - timetzcol timetz_minmax_multi_ops, - numericcol numeric_minmax_multi_ops, - uuidcol uuid_minmax_multi_ops, - lsncol pg_lsn_minmax_multi_ops -) with (pages_per_range = 1); -CREATE TABLE brinopers_multi (colname name, typ text, - op text[], value text[], matches int[], - check (cardinality(op) = cardinality(value)), - check (cardinality(op) = cardinality(matches))); -INSERT INTO brinopers_multi VALUES - ('int2col', 'int2', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 999}', - '{100, 100, 1, 100, 100}'), - ('int2col', 'int4', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int2col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 800, 999, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int2', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int4', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1999}', - '{100, 100, 1, 100, 100}'), - ('int4col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 800, 1999, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('int8col', 'int2', - '{>, >=}', - '{0, 0}', - '{100, 100}'), - ('int8col', 'int4', - '{>, >=}', - '{0, 0}', - '{100, 100}'), - ('int8col', 'int8', - '{>, >=, =, <=, <}', - '{0, 0, 1257141600, 1428427143, 1428427143}', - '{100, 100, 1, 100, 100}'), - ('oidcol', 'oid', - '{>, >=, =, <=, <}', - '{0, 0, 8800, 9999, 9999}', - '{100, 100, 1, 100, 100}'), - ('tidcol', 'tid', - '{>, >=, =, <=, <}', - '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', - '{100, 100, 1, 100, 100}'), - ('float4col', 'float4', - '{>, >=, =, <=, <}', - '{0.0103093, 0.0103093, 1, 1, 1}', - '{100, 100, 4, 100, 96}'), - ('float4col', 'float8', - '{>, >=, =, <=, <}', - '{0.0103093, 0.0103093, 1, 1, 1}', - '{100, 100, 4, 100, 96}'), - ('float8col', 'float4', - '{>, >=, =, <=, <}', - '{0, 0, 0, 1.98, 1.98}', - '{99, 100, 1, 100, 100}'), - ('float8col', 'float8', - '{>, >=, =, <=, <}', - '{0, 0, 0, 1.98, 1.98}', - '{99, 100, 1, 100, 100}'), - ('macaddrcol', 'macaddr', - '{>, >=, =, <=, <}', - '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', - '{99, 100, 2, 100, 100}'), - ('macaddr8col', 'macaddr8', - '{>, >=, =, <=, <}', - '{b1:d1:0e:7b:af:a4:42:12, d9:35:91:bd:f7:86:0e:1e, 72:8f:20:6c:2a:01:bf:57, 23:e8:46:63:86:07:ad:cb, 13:16:8e:6a:2e:6c:84:b4}', - '{33, 15, 1, 13, 6}'), - ('inetcol', 'inet', - '{=, <, <=, >, >=}', - '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', - '{1, 100, 100, 125, 125}'), - ('inetcol', 'cidr', - '{<, <=, >, >=}', - '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', - '{100, 100, 125, 125}'), - ('cidrcol', 'inet', - '{=, <, <=, >, >=}', - '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', - '{2, 100, 100, 125, 125}'), - ('cidrcol', 'cidr', - '{=, <, <=, >, >=}', - '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', - '{2, 100, 100, 125, 125}'), - ('datecol', 'date', - '{>, >=, =, <=, <}', - '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', - '{100, 100, 1, 100, 100}'), - ('timecol', 'time', - '{>, >=, =, <=, <}', - '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', - '{100, 100, 1, 100, 100}'), - ('timestampcol', 'timestamp', - '{>, >=, =, <=, <}', - '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', - '{100, 100, 1, 100, 100}'), - ('timestampcol', 'timestamptz', - '{>, >=, =, <=, <}', - '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', - '{100, 100, 1, 100, 100}'), - ('timestamptzcol', 'timestamptz', - '{>, >=, =, <=, <}', - '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', - '{100, 100, 1, 100, 100}'), - ('intervalcol', 'interval', - '{>, >=, =, <=, <}', - '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', - '{100, 100, 1, 100, 100}'), - ('timetzcol', 'timetz', - '{>, >=, =, <=, <}', - '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', - '{99, 100, 2, 100, 100}'), - ('numericcol', 'numeric', - '{>, >=, =, <=, <}', - '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', - '{100, 100, 1, 100, 100}'), - ('uuidcol', 'uuid', - '{>, >=, =, <=, <}', - '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', - '{100, 100, 1, 100, 100}'), - ('lsncol', 'pg_lsn', - '{>, >=, =, <=, <, IS, IS NOT}', - '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', - '{100, 100, 1, 100, 100, 25, 100}'); -DO $x$ -DECLARE - r record; - r2 record; - cond text; - idx_ctids tid[]; - ss_ctids tid[]; - count int; - plan_ok bool; - plan_line text; -BEGIN - FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP - - -- prepare the condition - IF r.value IS NULL THEN - cond := format('%I %s %L', r.colname, r.oper, r.value); - ELSE - cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); - END IF; - - -- run the query using the brin index - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get bitmap indexscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) - INTO idx_ctids; - - -- run the query using a seqscan - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - - plan_ok := false; - FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP - IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN - plan_ok := true; - END IF; - END LOOP; - IF NOT plan_ok THEN - RAISE WARNING 'did not get seqscan plan for %', r; - END IF; - - EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) - INTO ss_ctids; - - -- make sure both return the same results - count := array_length(idx_ctids, 1); - - IF NOT (count = array_length(ss_ctids, 1) AND - idx_ctids @> ss_ctids AND - idx_ctids <@ ss_ctids) THEN - -- report the results of each scan to make the differences obvious - RAISE WARNING 'something not right in %: count %', r, count; - SET enable_seqscan = 1; - SET enable_bitmapscan = 0; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP - RAISE NOTICE 'seqscan: %', r2; - END LOOP; - - SET enable_seqscan = 0; - SET enable_bitmapscan = 1; - FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP - RAISE NOTICE 'bitmapscan: %', r2; - END LOOP; - END IF; - - -- make sure we found expected number of matches - IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; - END LOOP; -END; -$x$; -RESET enable_seqscan; -RESET enable_bitmapscan; -INSERT INTO brintest_multi SELECT - 142857 * tenthous, - thousand, - twothousand, - unique1::oid, - format('(%s,%s)', tenthous, twenty)::tid, - (four + 1.0)/(hundred+1), - odd::float8 / (tenthous + 1), - format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, - substr(md5(unique1::text), 1, 16)::macaddr8, - inet '10.2.3.4' + tenthous, - cidr '10.2.3/24' + tenthous, - date '1995-08-15' + tenthous, - time '01:20:30' + thousand * interval '18.5 second', - timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', - timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', - justify_days(justify_hours(tenthous * interval '12 minutes')), - timetz '01:30:20' + hundred * interval '15 seconds', - tenthous::numeric(36,30) * fivethous * even / (hundred + 1), - format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; -SELECT brin_desummarize_range('brinidx_multi', 0); - brin_desummarize_range ------------------------- - -(1 row) - -VACUUM brintest_multi; -- force a summarization cycle in brinidx -UPDATE brintest_multi SET int8col = int8col * int4col; --- Tests for brin_summarize_new_values -SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index -ERROR: "brintest_multi" is not an index -SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index -ERROR: "tenk1_unique1" is not a BRIN index -SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected - brin_summarize_new_values ---------------------------- - 0 -(1 row) - --- Tests for brin_desummarize_range -SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range -ERROR: block number out of range: -1 -SELECT brin_desummarize_range('brinidx_multi', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx_multi', 0); - brin_desummarize_range ------------------------- - -(1 row) - -SELECT brin_desummarize_range('brinidx_multi', 100000000); - brin_desummarize_range ------------------------- - -(1 row) - --- test building an index with many values, to force compaction of the buffer -CREATE TABLE brin_large_range (a int4); -INSERT INTO brin_large_range SELECT i FROM generate_series(1,10000) s(i); -CREATE INDEX brin_large_range_idx ON brin_large_range USING brin (a int4_minmax_multi_ops); -DROP TABLE brin_large_range; --- Test brin_summarize_range -CREATE TABLE brin_summarize_multi ( - value int -) WITH (fillfactor=10, autovacuum_enabled=false); -CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); --- Fill a few pages -DO $$ -DECLARE curtid tid; -BEGIN - LOOP - INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; - EXIT WHEN curtid > tid '(2, 0)'; - END LOOP; -END; -$$; --- summarize one range -SELECT brin_summarize_range('brin_summarize_multi_idx', 0); - brin_summarize_range ----------------------- - 0 -(1 row) - --- nothing: already summarized -SELECT brin_summarize_range('brin_summarize_multi_idx', 1); - brin_summarize_range ----------------------- - 0 -(1 row) - --- summarize one range -SELECT brin_summarize_range('brin_summarize_multi_idx', 2); - brin_summarize_range ----------------------- - 1 -(1 row) - --- nothing: page doesn't exist in table -SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); - brin_summarize_range ----------------------- - 0 -(1 row) - --- invalid block number values -SELECT brin_summarize_range('brin_summarize_multi_idx', -1); -ERROR: block number out of range: -1 -SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); -ERROR: block number out of range: 4294967296 --- test brin cost estimates behave sanely based on correlation of values -CREATE TABLE brin_test_multi (a INT, b INT); -INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); -CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); -CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); -VACUUM ANALYZE brin_test_multi; --- Ensure brin index is used when columns are perfectly correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; - QUERY PLAN --------------------------------------------------- - Bitmap Heap Scan on brin_test_multi - Recheck Cond: (a = 1) - -> Bitmap Index Scan on brin_test_multi_a_idx - Index Cond: (a = 1) -(4 rows) - --- Ensure brin index is not used when values are not correlated -EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; - QUERY PLAN ------------------------------ - Seq Scan on brin_test_multi - Filter: (b = 1) -(2 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/create_table_like.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/create_table_like.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/create_table_like.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/create_table_like.out 2021-10-30 22:04:15 -0600 @@ -1,520 +1,2 @@ -/* Test inheritance of structure (LIKE) */ -CREATE TABLE inhx (xx text DEFAULT 'text'); -/* - * Test double inheritance - * - * Ensure that defaults are NOT included unless - * INCLUDING DEFAULTS is specified - */ -CREATE TABLE ctla (aa TEXT); -CREATE TABLE ctlb (bb TEXT) INHERITS (ctla); -CREATE TABLE foo (LIKE nonexistent); -ERROR: relation "nonexistent" does not exist -LINE 1: CREATE TABLE foo (LIKE nonexistent); - ^ -CREATE TABLE inhe (ee text, LIKE inhx) inherits (ctlb); -INSERT INTO inhe VALUES ('ee-col1', 'ee-col2', DEFAULT, 'ee-col4'); -SELECT * FROM inhe; /* Columns aa, bb, xx value NULL, ee */ - aa | bb | ee | xx ----------+---------+----+--------- - ee-col1 | ee-col2 | | ee-col4 -(1 row) - -SELECT * FROM inhx; /* Empty set since LIKE inherits structure only */ - xx ----- -(0 rows) - -SELECT * FROM ctlb; /* Has ee entry */ - aa | bb ----------+--------- - ee-col1 | ee-col2 -(1 row) - -SELECT * FROM ctla; /* Has ee entry */ - aa ---------- - ee-col1 -(1 row) - -CREATE TABLE inhf (LIKE inhx, LIKE inhx); /* Throw error */ -ERROR: column "xx" specified more than once -CREATE TABLE inhf (LIKE inhx INCLUDING DEFAULTS INCLUDING CONSTRAINTS); -INSERT INTO inhf DEFAULT VALUES; -SELECT * FROM inhf; /* Single entry with value 'text' */ - xx ------- - text -(1 row) - -ALTER TABLE inhx add constraint foo CHECK (xx = 'text'); -ALTER TABLE inhx ADD PRIMARY KEY (xx); -CREATE TABLE inhg (LIKE inhx); /* Doesn't copy constraint */ -INSERT INTO inhg VALUES ('foo'); -DROP TABLE inhg; -CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */ -INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */ -INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */ -INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */ -ERROR: new row for relation "inhg" violates check constraint "foo" -DETAIL: Failing row contains (x, foo, y). -SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */ - x | xx | y ----+------+--- - x | text | y - x | text | y -(2 rows) - -DROP TABLE inhg; -CREATE TABLE test_like_id_1 (a bigint GENERATED ALWAYS AS IDENTITY, b text); -\d test_like_id_1 - Table "public.test_like_id_1" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+------------------------------ - a | bigint | | not null | generated always as identity - b | text | | | - -INSERT INTO test_like_id_1 (b) VALUES ('b1'); -SELECT * FROM test_like_id_1; - a | b ----+---- - 1 | b1 -(1 row) - -CREATE TABLE test_like_id_2 (LIKE test_like_id_1); -\d test_like_id_2 - Table "public.test_like_id_2" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+--------- - a | bigint | | not null | - b | text | | | - -INSERT INTO test_like_id_2 (b) VALUES ('b2'); -ERROR: null value in column "a" of relation "test_like_id_2" violates not-null constraint -DETAIL: Failing row contains (null, b2). -SELECT * FROM test_like_id_2; -- identity was not copied - a | b ----+--- -(0 rows) - -CREATE TABLE test_like_id_3 (LIKE test_like_id_1 INCLUDING IDENTITY); -\d test_like_id_3 - Table "public.test_like_id_3" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+------------------------------ - a | bigint | | not null | generated always as identity - b | text | | | - -INSERT INTO test_like_id_3 (b) VALUES ('b3'); -SELECT * FROM test_like_id_3; -- identity was copied and applied - a | b ----+---- - 1 | b3 -(1 row) - -DROP TABLE test_like_id_1, test_like_id_2, test_like_id_3; -CREATE TABLE test_like_gen_1 (a int, b int GENERATED ALWAYS AS (a * 2) STORED); -\d test_like_gen_1 - Table "public.test_like_gen_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored - -INSERT INTO test_like_gen_1 (a) VALUES (1); -SELECT * FROM test_like_gen_1; - a | b ----+--- - 1 | 2 -(1 row) - -CREATE TABLE test_like_gen_2 (LIKE test_like_gen_1); -\d test_like_gen_2 - Table "public.test_like_gen_2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - -INSERT INTO test_like_gen_2 (a) VALUES (1); -SELECT * FROM test_like_gen_2; - a | b ----+--- - 1 | -(1 row) - -CREATE TABLE test_like_gen_3 (LIKE test_like_gen_1 INCLUDING GENERATED); -\d test_like_gen_3 - Table "public.test_like_gen_3" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - a | integer | | | - b | integer | | | generated always as (a * 2) stored - -INSERT INTO test_like_gen_3 (a) VALUES (1); -SELECT * FROM test_like_gen_3; - a | b ----+--- - 1 | 2 -(1 row) - -DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3; --- also test generated column with a "forward" reference (bug #16342) -CREATE TABLE test_like_4 (b int DEFAULT 42, - c int GENERATED ALWAYS AS (a * 2) STORED, - a int CHECK (a > 0)); -\d test_like_4 - Table "public.test_like_4" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - b | integer | | | 42 - c | integer | | | generated always as (a * 2) stored - a | integer | | | -Check constraints: - "test_like_4_a_check" CHECK (a > 0) - -CREATE TABLE test_like_4a (LIKE test_like_4); -CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS); -CREATE TABLE test_like_4c (LIKE test_like_4 INCLUDING GENERATED); -CREATE TABLE test_like_4d (LIKE test_like_4 INCLUDING DEFAULTS INCLUDING GENERATED); -\d test_like_4a - Table "public.test_like_4a" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | integer | | | - c | integer | | | - a | integer | | | - -INSERT INTO test_like_4a (a) VALUES(11); -SELECT a, b, c FROM test_like_4a; - a | b | c -----+---+--- - 11 | | -(1 row) - -\d test_like_4b - Table "public.test_like_4b" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | integer | | | 42 - c | integer | | | - a | integer | | | - -INSERT INTO test_like_4b (a) VALUES(11); -SELECT a, b, c FROM test_like_4b; - a | b | c -----+----+--- - 11 | 42 | -(1 row) - -\d test_like_4c - Table "public.test_like_4c" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - b | integer | | | - c | integer | | | generated always as (a * 2) stored - a | integer | | | - -INSERT INTO test_like_4c (a) VALUES(11); -SELECT a, b, c FROM test_like_4c; - a | b | c -----+---+---- - 11 | | 22 -(1 row) - -\d test_like_4d - Table "public.test_like_4d" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - b | integer | | | 42 - c | integer | | | generated always as (a * 2) stored - a | integer | | | - -INSERT INTO test_like_4d (a) VALUES(11); -SELECT a, b, c FROM test_like_4d; - a | b | c -----+----+---- - 11 | 42 | 22 -(1 row) - --- Test renumbering of Vars when combining LIKE with inheritance -CREATE TABLE test_like_5 (x point, y point, z point); -CREATE TABLE test_like_5x (p int CHECK (p > 0), - q int GENERATED ALWAYS AS (p * 2) STORED); -CREATE TABLE test_like_5c (LIKE test_like_4 INCLUDING ALL) - INHERITS (test_like_5, test_like_5x); -\d test_like_5c - Table "public.test_like_5c" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+------------------------------------ - x | point | | | - y | point | | | - z | point | | | - p | integer | | | - q | integer | | | generated always as (p * 2) stored - b | integer | | | 42 - c | integer | | | generated always as (a * 2) stored - a | integer | | | -Check constraints: - "test_like_4_a_check" CHECK (a > 0) - "test_like_5x_p_check" CHECK (p > 0) -Inherits: test_like_5, - test_like_5x - -DROP TABLE test_like_4, test_like_4a, test_like_4b, test_like_4c, test_like_4d; -DROP TABLE test_like_5, test_like_5x, test_like_5c; -CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */ -INSERT INTO inhg VALUES (5, 10); -INSERT INTO inhg VALUES (20, 10); -- should fail -ERROR: duplicate key value violates unique constraint "inhg_pkey" -DETAIL: Key (xx)=(10) already exists. -DROP TABLE inhg; -/* Multiple primary keys creation should fail */ -CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */ -ERROR: multiple primary keys for table "inhg" are not allowed -CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE); -CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test'; -/* Ok to create multiple unique indexes */ -CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES); -INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10); -INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15); -INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail -ERROR: duplicate key value violates unique constraint "inhg_x_key" -DETAIL: Key (x)=(15) already exists. -DROP TABLE inhg; -DROP TABLE inhz; -/* Use primary key imported by LIKE for self-referential FK constraint */ -CREATE TABLE inhz (x text REFERENCES inhz, LIKE inhx INCLUDING INDEXES); -\d inhz - Table "public.inhz" - Column | Type | Collation | Nullable | Default ---------+------+-----------+----------+--------- - x | text | | | - xx | text | | not null | -Indexes: - "inhz_pkey" PRIMARY KEY, btree (xx) -Foreign-key constraints: - "inhz_x_fkey" FOREIGN KEY (x) REFERENCES inhz(xx) -Referenced by: - TABLE "inhz" CONSTRAINT "inhz_x_fkey" FOREIGN KEY (x) REFERENCES inhz(xx) - -DROP TABLE inhz; --- including storage and comments -CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text); -CREATE INDEX ctlt1_b_key ON ctlt1 (b); -CREATE INDEX ctlt1_fnidx ON ctlt1 ((a || b)); -CREATE STATISTICS ctlt1_a_b_stat ON a,b FROM ctlt1; -CREATE STATISTICS ctlt1_expr_stat ON (a || b) FROM ctlt1; -COMMENT ON STATISTICS ctlt1_a_b_stat IS 'ab stats'; -COMMENT ON STATISTICS ctlt1_expr_stat IS 'ab expr stats'; -COMMENT ON COLUMN ctlt1.a IS 'A'; -COMMENT ON COLUMN ctlt1.b IS 'B'; -COMMENT ON CONSTRAINT ctlt1_a_check ON ctlt1 IS 't1_a_check'; -COMMENT ON INDEX ctlt1_pkey IS 'index pkey'; -COMMENT ON INDEX ctlt1_b_key IS 'index b_key'; -ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN; -CREATE TABLE ctlt2 (c text); -ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL; -COMMENT ON COLUMN ctlt2.c IS 'C'; -CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7)); -ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL; -ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN; -CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c)); -COMMENT ON COLUMN ctlt3.a IS 'A3'; -COMMENT ON COLUMN ctlt3.c IS 'C'; -COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check'; -CREATE TABLE ctlt4 (a text, c text); -ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL; -CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE); -\d+ ctlt12_storage - Table "public.ctlt12_storage" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | - b | text | | | | extended | | - c | text | | | | external | | - -CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS); -\d+ ctlt12_comments - Table "public.ctlt12_comments" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | extended | | A - b | text | | | | extended | | B - c | text | | | | extended | | C - -CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1); -NOTICE: merging column "a" with inherited definition -NOTICE: merging column "b" with inherited definition -NOTICE: merging constraint "ctlt1_a_check" with inherited definition -\d+ ctlt1_inh - Table "public.ctlt1_inh" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | A - b | text | | | | extended | | B -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) -Inherits: ctlt1 - -SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass; - description -------------- - t1_a_check -(1 row) - -CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3); -NOTICE: merging multiple inherited definitions of column "a" -\d+ ctlt13_inh - Table "public.ctlt13_inh" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | - b | text | | | | extended | | - c | text | | | | external | | -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) - "ctlt3_a_check" CHECK (length(a) < 5) - "ctlt3_c_check" CHECK (length(c) < 7) -Inherits: ctlt1, - ctlt3 - -CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1); -NOTICE: merging column "a" with inherited definition -\d+ ctlt13_like - Table "public.ctlt13_like" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | A3 - b | text | | | | extended | | - c | text | | | | external | | C -Indexes: - "ctlt13_like_expr_idx" btree ((a || c)) -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) - "ctlt3_a_check" CHECK (length(a) < 5) - "ctlt3_c_check" CHECK (length(c) < 7) -Inherits: ctlt1 - -SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass; - description -------------- - t3_a_check -(1 row) - -CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL); -\d+ ctlt_all - Table "public.ctlt_all" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | A - b | text | | | | extended | | B -Indexes: - "ctlt_all_pkey" PRIMARY KEY, btree (a) - "ctlt_all_b_idx" btree (b) - "ctlt_all_expr_idx" btree ((a || b)) -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) -Statistics objects: - "public.ctlt_all_a_b_stat" ON a, b FROM ctlt_all - "public.ctlt_all_expr_stat" ON (a || b) FROM ctlt_all - -SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 'ctlt_all'::regclass ORDER BY c.relname, objsubid; - relname | objsubid | description -----------------+----------+------------- - ctlt_all_b_idx | 0 | index b_key - ctlt_all_pkey | 0 | index pkey -(2 rows) - -SELECT s.stxname, objsubid, description FROM pg_description, pg_statistic_ext s WHERE classoid = 'pg_statistic_ext'::regclass AND objoid = s.oid AND s.stxrelid = 'ctlt_all'::regclass ORDER BY s.stxname, objsubid; - stxname | objsubid | description ---------------------+----------+--------------- - ctlt_all_a_b_stat | 0 | ab stats - ctlt_all_expr_stat | 0 | ab expr stats -(2 rows) - -CREATE TABLE inh_error1 () INHERITS (ctlt1, ctlt4); -NOTICE: merging multiple inherited definitions of column "a" -ERROR: inherited column "a" has a storage parameter conflict -DETAIL: MAIN versus EXTENDED -CREATE TABLE inh_error2 (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); -NOTICE: merging column "a" with inherited definition -ERROR: column "a" has a storage parameter conflict -DETAIL: MAIN versus EXTENDED --- Check that LIKE isn't confused by a system catalog of the same name -CREATE TABLE pg_attrdef (LIKE ctlt1 INCLUDING ALL); -\d+ public.pg_attrdef - Table "public.pg_attrdef" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | A - b | text | | | | extended | | B -Indexes: - "pg_attrdef_pkey" PRIMARY KEY, btree (a) - "pg_attrdef_b_idx" btree (b) - "pg_attrdef_expr_idx" btree ((a || b)) -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) -Statistics objects: - "public.pg_attrdef_a_b_stat" ON a, b FROM public.pg_attrdef - "public.pg_attrdef_expr_stat" ON (a || b) FROM public.pg_attrdef - -DROP TABLE public.pg_attrdef; --- Check that LIKE isn't confused when new table masks the old, either -BEGIN; -CREATE SCHEMA ctl_schema; -SET LOCAL search_path = ctl_schema, public; -CREATE TABLE ctlt1 (LIKE ctlt1 INCLUDING ALL); -\d+ ctlt1 - Table "ctl_schema.ctlt1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------+-----------+----------+---------+----------+--------------+------------- - a | text | | not null | | main | | A - b | text | | | | extended | | B -Indexes: - "ctlt1_pkey" PRIMARY KEY, btree (a) - "ctlt1_b_idx" btree (b) - "ctlt1_expr_idx" btree ((a || b)) -Check constraints: - "ctlt1_a_check" CHECK (length(a) > 2) -Statistics objects: - "ctl_schema.ctlt1_a_b_stat" ON a, b FROM ctlt1 - "ctl_schema.ctlt1_expr_stat" ON (a || b) FROM ctlt1 - -ROLLBACK; -DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE; -NOTICE: drop cascades to table inhe --- LIKE must respect NO INHERIT property of constraints -CREATE TABLE noinh_con_copy (a int CHECK (a > 0) NO INHERIT); -CREATE TABLE noinh_con_copy1 (LIKE noinh_con_copy INCLUDING CONSTRAINTS); -\d noinh_con_copy1 - Table "public.noinh_con_copy1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Check constraints: - "noinh_con_copy_a_check" CHECK (a > 0) NO INHERIT - --- fail, as partitioned tables don't allow NO INHERIT constraints -CREATE TABLE noinh_con_copy1_parted (LIKE noinh_con_copy INCLUDING ALL) - PARTITION BY LIST (a); -ERROR: cannot add NO INHERIT constraint to partitioned table "noinh_con_copy1_parted" -DROP TABLE noinh_con_copy, noinh_con_copy1; -/* LIKE with other relation kinds */ -CREATE TABLE ctlt4 (a int, b text); -CREATE SEQUENCE ctlseq1; -CREATE TABLE ctlt10 (LIKE ctlseq1); -- fail -ERROR: relation "ctlseq1" is invalid in LIKE clause -LINE 1: CREATE TABLE ctlt10 (LIKE ctlseq1); - ^ -DETAIL: This operation is not supported for sequences. -CREATE VIEW ctlv1 AS SELECT * FROM ctlt4; -CREATE TABLE ctlt11 (LIKE ctlv1); -CREATE TABLE ctlt11a (LIKE ctlv1 INCLUDING ALL); -CREATE TYPE ctlty1 AS (a int, b text); -CREATE TABLE ctlt12 (LIKE ctlty1); -DROP SEQUENCE ctlseq1; -DROP TYPE ctlty1; -DROP VIEW ctlv1; -DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12; -NOTICE: table "ctlt10" does not exist, skipping +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_generic.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_generic.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_generic.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_generic.out 2021-10-30 22:04:15 -0600 @@ -1,747 +1,2 @@ --- --- Test for ALTER some_object {RENAME TO, OWNER TO, SET SCHEMA} --- --- Clean up in case a prior regression run failed -SET client_min_messages TO 'warning'; -DROP ROLE IF EXISTS regress_alter_generic_user1; -DROP ROLE IF EXISTS regress_alter_generic_user2; -DROP ROLE IF EXISTS regress_alter_generic_user3; -RESET client_min_messages; -CREATE USER regress_alter_generic_user3; -CREATE USER regress_alter_generic_user2; -CREATE USER regress_alter_generic_user1 IN ROLE regress_alter_generic_user3; -CREATE SCHEMA alt_nsp1; -CREATE SCHEMA alt_nsp2; -GRANT ALL ON SCHEMA alt_nsp1, alt_nsp2 TO public; -SET search_path = alt_nsp1, public; --- --- Function and Aggregate --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql - AS 'SELECT $1 + 1'; -CREATE FUNCTION alt_func2(int) RETURNS int LANGUAGE sql - AS 'SELECT $1 - 1'; -CREATE AGGREGATE alt_agg1 ( - sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond = 0 -); -CREATE AGGREGATE alt_agg2 ( - sfunc1 = int4mi, basetype = int4, stype1 = int4, initcond = 0 -); -ALTER AGGREGATE alt_func1(int) RENAME TO alt_func3; -- failed (not aggregate) -ERROR: function alt_func1(integer) is not an aggregate -ALTER AGGREGATE alt_func1(int) OWNER TO regress_alter_generic_user3; -- failed (not aggregate) -ERROR: function alt_func1(integer) is not an aggregate -ALTER AGGREGATE alt_func1(int) SET SCHEMA alt_nsp2; -- failed (not aggregate) -ERROR: function alt_func1(integer) is not an aggregate -ALTER FUNCTION alt_func1(int) RENAME TO alt_func2; -- failed (name conflict) -ERROR: function alt_func2(integer) already exists in schema "alt_nsp1" -ALTER FUNCTION alt_func1(int) RENAME TO alt_func3; -- OK -ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- OK -ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp1; -- OK, already there -ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- OK -ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg2; -- failed (name conflict) -ERROR: function alt_agg2(integer) already exists in schema "alt_nsp1" -ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg3; -- OK -ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- OK -ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql - AS 'SELECT $1 + 2'; -CREATE FUNCTION alt_func2(int) RETURNS int LANGUAGE sql - AS 'SELECT $1 - 2'; -CREATE AGGREGATE alt_agg1 ( - sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond = 100 -); -CREATE AGGREGATE alt_agg2 ( - sfunc1 = int4mi, basetype = int4, stype1 = int4, initcond = -100 -); -ALTER FUNCTION alt_func3(int) RENAME TO alt_func4; -- failed (not owner) -ERROR: must be owner of function alt_func3 -ALTER FUNCTION alt_func1(int) RENAME TO alt_func4; -- OK -ALTER FUNCTION alt_func3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of function alt_func3 -ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER FUNCTION alt_func3(int) SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of function alt_func3 -ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- failed (name conflicts) -ERROR: function alt_func2(integer) already exists in schema "alt_nsp2" -ALTER AGGREGATE alt_agg3(int) RENAME TO alt_agg4; -- failed (not owner) -ERROR: must be owner of function alt_agg3 -ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg4; -- OK -ALTER AGGREGATE alt_agg3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of function alt_agg3 -ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER AGGREGATE alt_agg3(int) SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of function alt_agg3 -ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: function alt_agg2(integer) already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT n.nspname, proname, prorettype::regtype, prokind, a.rolname - FROM pg_proc p, pg_namespace n, pg_authid a - WHERE p.pronamespace = n.oid AND p.proowner = a.oid - AND n.nspname IN ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, proname; - nspname | proname | prorettype | prokind | rolname -----------+-----------+------------+---------+----------------------------- - alt_nsp1 | alt_agg2 | integer | a | regress_alter_generic_user2 - alt_nsp1 | alt_agg3 | integer | a | regress_alter_generic_user1 - alt_nsp1 | alt_agg4 | integer | a | regress_alter_generic_user2 - alt_nsp1 | alt_func2 | integer | f | regress_alter_generic_user2 - alt_nsp1 | alt_func3 | integer | f | regress_alter_generic_user1 - alt_nsp1 | alt_func4 | integer | f | regress_alter_generic_user2 - alt_nsp2 | alt_agg2 | integer | a | regress_alter_generic_user3 - alt_nsp2 | alt_func2 | integer | f | regress_alter_generic_user3 -(8 rows) - --- --- We would test collations here, but it's not possible because the error --- messages tend to be nonportable. --- --- --- Conversion --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -ALTER CONVERSION alt_conv1 RENAME TO alt_conv2; -- failed (name conflict) -ERROR: conversion "alt_conv2" already exists in schema "alt_nsp1" -ALTER CONVERSION alt_conv1 RENAME TO alt_conv3; -- OK -ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- OK -ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -ALTER CONVERSION alt_conv3 RENAME TO alt_conv4; -- failed (not owner) -ERROR: must be owner of conversion alt_conv3 -ALTER CONVERSION alt_conv1 RENAME TO alt_conv4; -- OK -ALTER CONVERSION alt_conv3 OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of conversion alt_conv3 -ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER CONVERSION alt_conv3 SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of conversion alt_conv3 -ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: conversion "alt_conv2" already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT n.nspname, c.conname, a.rolname - FROM pg_conversion c, pg_namespace n, pg_authid a - WHERE c.connamespace = n.oid AND c.conowner = a.oid - AND n.nspname IN ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, conname; - nspname | conname | rolname -----------+-----------+----------------------------- - alt_nsp1 | alt_conv2 | regress_alter_generic_user2 - alt_nsp1 | alt_conv3 | regress_alter_generic_user1 - alt_nsp1 | alt_conv4 | regress_alter_generic_user2 - alt_nsp2 | alt_conv2 | regress_alter_generic_user3 -(4 rows) - --- --- Foreign Data Wrapper and Foreign Server --- -CREATE FOREIGN DATA WRAPPER alt_fdw1; -CREATE FOREIGN DATA WRAPPER alt_fdw2; -CREATE SERVER alt_fserv1 FOREIGN DATA WRAPPER alt_fdw1; -CREATE SERVER alt_fserv2 FOREIGN DATA WRAPPER alt_fdw2; -ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw2; -- failed (name conflict) -ERROR: foreign-data wrapper "alt_fdw2" already exists -ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw3; -- OK -ALTER SERVER alt_fserv1 RENAME TO alt_fserv2; -- failed (name conflict) -ERROR: server "alt_fserv2" already exists -ALTER SERVER alt_fserv1 RENAME TO alt_fserv3; -- OK -SELECT fdwname FROM pg_foreign_data_wrapper WHERE fdwname like 'alt_fdw%'; - fdwname ----------- - alt_fdw2 - alt_fdw3 -(2 rows) - -SELECT srvname FROM pg_foreign_server WHERE srvname like 'alt_fserv%'; - srvname ------------- - alt_fserv2 - alt_fserv3 -(2 rows) - --- --- Procedural Language --- -CREATE LANGUAGE alt_lang1 HANDLER plpgsql_call_handler; -CREATE LANGUAGE alt_lang2 HANDLER plpgsql_call_handler; -ALTER LANGUAGE alt_lang1 OWNER TO regress_alter_generic_user1; -- OK -ALTER LANGUAGE alt_lang2 OWNER TO regress_alter_generic_user2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user1; -ALTER LANGUAGE alt_lang1 RENAME TO alt_lang2; -- failed (name conflict) -ERROR: language "alt_lang2" already exists -ALTER LANGUAGE alt_lang2 RENAME TO alt_lang3; -- failed (not owner) -ERROR: must be owner of language alt_lang2 -ALTER LANGUAGE alt_lang1 RENAME TO alt_lang3; -- OK -ALTER LANGUAGE alt_lang2 OWNER TO regress_alter_generic_user3; -- failed (not owner) -ERROR: must be owner of language alt_lang2 -ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user3; -- OK -RESET SESSION AUTHORIZATION; -SELECT lanname, a.rolname - FROM pg_language l, pg_authid a - WHERE l.lanowner = a.oid AND l.lanname like 'alt_lang%' - ORDER BY lanname; - lanname | rolname ------------+----------------------------- - alt_lang2 | regress_alter_generic_user2 - alt_lang3 | regress_alter_generic_user3 -(2 rows) - --- --- Operator --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); -CREATE OPERATOR @+@ ( leftarg = int4, rightarg = int4, procedure = int4pl ); -ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user3; -- OK -ALTER OPERATOR @-@(int4, int4) SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); -ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of operator @+@ -ALTER OPERATOR @-@(int4, int4) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER OPERATOR @+@(int4, int4) SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of operator @+@ --- can't test this: the error message includes the raw oid of namespace --- ALTER OPERATOR @-@(int4, int4) SET SCHEMA alt_nsp2; -- failed (name conflict) -RESET SESSION AUTHORIZATION; -SELECT n.nspname, oprname, a.rolname, - oprleft::regtype, oprright::regtype, oprcode::regproc - FROM pg_operator o, pg_namespace n, pg_authid a - WHERE o.oprnamespace = n.oid AND o.oprowner = a.oid - AND n.nspname IN ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, oprname; - nspname | oprname | rolname | oprleft | oprright | oprcode -----------+---------+-----------------------------+---------+----------+--------- - alt_nsp1 | @+@ | regress_alter_generic_user3 | integer | integer | int4pl - alt_nsp1 | @-@ | regress_alter_generic_user2 | integer | integer | int4mi - alt_nsp2 | @-@ | regress_alter_generic_user1 | integer | integer | int4mi -(3 rows) - --- --- OpFamily and OpClass --- -CREATE OPERATOR FAMILY alt_opf1 USING hash; -CREATE OPERATOR FAMILY alt_opf2 USING hash; -ALTER OPERATOR FAMILY alt_opf1 USING hash OWNER TO regress_alter_generic_user1; -ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user1; -CREATE OPERATOR CLASS alt_opc1 FOR TYPE uuid USING hash AS STORAGE uuid; -CREATE OPERATOR CLASS alt_opc2 FOR TYPE uuid USING hash AS STORAGE uuid; -ALTER OPERATOR CLASS alt_opc1 USING hash OWNER TO regress_alter_generic_user1; -ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user1; -SET SESSION AUTHORIZATION regress_alter_generic_user1; -ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf2; -- failed (name conflict) -ERROR: operator family "alt_opf2" for access method "hash" already exists in schema "alt_nsp1" -ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf3; -- OK -ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- OK -ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- OK -ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc2; -- failed (name conflict) -ERROR: operator class "alt_opc2" for access method "hash" already exists in schema "alt_nsp1" -ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc3; -- OK -ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- OK -ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- OK -RESET SESSION AUTHORIZATION; -CREATE OPERATOR FAMILY alt_opf1 USING hash; -CREATE OPERATOR FAMILY alt_opf2 USING hash; -ALTER OPERATOR FAMILY alt_opf1 USING hash OWNER TO regress_alter_generic_user2; -ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user2; -CREATE OPERATOR CLASS alt_opc1 FOR TYPE macaddr USING hash AS STORAGE macaddr; -CREATE OPERATOR CLASS alt_opc2 FOR TYPE macaddr USING hash AS STORAGE macaddr; -ALTER OPERATOR CLASS alt_opc1 USING hash OWNER TO regress_alter_generic_user2; -ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user2; -SET SESSION AUTHORIZATION regress_alter_generic_user2; -ALTER OPERATOR FAMILY alt_opf3 USING hash RENAME TO alt_opf4; -- failed (not owner) -ERROR: must be owner of operator family alt_opf3 -ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf4; -- OK -ALTER OPERATOR FAMILY alt_opf3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of operator family alt_opf3 -ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER OPERATOR FAMILY alt_opf3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of operator family alt_opf3 -ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: operator family "alt_opf2" for access method "hash" already exists in schema "alt_nsp2" -ALTER OPERATOR CLASS alt_opc3 USING hash RENAME TO alt_opc4; -- failed (not owner) -ERROR: must be owner of operator class alt_opc3 -ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc4; -- OK -ALTER OPERATOR CLASS alt_opc3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of operator class alt_opc3 -ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER OPERATOR CLASS alt_opc3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of operator class alt_opc3 -ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: operator class "alt_opc2" for access method "hash" already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT nspname, opfname, amname, rolname - FROM pg_opfamily o, pg_am m, pg_namespace n, pg_authid a - WHERE o.opfmethod = m.oid AND o.opfnamespace = n.oid AND o.opfowner = a.oid - AND n.nspname IN ('alt_nsp1', 'alt_nsp2') - AND NOT opfname LIKE 'alt_opc%' - ORDER BY nspname, opfname; - nspname | opfname | amname | rolname -----------+----------+--------+----------------------------- - alt_nsp1 | alt_opf2 | hash | regress_alter_generic_user2 - alt_nsp1 | alt_opf3 | hash | regress_alter_generic_user1 - alt_nsp1 | alt_opf4 | hash | regress_alter_generic_user2 - alt_nsp2 | alt_opf2 | hash | regress_alter_generic_user3 -(4 rows) - -SELECT nspname, opcname, amname, rolname - FROM pg_opclass o, pg_am m, pg_namespace n, pg_authid a - WHERE o.opcmethod = m.oid AND o.opcnamespace = n.oid AND o.opcowner = a.oid - AND n.nspname IN ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, opcname; - nspname | opcname | amname | rolname -----------+----------+--------+----------------------------- - alt_nsp1 | alt_opc2 | hash | regress_alter_generic_user2 - alt_nsp1 | alt_opc3 | hash | regress_alter_generic_user1 - alt_nsp1 | alt_opc4 | hash | regress_alter_generic_user2 - alt_nsp2 | alt_opc2 | hash | regress_alter_generic_user3 -(4 rows) - --- ALTER OPERATOR FAMILY ... ADD/DROP --- Should work. Textbook case of CREATE / ALTER ADD / ALTER DROP / DROP -BEGIN TRANSACTION; -CREATE OPERATOR FAMILY alt_opf4 USING btree; -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD - -- int4 vs int2 - OPERATOR 1 < (int4, int2) , - OPERATOR 2 <= (int4, int2) , - OPERATOR 3 = (int4, int2) , - OPERATOR 4 >= (int4, int2) , - OPERATOR 5 > (int4, int2) , - FUNCTION 1 btint42cmp(int4, int2); -ALTER OPERATOR FAMILY alt_opf4 USING btree DROP - -- int4 vs int2 - OPERATOR 1 (int4, int2) , - OPERATOR 2 (int4, int2) , - OPERATOR 3 (int4, int2) , - OPERATOR 4 (int4, int2) , - OPERATOR 5 (int4, int2) , - FUNCTION 1 (int4, int2) ; -DROP OPERATOR FAMILY alt_opf4 USING btree; -ROLLBACK; --- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP -CREATE OPERATOR FAMILY alt_opf4 USING btree; -ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method -ERROR: access method "invalid_index_method" does not exist -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5 -ERROR: invalid operator number 6, must be between 1 and 5 -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5 -ERROR: invalid operator number 0, must be between 1 and 5 -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types -ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function -ERROR: invalid function number 0, must be between 1 and 5 -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5 -ERROR: invalid function number 6, must be between 1 and 5 -ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY -ERROR: STORAGE cannot be specified in ALTER OPERATOR FAMILY -DROP OPERATOR FAMILY alt_opf4 USING btree; --- Should fail. Need to be SUPERUSER to do ALTER OPERATOR FAMILY .. ADD / DROP -BEGIN TRANSACTION; -CREATE ROLE regress_alter_generic_user5 NOSUPERUSER; -CREATE OPERATOR FAMILY alt_opf5 USING btree; -SET ROLE regress_alter_generic_user5; -ALTER OPERATOR FAMILY alt_opf5 USING btree ADD OPERATOR 1 < (int4, int2), FUNCTION 1 btint42cmp(int4, int2); -ERROR: must be superuser to alter an operator family -RESET ROLE; -ERROR: current transaction is aborted, commands ignored until end of transaction block -DROP OPERATOR FAMILY alt_opf5 USING btree; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- Should fail. Need rights to namespace for ALTER OPERATOR FAMILY .. ADD / DROP -BEGIN TRANSACTION; -CREATE ROLE regress_alter_generic_user6; -CREATE SCHEMA alt_nsp6; -REVOKE ALL ON SCHEMA alt_nsp6 FROM regress_alter_generic_user6; -CREATE OPERATOR FAMILY alt_nsp6.alt_opf6 USING btree; -SET ROLE regress_alter_generic_user6; -ALTER OPERATOR FAMILY alt_nsp6.alt_opf6 USING btree ADD OPERATOR 1 < (int4, int2); -ERROR: permission denied for schema alt_nsp6 -ROLLBACK; --- Should fail. Only two arguments required for ALTER OPERATOR FAMILY ... DROP OPERATOR -CREATE OPERATOR FAMILY alt_opf7 USING btree; -ALTER OPERATOR FAMILY alt_opf7 USING btree ADD OPERATOR 1 < (int4, int2); -ALTER OPERATOR FAMILY alt_opf7 USING btree DROP OPERATOR 1 (int4, int2, int8); -ERROR: one or two argument types must be specified -DROP OPERATOR FAMILY alt_opf7 USING btree; --- Should work. During ALTER OPERATOR FAMILY ... DROP OPERATOR --- when left type is the same as right type, a DROP with only one argument type should work -CREATE OPERATOR FAMILY alt_opf8 USING btree; -ALTER OPERATOR FAMILY alt_opf8 USING btree ADD OPERATOR 1 < (int4, int4); -DROP OPERATOR FAMILY alt_opf8 USING btree; --- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY -CREATE OPERATOR FAMILY alt_opf9 USING gist; -ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; -DROP OPERATOR FAMILY alt_opf9 USING gist; --- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY -CREATE OPERATOR FAMILY alt_opf10 USING btree; -ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; -ERROR: access method "btree" does not support ordering operators -DROP OPERATOR FAMILY alt_opf10 USING btree; --- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY -CREATE OPERATOR FAMILY alt_opf11 USING gist; -ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; -ALTER OPERATOR FAMILY alt_opf11 USING gist DROP OPERATOR 1 (int4, int4); -DROP OPERATOR FAMILY alt_opf11 USING gist; --- Should fail. btree comparison functions should return INTEGER in ALTER OPERATOR FAMILY ... ADD FUNCTION -BEGIN TRANSACTION; -CREATE OPERATOR FAMILY alt_opf12 USING btree; -CREATE FUNCTION fn_opf12 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; -ALTER OPERATOR FAMILY alt_opf12 USING btree ADD FUNCTION 1 fn_opf12(int4, int2); -ERROR: btree comparison functions must return integer -DROP OPERATOR FAMILY alt_opf12 USING btree; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- Should fail. hash comparison functions should return INTEGER in ALTER OPERATOR FAMILY ... ADD FUNCTION -BEGIN TRANSACTION; -CREATE OPERATOR FAMILY alt_opf13 USING hash; -CREATE FUNCTION fn_opf13 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; -ALTER OPERATOR FAMILY alt_opf13 USING hash ADD FUNCTION 1 fn_opf13(int4); -ERROR: hash function 1 must return integer -DROP OPERATOR FAMILY alt_opf13 USING hash; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- Should fail. btree comparison functions should have two arguments in ALTER OPERATOR FAMILY ... ADD FUNCTION -BEGIN TRANSACTION; -CREATE OPERATOR FAMILY alt_opf14 USING btree; -CREATE FUNCTION fn_opf14 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; -ALTER OPERATOR FAMILY alt_opf14 USING btree ADD FUNCTION 1 fn_opf14(int4); -ERROR: btree comparison functions must have two arguments -DROP OPERATOR FAMILY alt_opf14 USING btree; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- Should fail. hash comparison functions should have one argument in ALTER OPERATOR FAMILY ... ADD FUNCTION -BEGIN TRANSACTION; -CREATE OPERATOR FAMILY alt_opf15 USING hash; -CREATE FUNCTION fn_opf15 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; -ALTER OPERATOR FAMILY alt_opf15 USING hash ADD FUNCTION 1 fn_opf15(int4, int2); -ERROR: hash function 1 must have one argument -DROP OPERATOR FAMILY alt_opf15 USING hash; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- Should fail. In gist throw an error when giving different data types for function argument --- without defining left / right type in ALTER OPERATOR FAMILY ... ADD FUNCTION -CREATE OPERATOR FAMILY alt_opf16 USING gist; -ALTER OPERATOR FAMILY alt_opf16 USING gist ADD FUNCTION 1 btint42cmp(int4, int2); -ERROR: associated data types must be specified for index support function -DROP OPERATOR FAMILY alt_opf16 USING gist; --- Should fail. duplicate operator number / function number in ALTER OPERATOR FAMILY ... ADD FUNCTION -CREATE OPERATOR FAMILY alt_opf17 USING btree; -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4), OPERATOR 1 < (int4, int4); -- operator # appears twice in same statement -ERROR: operator number 1 for (integer,integer) appears more than once -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested first-time -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested again in separate statement -ERROR: operator 1(integer,integer) already exists in operator family "alt_opf17" -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD - OPERATOR 1 < (int4, int2) , - OPERATOR 2 <= (int4, int2) , - OPERATOR 3 = (int4, int2) , - OPERATOR 4 >= (int4, int2) , - OPERATOR 5 > (int4, int2) , - FUNCTION 1 btint42cmp(int4, int2) , - FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears twice in same statement -ERROR: function number 1 for (integer,smallint) appears more than once -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD - OPERATOR 1 < (int4, int2) , - OPERATOR 2 <= (int4, int2) , - OPERATOR 3 = (int4, int2) , - OPERATOR 4 >= (int4, int2) , - OPERATOR 5 > (int4, int2) , - FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears first time -ALTER OPERATOR FAMILY alt_opf17 USING btree ADD - OPERATOR 1 < (int4, int2) , - OPERATOR 2 <= (int4, int2) , - OPERATOR 3 = (int4, int2) , - OPERATOR 4 >= (int4, int2) , - OPERATOR 5 > (int4, int2) , - FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 requested again in separate statement -ERROR: operator 1(integer,smallint) already exists in operator family "alt_opf17" -DROP OPERATOR FAMILY alt_opf17 USING btree; --- Should fail. Ensure that DROP requests for missing OPERATOR / FUNCTIONS --- return appropriate message in ALTER OPERATOR FAMILY ... DROP OPERATOR / FUNCTION -CREATE OPERATOR FAMILY alt_opf18 USING btree; -ALTER OPERATOR FAMILY alt_opf18 USING btree DROP OPERATOR 1 (int4, int4); -ERROR: operator 1(integer,integer) does not exist in operator family "alt_opf18" -ALTER OPERATOR FAMILY alt_opf18 USING btree ADD - OPERATOR 1 < (int4, int2) , - OPERATOR 2 <= (int4, int2) , - OPERATOR 3 = (int4, int2) , - OPERATOR 4 >= (int4, int2) , - OPERATOR 5 > (int4, int2) , - FUNCTION 1 btint42cmp(int4, int2); --- Should fail. Not allowed to have cross-type equalimage function. -ALTER OPERATOR FAMILY alt_opf18 USING btree - ADD FUNCTION 4 (int4, int2) btequalimage(oid); -ERROR: btree equal image functions must not be cross-type -ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4); -ERROR: function 2(integer,integer) does not exist in operator family "alt_opf18" -DROP OPERATOR FAMILY alt_opf18 USING btree; --- Should fail. Invalid opclass options function (#5) specifications. -CREATE OPERATOR FAMILY alt_opf19 USING btree; -ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 test_opclass_options_func(internal, text[], bool); -ERROR: function test_opclass_options_func(internal, text[], boolean) does not exist -ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) btint42cmp(int4, int2); -ERROR: invalid operator class options parsing function -HINT: Valid signature of operator class options parsing function is (internal) RETURNS void. -ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4, int2) btint42cmp(int4, int2); -ERROR: left and right associated data types for operator class options parsing functions must match -ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok -ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4); -DROP OPERATOR FAMILY alt_opf19 USING btree; --- --- Statistics --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); -CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_1; -CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_1; -ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) -ERROR: statistics object "alt_stat2" already exists in schema "alt_nsp1" -ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- OK -ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- OK -ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE TABLE alt_regress_2 (a INTEGER, b INTEGER); -CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_2; -CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_2; -ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) -ERROR: must be owner of statistics object alt_stat3 -ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK -ALTER STATISTICS alt_stat3 OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of statistics object alt_stat3 -ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER STATISTICS alt_stat3 SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of statistics object alt_stat3 -ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: statistics object "alt_stat2" already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT nspname, stxname, rolname - FROM pg_statistic_ext s, pg_namespace n, pg_authid a - WHERE s.stxnamespace = n.oid AND s.stxowner = a.oid - AND n.nspname in ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, stxname; - nspname | stxname | rolname -----------+-----------+----------------------------- - alt_nsp1 | alt_stat2 | regress_alter_generic_user2 - alt_nsp1 | alt_stat3 | regress_alter_generic_user1 - alt_nsp1 | alt_stat4 | regress_alter_generic_user2 - alt_nsp2 | alt_stat2 | regress_alter_generic_user3 -(4 rows) - --- --- Text Search Dictionary --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 (template=simple); -CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 (template=simple); -ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict2; -- failed (name conflict) -ERROR: text search dictionary "alt_ts_dict2" already exists in schema "alt_nsp1" -ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict3; -- OK -ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- OK -ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 (template=simple); -CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 (template=simple); -ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 RENAME TO alt_ts_dict4; -- failed (not owner) -ERROR: must be owner of text search dictionary alt_ts_dict3 -ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict4; -- OK -ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of text search dictionary alt_ts_dict3 -ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of text search dictionary alt_ts_dict3 -ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search dictionary "alt_ts_dict2" already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT nspname, dictname, rolname - FROM pg_ts_dict t, pg_namespace n, pg_authid a - WHERE t.dictnamespace = n.oid AND t.dictowner = a.oid - AND n.nspname in ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, dictname; - nspname | dictname | rolname -----------+--------------+----------------------------- - alt_nsp1 | alt_ts_dict2 | regress_alter_generic_user2 - alt_nsp1 | alt_ts_dict3 | regress_alter_generic_user1 - alt_nsp1 | alt_ts_dict4 | regress_alter_generic_user2 - alt_nsp2 | alt_ts_dict2 | regress_alter_generic_user3 -(4 rows) - --- --- Text Search Configuration --- -SET SESSION AUTHORIZATION regress_alter_generic_user1; -CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 (copy=english); -CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 (copy=english); -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf2; -- failed (name conflict) -ERROR: text search configuration "alt_ts_conf2" already exists in schema "alt_nsp1" -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf3; -- OK -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- OK -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- OK -SET SESSION AUTHORIZATION regress_alter_generic_user2; -CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 (copy=english); -CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 (copy=english); -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 RENAME TO alt_ts_conf4; -- failed (not owner) -ERROR: must be owner of text search configuration alt_ts_conf3 -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf4; -- OK -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 OWNER TO regress_alter_generic_user2; -- failed (not owner) -ERROR: must be owner of text search configuration alt_ts_conf3 -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 SET SCHEMA alt_nsp2; -- failed (not owner) -ERROR: must be owner of text search configuration alt_ts_conf3 -ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search configuration "alt_ts_conf2" already exists in schema "alt_nsp2" -RESET SESSION AUTHORIZATION; -SELECT nspname, cfgname, rolname - FROM pg_ts_config t, pg_namespace n, pg_authid a - WHERE t.cfgnamespace = n.oid AND t.cfgowner = a.oid - AND n.nspname in ('alt_nsp1', 'alt_nsp2') - ORDER BY nspname, cfgname; - nspname | cfgname | rolname -----------+--------------+----------------------------- - alt_nsp1 | alt_ts_conf2 | regress_alter_generic_user2 - alt_nsp1 | alt_ts_conf3 | regress_alter_generic_user1 - alt_nsp1 | alt_ts_conf4 | regress_alter_generic_user2 - alt_nsp2 | alt_ts_conf2 | regress_alter_generic_user3 -(4 rows) - --- --- Text Search Template --- -CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); -CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); -ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp2; -- failed (name conflict) -ERROR: text search template "alt_ts_temp2" already exists in schema "alt_nsp1" -ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp3; -- OK -ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- OK -CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); -ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search template "alt_ts_temp2" already exists in schema "alt_nsp2" --- invalid: non-lowercase quoted identifiers -CREATE TEXT SEARCH TEMPLATE tstemp_case ("Init" = init_function); -ERROR: text search template parameter "Init" not recognized -SELECT nspname, tmplname - FROM pg_ts_template t, pg_namespace n - WHERE t.tmplnamespace = n.oid AND nspname like 'alt_nsp%' - ORDER BY nspname, tmplname; - nspname | tmplname -----------+-------------- - alt_nsp1 | alt_ts_temp2 - alt_nsp1 | alt_ts_temp3 - alt_nsp2 | alt_ts_temp2 -(3 rows) - --- --- Text Search Parser --- -CREATE TEXT SEARCH PARSER alt_ts_prs1 - (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); -CREATE TEXT SEARCH PARSER alt_ts_prs2 - (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); -ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs2; -- failed (name conflict) -ERROR: text search parser "alt_ts_prs2" already exists in schema "alt_nsp1" -ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs3; -- OK -ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- OK -CREATE TEXT SEARCH PARSER alt_ts_prs2 - (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); -ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search parser "alt_ts_prs2" already exists in schema "alt_nsp2" --- invalid: non-lowercase quoted identifiers -CREATE TEXT SEARCH PARSER tspars_case ("Start" = start_function); -ERROR: text search parser parameter "Start" not recognized -SELECT nspname, prsname - FROM pg_ts_parser t, pg_namespace n - WHERE t.prsnamespace = n.oid AND nspname like 'alt_nsp%' - ORDER BY nspname, prsname; - nspname | prsname -----------+------------- - alt_nsp1 | alt_ts_prs2 - alt_nsp1 | alt_ts_prs3 - alt_nsp2 | alt_ts_prs2 -(3 rows) - ---- ---- Cleanup resources ---- -DROP FOREIGN DATA WRAPPER alt_fdw2 CASCADE; -NOTICE: drop cascades to server alt_fserv2 -DROP FOREIGN DATA WRAPPER alt_fdw3 CASCADE; -NOTICE: drop cascades to server alt_fserv3 -DROP LANGUAGE alt_lang2 CASCADE; -DROP LANGUAGE alt_lang3 CASCADE; -DROP SCHEMA alt_nsp1 CASCADE; -NOTICE: drop cascades to 28 other objects -DETAIL: drop cascades to function alt_func3(integer) -drop cascades to function alt_agg3(integer) -drop cascades to function alt_func4(integer) -drop cascades to function alt_func2(integer) -drop cascades to function alt_agg4(integer) -drop cascades to function alt_agg2(integer) -drop cascades to conversion alt_conv3 -drop cascades to conversion alt_conv4 -drop cascades to conversion alt_conv2 -drop cascades to operator @+@(integer,integer) -drop cascades to operator @-@(integer,integer) -drop cascades to operator family alt_opf3 for access method hash -drop cascades to operator family alt_opc1 for access method hash -drop cascades to operator family alt_opc2 for access method hash -drop cascades to operator family alt_opf4 for access method hash -drop cascades to operator family alt_opf2 for access method hash -drop cascades to table alt_regress_1 -drop cascades to table alt_regress_2 -drop cascades to text search dictionary alt_ts_dict3 -drop cascades to text search dictionary alt_ts_dict4 -drop cascades to text search dictionary alt_ts_dict2 -drop cascades to text search configuration alt_ts_conf3 -drop cascades to text search configuration alt_ts_conf4 -drop cascades to text search configuration alt_ts_conf2 -drop cascades to text search template alt_ts_temp3 -drop cascades to text search template alt_ts_temp2 -drop cascades to text search parser alt_ts_prs3 -drop cascades to text search parser alt_ts_prs2 -DROP SCHEMA alt_nsp2 CASCADE; -NOTICE: drop cascades to 9 other objects -DETAIL: drop cascades to function alt_nsp2.alt_func2(integer) -drop cascades to function alt_nsp2.alt_agg2(integer) -drop cascades to conversion alt_nsp2.alt_conv2 -drop cascades to operator alt_nsp2.@-@(integer,integer) -drop cascades to operator family alt_nsp2.alt_opf2 for access method hash -drop cascades to text search dictionary alt_nsp2.alt_ts_dict2 -drop cascades to text search configuration alt_nsp2.alt_ts_conf2 -drop cascades to text search template alt_nsp2.alt_ts_temp2 -drop cascades to text search parser alt_nsp2.alt_ts_prs2 -DROP USER regress_alter_generic_user1; -DROP USER regress_alter_generic_user2; -DROP USER regress_alter_generic_user3; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_operator.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_operator.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_operator.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_operator.out 2021-10-30 22:04:15 -0600 @@ -1,139 +1,2 @@ -CREATE FUNCTION alter_op_test_fn(boolean, boolean) -RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE; -CREATE FUNCTION customcontsel(internal, oid, internal, integer) -RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; -CREATE OPERATOR === ( - LEFTARG = boolean, - RIGHTARG = boolean, - PROCEDURE = alter_op_test_fn, - COMMUTATOR = ===, - NEGATOR = !==, - RESTRICT = customcontsel, - JOIN = contjoinsel, - HASHES, MERGES -); -SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype -FROM pg_depend -WHERE classid = 'pg_operator'::regclass AND - objid = '===(bool,bool)'::regoperator -ORDER BY 1; - ref | deptype --------------------------------------------------------+--------- - function alter_op_test_fn(boolean,boolean) | n - function customcontsel(internal,oid,internal,integer) | n - schema public | n -(3 rows) - --- --- Reset and set params --- -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); -ALTER OPERATOR === (boolean, boolean) SET (JOIN = NONE); -SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' - AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; - oprrest | oprjoin ----------+--------- - - | - -(1 row) - -SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype -FROM pg_depend -WHERE classid = 'pg_operator'::regclass AND - objid = '===(bool,bool)'::regoperator -ORDER BY 1; - ref | deptype ---------------------------------------------+--------- - function alter_op_test_fn(boolean,boolean) | n - schema public | n -(2 rows) - -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel); -ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel); -SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' - AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; - oprrest | oprjoin ----------+------------- - contsel | contjoinsel -(1 row) - -SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype -FROM pg_depend -WHERE classid = 'pg_operator'::regclass AND - objid = '===(bool,bool)'::regoperator -ORDER BY 1; - ref | deptype ---------------------------------------------+--------- - function alter_op_test_fn(boolean,boolean) | n - schema public | n -(2 rows) - -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE, JOIN = NONE); -SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' - AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; - oprrest | oprjoin ----------+--------- - - | - -(1 row) - -SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype -FROM pg_depend -WHERE classid = 'pg_operator'::regclass AND - objid = '===(bool,bool)'::regoperator -ORDER BY 1; - ref | deptype ---------------------------------------------+--------- - function alter_op_test_fn(boolean,boolean) | n - schema public | n -(2 rows) - -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = customcontsel, JOIN = contjoinsel); -SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' - AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; - oprrest | oprjoin ----------------+------------- - customcontsel | contjoinsel -(1 row) - -SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype -FROM pg_depend -WHERE classid = 'pg_operator'::regclass AND - objid = '===(bool,bool)'::regoperator -ORDER BY 1; - ref | deptype --------------------------------------------------------+--------- - function alter_op_test_fn(boolean,boolean) | n - function customcontsel(internal,oid,internal,integer) | n - schema public | n -(3 rows) - --- --- Test invalid options. --- -ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====); -ERROR: operator attribute "commutator" cannot be changed -ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====); -ERROR: operator attribute "negator" cannot be changed -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = non_existent_func); -ERROR: function non_existent_func(internal, oid, internal, integer) does not exist -ALTER OPERATOR === (boolean, boolean) SET (JOIN = non_existent_func); -ERROR: function non_existent_func(internal, oid, internal, smallint, internal) does not exist -ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==); -ERROR: operator attribute "commutator" cannot be changed -ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==); -ERROR: operator attribute "negator" cannot be changed --- invalid: non-lowercase quoted identifiers -ALTER OPERATOR & (bit, bit) SET ("Restrict" = _int_contsel, "Join" = _int_contjoinsel); -ERROR: operator attribute "Restrict" not recognized --- --- Test permission check. Must be owner to ALTER OPERATOR. --- -CREATE USER regress_alter_op_user; -SET SESSION AUTHORIZATION regress_alter_op_user; -ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); -ERROR: must be owner of operator === --- Clean up -RESET SESSION AUTHORIZATION; -DROP USER regress_alter_op_user; -DROP OPERATOR === (boolean, boolean); -DROP FUNCTION customcontsel(internal, oid, internal, integer); -DROP FUNCTION alter_op_test_fn(boolean, boolean); +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/misc.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/misc.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/misc.out 2021-10-30 21:52:35 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/misc.out 2021-10-30 22:04:15 -0600 @@ -1,692 +1,2 @@ --- --- MISC --- --- --- BTREE --- -UPDATE onek - SET unique1 = onek.unique1 + 1; -UPDATE onek - SET unique1 = onek.unique1 - 1; --- --- BTREE partial --- --- UPDATE onek2 --- SET unique1 = onek2.unique1 + 1; ---UPDATE onek2 --- SET unique1 = onek2.unique1 - 1; --- --- BTREE shutting out non-functional updates --- --- the following two tests seem to take a long time on some --- systems. This non-func update stuff needs to be examined --- more closely. - jolly (2/22/96) --- -UPDATE tmp - SET stringu1 = reverse_name(onek.stringu1) - FROM onek - WHERE onek.stringu1 = 'JBAAAA' and - onek.stringu1 = tmp.stringu1; -UPDATE tmp - SET stringu1 = reverse_name(onek2.stringu1) - FROM onek2 - WHERE onek2.stringu1 = 'JCAAAA' and - onek2.stringu1 = tmp.stringu1; -DROP TABLE tmp; ---UPDATE person* --- SET age = age + 1; ---UPDATE person* --- SET age = age + 3 --- WHERE name = 'linda'; --- --- copy --- -COPY onek TO '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/onek.data'; -DELETE FROM onek; -COPY onek FROM '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/onek.data'; -SELECT unique1 FROM onek WHERE unique1 < 2 ORDER BY unique1; - unique1 ---------- - 0 - 1 -(2 rows) - -DELETE FROM onek2; -COPY onek2 FROM '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/onek.data'; -SELECT unique1 FROM onek2 WHERE unique1 < 2 ORDER BY unique1; - unique1 ---------- - 0 - 1 -(2 rows) - -COPY BINARY stud_emp TO '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stud_emp.data'; -DELETE FROM stud_emp; -COPY BINARY stud_emp FROM '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stud_emp.data'; -SELECT * FROM stud_emp; - name | age | location | salary | manager | gpa | percent --------+-----+------------+--------+---------+-----+--------- - jeff | 23 | (8,7.7) | 600 | sharon | 3.5 | - cim | 30 | (10.5,4.7) | 400 | | 3.4 | - linda | 19 | (0.9,6.1) | 100 | | 2.9 | -(3 rows) - --- COPY aggtest FROM stdin; --- 56 7.8 --- 100 99.097 --- 0 0.09561 --- 42 324.78 --- . --- COPY aggtest TO stdout; --- --- inheritance stress test --- -SELECT * FROM a_star*; - class | a --------+---- - a | 1 - a | 2 - a | - b | 3 - b | 4 - b | - b | - c | 5 - c | 6 - c | - c | - d | 7 - d | 8 - d | 9 - d | 10 - d | - d | 11 - d | 12 - d | 13 - d | - d | - d | - d | 14 - d | - d | - d | - d | - e | 15 - e | 16 - e | 17 - e | - e | 18 - e | - e | - f | 19 - f | 20 - f | 21 - f | 22 - f | - f | 24 - f | 25 - f | 26 - f | - f | - f | - f | 27 - f | - f | - f | - f | -(50 rows) - -SELECT * - FROM b_star* x - WHERE x.b = text 'bumble' or x.a < 3; - class | a | b --------+---+-------- - b | | bumble -(1 row) - -SELECT class, a - FROM c_star* x - WHERE x.c ~ text 'hi'; - class | a --------+---- - c | 5 - c | - d | 7 - d | 8 - d | 10 - d | - d | 12 - d | - d | - d | - e | 15 - e | 16 - e | - e | - f | 19 - f | 20 - f | 21 - f | - f | 24 - f | - f | - f | -(22 rows) - -SELECT class, b, c - FROM d_star* x - WHERE x.a < 100; - class | b | c --------+---------+------------ - d | grumble | hi sunita - d | stumble | hi koko - d | rumble | - d | | hi kristin - d | fumble | - d | | hi avi - d | | - d | | -(8 rows) - -SELECT class, c FROM e_star* x WHERE x.c NOTNULL; - class | c --------+------------- - e | hi carol - e | hi bob - e | hi michelle - e | hi elisa - f | hi claire - f | hi mike - f | hi marcel - f | hi keith - f | hi marc - f | hi allison - f | hi jeff - f | hi carl -(12 rows) - -SELECT * FROM f_star* x WHERE x.c ISNULL; - class | a | c | e | f --------+----+---+-----+------------------------------------------- - f | 22 | | -7 | ((111,555),(222,666),(333,777),(444,888)) - f | 25 | | -9 | - f | 26 | | | ((11111,33333),(22222,44444)) - f | | | -11 | ((1111111,3333333),(2222222,4444444)) - f | 27 | | | - f | | | -12 | - f | | | | ((11111111,33333333),(22222222,44444444)) - f | | | | -(8 rows) - --- grouping and aggregation on inherited sets have been busted in the past... -SELECT sum(a) FROM a_star*; - sum ------ - 355 -(1 row) - -SELECT class, sum(a) FROM a_star* GROUP BY class ORDER BY class; - class | sum --------+----- - a | 3 - b | 7 - c | 11 - d | 84 - e | 66 - f | 184 -(6 rows) - -ALTER TABLE f_star RENAME COLUMN f TO ff; -ALTER TABLE e_star* RENAME COLUMN e TO ee; -ALTER TABLE d_star* RENAME COLUMN d TO dd; -ALTER TABLE c_star* RENAME COLUMN c TO cc; -ALTER TABLE b_star* RENAME COLUMN b TO bb; -ALTER TABLE a_star* RENAME COLUMN a TO aa; -SELECT class, aa - FROM a_star* x - WHERE aa ISNULL; - class | aa --------+---- - a | - b | - b | - c | - c | - d | - d | - d | - d | - d | - d | - d | - d | - e | - e | - e | - f | - f | - f | - f | - f | - f | - f | - f | -(24 rows) - --- As of Postgres 7.1, ALTER implicitly recurses, --- so this should be same as ALTER a_star* -ALTER TABLE a_star RENAME COLUMN aa TO foo; -SELECT class, foo - FROM a_star* x - WHERE x.foo >= 2; - class | foo --------+----- - a | 2 - b | 3 - b | 4 - c | 5 - c | 6 - d | 7 - d | 8 - d | 9 - d | 10 - d | 11 - d | 12 - d | 13 - d | 14 - e | 15 - e | 16 - e | 17 - e | 18 - f | 19 - f | 20 - f | 21 - f | 22 - f | 24 - f | 25 - f | 26 - f | 27 -(25 rows) - -ALTER TABLE a_star RENAME COLUMN foo TO aa; -SELECT * - from a_star* - WHERE aa < 1000; - class | aa --------+---- - a | 1 - a | 2 - b | 3 - b | 4 - c | 5 - c | 6 - d | 7 - d | 8 - d | 9 - d | 10 - d | 11 - d | 12 - d | 13 - d | 14 - e | 15 - e | 16 - e | 17 - e | 18 - f | 19 - f | 20 - f | 21 - f | 22 - f | 24 - f | 25 - f | 26 - f | 27 -(26 rows) - -ALTER TABLE f_star ADD COLUMN f int4; -UPDATE f_star SET f = 10; -ALTER TABLE e_star* ADD COLUMN e int4; ---UPDATE e_star* SET e = 42; -SELECT * FROM e_star*; - class | aa | cc | ee | e --------+----+-------------+-----+--- - e | 15 | hi carol | -1 | - e | 16 | hi bob | | - e | 17 | | -2 | - e | | hi michelle | -3 | - e | 18 | | | - e | | hi elisa | | - e | | | -4 | - f | 19 | hi claire | -5 | - f | 20 | hi mike | -6 | - f | 21 | hi marcel | | - f | 22 | | -7 | - f | | hi keith | -8 | - f | 24 | hi marc | | - f | 25 | | -9 | - f | 26 | | | - f | | hi allison | -10 | - f | | hi jeff | | - f | | | -11 | - f | 27 | | | - f | | hi carl | | - f | | | -12 | - f | | | | - f | | | | -(23 rows) - -ALTER TABLE a_star* ADD COLUMN a text; -NOTICE: merging definition of column "a" for child "d_star" --- That ALTER TABLE should have added TOAST tables. -SELECT relname, reltoastrelid <> 0 AS has_toast_table - FROM pg_class - WHERE oid::regclass IN ('a_star', 'c_star') - ORDER BY 1; - relname | has_toast_table ----------+----------------- - a_star | t - c_star | t -(2 rows) - ---UPDATE b_star* --- SET a = text 'gazpacho' --- WHERE aa > 4; -SELECT class, aa, a FROM a_star*; - class | aa | a --------+----+--- - a | 1 | - a | 2 | - a | | - b | 3 | - b | 4 | - b | | - b | | - c | 5 | - c | 6 | - c | | - c | | - d | 7 | - d | 8 | - d | 9 | - d | 10 | - d | | - d | 11 | - d | 12 | - d | 13 | - d | | - d | | - d | | - d | 14 | - d | | - d | | - d | | - d | | - e | 15 | - e | 16 | - e | 17 | - e | | - e | 18 | - e | | - e | | - f | 19 | - f | 20 | - f | 21 | - f | 22 | - f | | - f | 24 | - f | 25 | - f | 26 | - f | | - f | | - f | | - f | 27 | - f | | - f | | - f | | - f | | -(50 rows) - --- --- versions --- --- --- postquel functions --- --- --- mike does post_hacking, --- joe and sally play basketball, and --- everyone else does nothing. --- -SELECT p.name, name(p.hobbies) FROM ONLY person p; - name | name --------+------------- - mike | posthacking - joe | basketball - sally | basketball -(3 rows) - --- --- as above, but jeff also does post_hacking. --- -SELECT p.name, name(p.hobbies) FROM person* p; - name | name --------+------------- - mike | posthacking - joe | basketball - sally | basketball - jeff | posthacking -(4 rows) - --- --- the next two queries demonstrate how functions generate bogus duplicates. --- this is a "feature" .. --- -SELECT DISTINCT hobbies_r.name, name(hobbies_r.equipment) FROM hobbies_r - ORDER BY 1,2; - name | name --------------+--------------- - basketball | hightops - posthacking | advil - posthacking | peet's coffee - skywalking | guts -(4 rows) - -SELECT hobbies_r.name, (hobbies_r.equipment).name FROM hobbies_r; - name | name --------------+--------------- - posthacking | advil - posthacking | peet's coffee - posthacking | advil - posthacking | peet's coffee - basketball | hightops - basketball | hightops - skywalking | guts -(7 rows) - --- --- mike needs advil and peet's coffee, --- joe and sally need hightops, and --- everyone else is fine. --- -SELECT p.name, name(p.hobbies), name(equipment(p.hobbies)) FROM ONLY person p; - name | name | name --------+-------------+--------------- - mike | posthacking | advil - mike | posthacking | peet's coffee - joe | basketball | hightops - sally | basketball | hightops -(4 rows) - --- --- as above, but jeff needs advil and peet's coffee as well. --- -SELECT p.name, name(p.hobbies), name(equipment(p.hobbies)) FROM person* p; - name | name | name --------+-------------+--------------- - mike | posthacking | advil - mike | posthacking | peet's coffee - joe | basketball | hightops - sally | basketball | hightops - jeff | posthacking | advil - jeff | posthacking | peet's coffee -(6 rows) - --- --- just like the last two, but make sure that the target list fixup and --- unflattening is being done correctly. --- -SELECT name(equipment(p.hobbies)), p.name, name(p.hobbies) FROM ONLY person p; - name | name | name ----------------+-------+------------- - advil | mike | posthacking - peet's coffee | mike | posthacking - hightops | joe | basketball - hightops | sally | basketball -(4 rows) - -SELECT (p.hobbies).equipment.name, p.name, name(p.hobbies) FROM person* p; - name | name | name ----------------+-------+------------- - advil | mike | posthacking - peet's coffee | mike | posthacking - hightops | joe | basketball - hightops | sally | basketball - advil | jeff | posthacking - peet's coffee | jeff | posthacking -(6 rows) - -SELECT (p.hobbies).equipment.name, name(p.hobbies), p.name FROM ONLY person p; - name | name | name ----------------+-------------+------- - advil | posthacking | mike - peet's coffee | posthacking | mike - hightops | basketball | joe - hightops | basketball | sally -(4 rows) - -SELECT name(equipment(p.hobbies)), name(p.hobbies), p.name FROM person* p; - name | name | name ----------------+-------------+------- - advil | posthacking | mike - peet's coffee | posthacking | mike - hightops | basketball | joe - hightops | basketball | sally - advil | posthacking | jeff - peet's coffee | posthacking | jeff -(6 rows) - -SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment_named_ambiguous_1a(hobby_construct_named(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment_named_ambiguous_1b(hobby_construct_named(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment_named_ambiguous_1c(hobby_construct_named(text 'skywalking', text 'mer'))); - name ------- - guts -(1 row) - -SELECT name(equipment_named_ambiguous_2a(text 'skywalking')); - name ------- - guts -(1 row) - -SELECT name(equipment_named_ambiguous_2b(text 'skywalking')); - name ---------------- - advil - peet's coffee - hightops - guts -(4 rows) - -SELECT hobbies_by_name('basketball'); - hobbies_by_name ------------------ - joe -(1 row) - -SELECT name, overpaid(emp.*) FROM emp; - name | overpaid ---------+---------- - sharon | t - sam | t - bill | t - jeff | f - cim | f - linda | f -(6 rows) - --- --- Try a few cases with SQL-spec row constructor expressions --- -SELECT * FROM equipment(ROW('skywalking', 'mer')); - name | hobby -------+------------ - guts | skywalking -(1 row) - -SELECT name(equipment(ROW('skywalking', 'mer'))); - name ------- - guts -(1 row) - -SELECT *, name(equipment(h.*)) FROM hobbies_r h; - name | person | name --------------+--------+--------------- - posthacking | mike | advil - posthacking | mike | peet's coffee - posthacking | jeff | advil - posthacking | jeff | peet's coffee - basketball | joe | hightops - basketball | sally | hightops - skywalking | | guts -(7 rows) - -SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h; - name | person | name --------------+--------+--------------- - posthacking | mike | advil - posthacking | mike | peet's coffee - posthacking | jeff | advil - posthacking | jeff | peet's coffee - basketball | joe | hightops - basketball | sally | hightops - skywalking | | guts -(7 rows) - --- --- functional joins --- --- --- instance rules --- --- --- rewrite rules --- +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/async.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/async.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/async.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/async.out 2021-10-30 22:04:15 -0600 @@ -1,42 +1,2 @@ --- --- ASYNC --- ---Should work. Send a valid message via a valid channel name -SELECT pg_notify('notify_async1','sample message1'); - pg_notify ------------ - -(1 row) - -SELECT pg_notify('notify_async1',''); - pg_notify ------------ - -(1 row) - -SELECT pg_notify('notify_async1',NULL); - pg_notify ------------ - -(1 row) - --- Should fail. Send a valid message via an invalid channel name -SELECT pg_notify('','sample message1'); -ERROR: channel name cannot be empty -SELECT pg_notify(NULL,'sample message1'); -ERROR: channel name cannot be empty -SELECT pg_notify('notify_async_channel_name_too_long______________________________','sample_message1'); -ERROR: channel name too long ---Should work. Valid NOTIFY/LISTEN/UNLISTEN commands -NOTIFY notify_async2; -LISTEN notify_async2; -UNLISTEN notify_async2; -UNLISTEN *; --- Should return zero while there are no pending notifications. --- src/test/isolation/specs/async-notify.spec tests for actual usage. -SELECT pg_notification_queue_usage(); - pg_notification_queue_usage ------------------------------ - 0 -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/dbsize.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/dbsize.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/dbsize.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/dbsize.out 2021-10-30 22:04:15 -0600 @@ -1,194 +1,2 @@ -SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM - (VALUES (10::bigint), (1000::bigint), (1000000::bigint), - (1000000000::bigint), (1000000000000::bigint), - (1000000000000000::bigint)) x(size); - size | pg_size_pretty | pg_size_pretty -------------------+----------------+---------------- - 10 | 10 bytes | -10 bytes - 1000 | 1000 bytes | -1000 bytes - 1000000 | 977 kB | -977 kB - 1000000000 | 954 MB | -954 MB - 1000000000000 | 931 GB | -931 GB - 1000000000000000 | 909 TB | -909 TB -(6 rows) - -SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM - (VALUES (10::numeric), (1000::numeric), (1000000::numeric), - (1000000000::numeric), (1000000000000::numeric), - (1000000000000000::numeric), - (10.5::numeric), (1000.5::numeric), (1000000.5::numeric), - (1000000000.5::numeric), (1000000000000.5::numeric), - (1000000000000000.5::numeric)) x(size); - size | pg_size_pretty | pg_size_pretty ---------------------+----------------+---------------- - 10 | 10 bytes | -10 bytes - 1000 | 1000 bytes | -1000 bytes - 1000000 | 977 kB | -977 kB - 1000000000 | 954 MB | -954 MB - 1000000000000 | 931 GB | -931 GB - 1000000000000000 | 909 TB | -909 TB - 10.5 | 10.5 bytes | -10.5 bytes - 1000.5 | 1000.5 bytes | -1000.5 bytes - 1000000.5 | 977 kB | -977 kB - 1000000000.5 | 954 MB | -954 MB - 1000000000000.5 | 931 GB | -931 GB - 1000000000000000.5 | 909 TB | -909 TB -(12 rows) - --- test where units change up -SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM - (VALUES (10239::bigint), (10240::bigint), - (10485247::bigint), (10485248::bigint), - (10736893951::bigint), (10736893952::bigint), - (10994579406847::bigint), (10994579406848::bigint), - (11258449312612351::bigint), (11258449312612352::bigint)) x(size); - size | pg_size_pretty | pg_size_pretty --------------------+----------------+---------------- - 10239 | 10239 bytes | -10239 bytes - 10240 | 10 kB | -10 kB - 10485247 | 10239 kB | -10239 kB - 10485248 | 10 MB | -10 MB - 10736893951 | 10239 MB | -10239 MB - 10736893952 | 10 GB | -10 GB - 10994579406847 | 10239 GB | -10239 GB - 10994579406848 | 10 TB | -10 TB - 11258449312612351 | 10239 TB | -10239 TB - 11258449312612352 | 10 PB | -10 PB -(10 rows) - -SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM - (VALUES (10239::numeric), (10240::numeric), - (10485247::numeric), (10485248::numeric), - (10736893951::numeric), (10736893952::numeric), - (10994579406847::numeric), (10994579406848::numeric), - (11258449312612351::numeric), (11258449312612352::numeric), - (11528652096115048447::numeric), (11528652096115048448::numeric)) x(size); - size | pg_size_pretty | pg_size_pretty -----------------------+----------------+---------------- - 10239 | 10239 bytes | -10239 bytes - 10240 | 10 kB | -10 kB - 10485247 | 10239 kB | -10239 kB - 10485248 | 10 MB | -10 MB - 10736893951 | 10239 MB | -10239 MB - 10736893952 | 10 GB | -10 GB - 10994579406847 | 10239 GB | -10239 GB - 10994579406848 | 10 TB | -10 TB - 11258449312612351 | 10239 TB | -10239 TB - 11258449312612352 | 10 PB | -10 PB - 11528652096115048447 | 10239 PB | -10239 PB - 11528652096115048448 | 10240 PB | -10240 PB -(12 rows) - --- pg_size_bytes() tests -SELECT size, pg_size_bytes(size) FROM - (VALUES ('1'), ('123bytes'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '), - ('1TB'), ('3000 TB'), ('1e6 MB'), ('99 PB')) x(size); - size | pg_size_bytes -----------+-------------------- - 1 | 1 - 123bytes | 123 - 1kB | 1024 - 1MB | 1048576 - 1 GB | 1073741824 - 1.5 GB | 1610612736 - 1TB | 1099511627776 - 3000 TB | 3298534883328000 - 1e6 MB | 1048576000000 - 99 PB | 111464090777419776 -(10 rows) - --- case-insensitive units are supported -SELECT size, pg_size_bytes(size) FROM - (VALUES ('1'), ('123bYteS'), ('1kb'), ('1mb'), (' 1 Gb'), ('1.5 gB '), - ('1tb'), ('3000 tb'), ('1e6 mb'), ('99 pb')) x(size); - size | pg_size_bytes -----------+-------------------- - 1 | 1 - 123bYteS | 123 - 1kb | 1024 - 1mb | 1048576 - 1 Gb | 1073741824 - 1.5 gB | 1610612736 - 1tb | 1099511627776 - 3000 tb | 3298534883328000 - 1e6 mb | 1048576000000 - 99 pb | 111464090777419776 -(10 rows) - --- negative numbers are supported -SELECT size, pg_size_bytes(size) FROM - (VALUES ('-1'), ('-123bytes'), ('-1kb'), ('-1mb'), (' -1 Gb'), ('-1.5 gB '), - ('-1tb'), ('-3000 TB'), ('-10e-1 MB'), ('-99 PB')) x(size); - size | pg_size_bytes ------------+--------------------- - -1 | -1 - -123bytes | -123 - -1kb | -1024 - -1mb | -1048576 - -1 Gb | -1073741824 - -1.5 gB | -1610612736 - -1tb | -1099511627776 - -3000 TB | -3298534883328000 - -10e-1 MB | -1048576 - -99 PB | -111464090777419776 -(10 rows) - --- different cases with allowed points -SELECT size, pg_size_bytes(size) FROM - (VALUES ('-1.'), ('-1.kb'), ('-1. kb'), ('-0. gb'), - ('-.1'), ('-.1kb'), ('-.1 kb'), ('-.0 gb')) x(size); - size | pg_size_bytes ---------+--------------- - -1. | -1 - -1.kb | -1024 - -1. kb | -1024 - -0. gb | 0 - -.1 | 0 - -.1kb | -102 - -.1 kb | -102 - -.0 gb | 0 -(8 rows) - --- invalid inputs -SELECT pg_size_bytes('1 AB'); -ERROR: invalid size: "1 AB" -DETAIL: Invalid size unit: "AB". -HINT: Valid units are "bytes", "kB", "MB", "GB", "TB", and "PB". -SELECT pg_size_bytes('1 AB A'); -ERROR: invalid size: "1 AB A" -DETAIL: Invalid size unit: "AB A". -HINT: Valid units are "bytes", "kB", "MB", "GB", "TB", and "PB". -SELECT pg_size_bytes('1 AB A '); -ERROR: invalid size: "1 AB A " -DETAIL: Invalid size unit: "AB A". -HINT: Valid units are "bytes", "kB", "MB", "GB", "TB", and "PB". -SELECT pg_size_bytes('9223372036854775807.9'); -ERROR: bigint out of range -SELECT pg_size_bytes('1e100'); -ERROR: bigint out of range -SELECT pg_size_bytes('1e1000000000000000000'); -ERROR: value overflows numeric format -SELECT pg_size_bytes('1 byte'); -- the singular "byte" is not supported -ERROR: invalid size: "1 byte" -DETAIL: Invalid size unit: "byte". -HINT: Valid units are "bytes", "kB", "MB", "GB", "TB", and "PB". -SELECT pg_size_bytes(''); -ERROR: invalid size: "" -SELECT pg_size_bytes('kb'); -ERROR: invalid size: "kb" -SELECT pg_size_bytes('..'); -ERROR: invalid size: ".." -SELECT pg_size_bytes('-.'); -ERROR: invalid size: "-." -SELECT pg_size_bytes('-.kb'); -ERROR: invalid size: "-.kb" -SELECT pg_size_bytes('-. kb'); -ERROR: invalid size: "-. kb" -SELECT pg_size_bytes('.+912'); -ERROR: invalid size: ".+912" -SELECT pg_size_bytes('+912+ kB'); -ERROR: invalid size: "+912+ kB" -DETAIL: Invalid size unit: "+ kB". -HINT: Valid units are "bytes", "kB", "MB", "GB", "TB", and "PB". -SELECT pg_size_bytes('++123 kB'); -ERROR: invalid size: "++123 kB" +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/misc_functions.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/misc_functions.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/misc_functions.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/misc_functions.out 2021-10-30 22:04:15 -0600 @@ -1,276 +1,2 @@ --- --- num_nulls() --- -SELECT num_nonnulls(NULL); - num_nonnulls --------------- - 0 -(1 row) - -SELECT num_nonnulls('1'); - num_nonnulls --------------- - 1 -(1 row) - -SELECT num_nonnulls(NULL::text); - num_nonnulls --------------- - 0 -(1 row) - -SELECT num_nonnulls(NULL::text, NULL::int); - num_nonnulls --------------- - 0 -(1 row) - -SELECT num_nonnulls(1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); - num_nonnulls --------------- - 4 -(1 row) - -SELECT num_nonnulls(VARIADIC '{1,2,NULL,3}'::int[]); - num_nonnulls --------------- - 3 -(1 row) - -SELECT num_nonnulls(VARIADIC '{"1","2","3","4"}'::text[]); - num_nonnulls --------------- - 4 -(1 row) - -SELECT num_nonnulls(VARIADIC ARRAY(SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); - num_nonnulls --------------- - 99 -(1 row) - -SELECT num_nulls(NULL); - num_nulls ------------ - 1 -(1 row) - -SELECT num_nulls('1'); - num_nulls ------------ - 0 -(1 row) - -SELECT num_nulls(NULL::text); - num_nulls ------------ - 1 -(1 row) - -SELECT num_nulls(NULL::text, NULL::int); - num_nulls ------------ - 2 -(1 row) - -SELECT num_nulls(1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); - num_nulls ------------ - 3 -(1 row) - -SELECT num_nulls(VARIADIC '{1,2,NULL,3}'::int[]); - num_nulls ------------ - 1 -(1 row) - -SELECT num_nulls(VARIADIC '{"1","2","3","4"}'::text[]); - num_nulls ------------ - 0 -(1 row) - -SELECT num_nulls(VARIADIC ARRAY(SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); - num_nulls ------------ - 1 -(1 row) - --- special cases -SELECT num_nonnulls(VARIADIC NULL::text[]); - num_nonnulls --------------- - -(1 row) - -SELECT num_nonnulls(VARIADIC '{}'::int[]); - num_nonnulls --------------- - 0 -(1 row) - -SELECT num_nulls(VARIADIC NULL::text[]); - num_nulls ------------ - -(1 row) - -SELECT num_nulls(VARIADIC '{}'::int[]); - num_nulls ------------ - 0 -(1 row) - --- should fail, one or more arguments is required -SELECT num_nonnulls(); -ERROR: function num_nonnulls() does not exist -LINE 1: SELECT num_nonnulls(); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT num_nulls(); -ERROR: function num_nulls() does not exist -LINE 1: SELECT num_nulls(); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. --- --- pg_log_backend_memory_contexts() --- --- Memory contexts are logged and they are not returned to the function. --- Furthermore, their contents can vary depending on the timing. However, --- we can at least verify that the code doesn't fail. --- -SELECT * FROM pg_log_backend_memory_contexts(pg_backend_pid()); - pg_log_backend_memory_contexts --------------------------------- - t -(1 row) - --- --- Test some built-in SRFs --- --- The outputs of these are variable, so we can't just print their results --- directly, but we can at least verify that the code doesn't fail. --- -select setting as segsize -from pg_settings where name = 'wal_segment_size' -\gset -select count(*) > 0 as ok from pg_ls_waldir(); - ok ----- - t -(1 row) - --- Test ProjectSet as well as FunctionScan -select count(*) > 0 as ok from (select pg_ls_waldir()) ss; - ok ----- - t -(1 row) - --- Test not-run-to-completion cases. -select * from pg_ls_waldir() limit 0; - name | size | modification -------+------+-------------- -(0 rows) - -select count(*) > 0 as ok from (select * from pg_ls_waldir() limit 1) ss; - ok ----- - t -(1 row) - -select (w).size = :segsize as ok -from (select pg_ls_waldir() w) ss where length((w).name) = 24 limit 1; - ok ----- - t -(1 row) - -select count(*) >= 0 as ok from pg_ls_archive_statusdir(); - ok ----- - t -(1 row) - -select * from (select pg_ls_dir('.') a) a where a = 'base' limit 1; - a ------- - base -(1 row) - -select * from (select (pg_timezone_names()).name) ptn where name='UTC' limit 1; - name ------- - UTC -(1 row) - -select count(*) > 0 from - (select pg_tablespace_databases(oid) as pts from pg_tablespace - where spcname = 'pg_default') pts - join pg_database db on pts.pts = db.oid; - ?column? ----------- - t -(1 row) - --- --- Test adding a support function to a subject function --- -CREATE FUNCTION my_int_eq(int, int) RETURNS bool - LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE - AS $$int4eq$$; --- By default, planner does not think that's selective -EXPLAIN (COSTS OFF) -SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 -WHERE my_int_eq(a.unique2, 42); - QUERY PLAN ----------------------------------------------- - Hash Join - Hash Cond: (b.unique1 = a.unique1) - -> Seq Scan on tenk1 b - -> Hash - -> Seq Scan on tenk1 a - Filter: my_int_eq(unique2, 42) -(6 rows) - --- With support function that knows it's int4eq, we get a different plan -ALTER FUNCTION my_int_eq(int, int) SUPPORT test_support_func; -EXPLAIN (COSTS OFF) -SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 -WHERE my_int_eq(a.unique2, 42); - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on tenk1 a - Filter: my_int_eq(unique2, 42) - -> Index Scan using tenk1_unique1 on tenk1 b - Index Cond: (unique1 = a.unique1) -(5 rows) - --- Also test non-default rowcount estimate -CREATE FUNCTION my_gen_series(int, int) RETURNS SETOF integer - LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE - AS $$generate_series_int4$$ - SUPPORT test_support_func; -EXPLAIN (COSTS OFF) -SELECT * FROM tenk1 a JOIN my_gen_series(1,1000) g ON a.unique1 = g; - QUERY PLAN ----------------------------------------- - Hash Join - Hash Cond: (g.g = a.unique1) - -> Function Scan on my_gen_series g - -> Hash - -> Seq Scan on tenk1 a -(5 rows) - -EXPLAIN (COSTS OFF) -SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Function Scan on my_gen_series g - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = g.g) -(4 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sysviews.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sysviews.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sysviews.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sysviews.out 2021-10-30 22:04:15 -0600 @@ -1,152 +1,2 @@ --- --- Test assorted system views --- --- This test is mainly meant to provide some code coverage for the --- set-returning functions that underlie certain system views. --- The output of most of these functions is very environment-dependent, --- so our ability to test with fixed expected output is pretty limited; --- but even a trivial check of count(*) will exercise the normal code path --- through the SRF. -select count(*) >= 0 as ok from pg_available_extension_versions; - ok ----- - t -(1 row) - -select count(*) >= 0 as ok from pg_available_extensions; - ok ----- - t -(1 row) - --- The entire output of pg_backend_memory_contexts is not stable, --- we test only the existance and basic condition of TopMemoryContext. -select name, ident, parent, level, total_bytes >= free_bytes - from pg_backend_memory_contexts where level = 0; - name | ident | parent | level | ?column? -------------------+-------+--------+-------+---------- - TopMemoryContext | | | 0 | t -(1 row) - --- At introduction, pg_config had 23 entries; it may grow -select count(*) > 20 as ok from pg_config; - ok ----- - t -(1 row) - --- We expect no cursors in this test; see also portals.sql -select count(*) = 0 as ok from pg_cursors; - ok ----- - t -(1 row) - -select count(*) >= 0 as ok from pg_file_settings; - ok ----- - t -(1 row) - --- There will surely be at least one rule -select count(*) > 0 as ok from pg_hba_file_rules; - ok ----- - t -(1 row) - --- There will surely be at least one active lock -select count(*) > 0 as ok from pg_locks; - ok ----- - t -(1 row) - --- We expect no prepared statements in this test; see also prepare.sql -select count(*) = 0 as ok from pg_prepared_statements; - ok ----- - t -(1 row) - --- See also prepared_xacts.sql -select count(*) >= 0 as ok from pg_prepared_xacts; - ok ----- - t -(1 row) - --- There must be only one record -select count(*) = 1 as ok from pg_stat_wal; - ok ----- - t -(1 row) - --- We expect no walreceiver running in this test -select count(*) = 0 as ok from pg_stat_wal_receiver; - ok ----- - t -(1 row) - --- This is to record the prevailing planner enable_foo settings during --- a regression test run. -select name, setting from pg_settings where name like 'enable%'; - name | setting ---------------------------------+--------- - enable_async_append | on - enable_bitmapscan | on - enable_gathermerge | on - enable_hashagg | on - enable_hashjoin | on - enable_incremental_sort | on - enable_indexonlyscan | on - enable_indexscan | on - enable_material | on - enable_memoize | on - enable_mergejoin | on - enable_nestloop | on - enable_parallel_append | on - enable_parallel_hash | on - enable_partition_pruning | on - enable_partitionwise_aggregate | off - enable_partitionwise_join | off - enable_seqscan | on - enable_sort | on - enable_tidscan | on -(20 rows) - --- Test that the pg_timezone_names and pg_timezone_abbrevs views are --- more-or-less working. We can't test their contents in any great detail --- without the outputs changing anytime IANA updates the underlying data, --- but it seems reasonable to expect at least one entry per major meridian. --- (At the time of writing, the actual counts are around 38 because of --- zones using fractional GMT offsets, so this is a pretty loose test.) -select count(distinct utc_offset) >= 24 as ok from pg_timezone_names; - ok ----- - t -(1 row) - -select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; - ok ----- - t -(1 row) - --- Let's check the non-default timezone abbreviation sets, too -set timezone_abbreviations = 'Australia'; -select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; - ok ----- - t -(1 row) - -set timezone_abbreviations = 'India'; -select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; - ok ----- - t -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tsrf.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tsrf.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tsrf.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tsrf.out 2021-10-30 22:04:15 -0600 @@ -1,712 +1,2 @@ --- --- tsrf - targetlist set returning function tests --- --- simple srf -SELECT generate_series(1, 3); - generate_series ------------------ - 1 - 2 - 3 -(3 rows) - --- parallel iteration -SELECT generate_series(1, 3), generate_series(3,5); - generate_series | generate_series ------------------+----------------- - 1 | 3 - 2 | 4 - 3 | 5 -(3 rows) - --- parallel iteration, different number of rows -SELECT generate_series(1, 2), generate_series(1,4); - generate_series | generate_series ------------------+----------------- - 1 | 1 - 2 | 2 - | 3 - | 4 -(4 rows) - --- srf, with SRF argument -SELECT generate_series(1, generate_series(1, 3)); - generate_series ------------------ - 1 - 1 - 2 - 1 - 2 - 3 -(6 rows) - --- but we've traditionally rejected the same in FROM -SELECT * FROM generate_series(1, generate_series(1, 3)); -ERROR: set-returning functions must appear at top level of FROM -LINE 1: SELECT * FROM generate_series(1, generate_series(1, 3)); - ^ --- srf, with two SRF arguments -SELECT generate_series(generate_series(1,3), generate_series(2, 4)); - generate_series ------------------ - 1 - 2 - 2 - 3 - 3 - 4 -(6 rows) - --- check proper nesting of SRFs in different expressions -explain (verbose, costs off) -SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); - QUERY PLAN --------------------------------------------------------------------------------- - ProjectSet - Output: generate_series(1, (generate_series(1, 3))), (generate_series(2, 4)) - -> ProjectSet - Output: generate_series(1, 3), generate_series(2, 4) - -> Result -(5 rows) - -SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); - generate_series | generate_series ------------------+----------------- - 1 | 2 - 1 | 3 - 2 | 3 - 1 | 4 - 2 | 4 - 3 | 4 -(6 rows) - -CREATE TABLE few(id int, dataa text, datab text); -INSERT INTO few VALUES(1, 'a', 'foo'),(2, 'a', 'bar'),(3, 'b', 'bar'); --- SRF with a provably-dummy relation -explain (verbose, costs off) -SELECT unnest(ARRAY[1, 2]) FROM few WHERE false; - QUERY PLAN --------------------------------------- - ProjectSet - Output: unnest('{1,2}'::integer[]) - -> Result - One-Time Filter: false -(4 rows) - -SELECT unnest(ARRAY[1, 2]) FROM few WHERE false; - unnest --------- -(0 rows) - --- SRF shouldn't prevent upper query from recognizing lower as dummy -explain (verbose, costs off) -SELECT * FROM few f1, - (SELECT unnest(ARRAY[1,2]) FROM few f2 WHERE false OFFSET 0) ss; - QUERY PLAN ------------------------------------------------- - Result - Output: f1.id, f1.dataa, f1.datab, ss.unnest - One-Time Filter: false -(3 rows) - -SELECT * FROM few f1, - (SELECT unnest(ARRAY[1,2]) FROM few f2 WHERE false OFFSET 0) ss; - id | dataa | datab | unnest -----+-------+-------+-------- -(0 rows) - --- SRF output order of sorting is maintained, if SRF is not referenced -SELECT few.id, generate_series(1,3) g FROM few ORDER BY id DESC; - id | g -----+--- - 3 | 1 - 3 | 2 - 3 | 3 - 2 | 1 - 2 | 2 - 2 | 3 - 1 | 1 - 1 | 2 - 1 | 3 -(9 rows) - --- but SRFs can be referenced in sort -SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, g DESC; - id | g -----+--- - 1 | 3 - 1 | 2 - 1 | 1 - 2 | 3 - 2 | 2 - 2 | 1 - 3 | 3 - 3 | 2 - 3 | 1 -(9 rows) - -SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, generate_series(1,3) DESC; - id | g -----+--- - 1 | 3 - 1 | 2 - 1 | 1 - 2 | 3 - 2 | 2 - 2 | 1 - 3 | 3 - 3 | 2 - 3 | 1 -(9 rows) - --- it's weird to have ORDER BYs that increase the number of results -SELECT few.id FROM few ORDER BY id, generate_series(1,3) DESC; - id ----- - 1 - 1 - 1 - 2 - 2 - 2 - 3 - 3 - 3 -(9 rows) - --- SRFs are computed after aggregation -SET enable_hashagg TO 0; -- stable output order -SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa; - dataa | count | min | max | unnest --------+-------+-----+-----+-------- - a | 1 | 1 | 1 | 1 - a | 1 | 1 | 1 | 1 - a | 1 | 1 | 1 | 3 -(3 rows) - --- unless referenced in GROUP BY clause -SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, unnest('{1,1,3}'::int[]); - dataa | count | min | max | unnest --------+-------+-----+-----+-------- - a | 2 | 1 | 1 | 1 - a | 1 | 1 | 1 | 3 -(2 rows) - -SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, 5; - dataa | count | min | max | unnest --------+-------+-----+-----+-------- - a | 2 | 1 | 1 | 1 - a | 1 | 1 | 1 | 3 -(2 rows) - -RESET enable_hashagg; --- check HAVING works when GROUP BY does [not] reference SRF output -SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1 HAVING count(*) > 1; - dataa | generate_series | count --------+-----------------+------- - a | 1 | 2 -(1 row) - -SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1, 2 HAVING count(*) > 1; - dataa | generate_series | count --------+-----------------+------- - a | 1 | 2 -(1 row) - --- it's weird to have GROUP BYs that increase the number of results -SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa ORDER BY 2; - dataa | count --------+------- - a | 2 -(1 row) - -SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa, unnest('{1,1,3}'::int[]) ORDER BY 2; - dataa | count --------+------- - a | 2 - a | 4 -(2 rows) - --- SRFs are not allowed if they'd need to be conditionally executed -SELECT q1, case when q1 > 0 then generate_series(1,3) else 0 end FROM int8_tbl; -ERROR: set-returning functions are not allowed in CASE -LINE 1: SELECT q1, case when q1 > 0 then generate_series(1,3) else 0... - ^ -HINT: You might be able to move the set-returning function into a LATERAL FROM item. -SELECT q1, coalesce(generate_series(1,3), 0) FROM int8_tbl; -ERROR: set-returning functions are not allowed in COALESCE -LINE 1: SELECT q1, coalesce(generate_series(1,3), 0) FROM int8_tbl; - ^ -HINT: You might be able to move the set-returning function into a LATERAL FROM item. --- SRFs are not allowed in aggregate arguments -SELECT min(generate_series(1, 3)) FROM few; -ERROR: aggregate function calls cannot contain set-returning function calls -LINE 1: SELECT min(generate_series(1, 3)) FROM few; - ^ -HINT: You might be able to move the set-returning function into a LATERAL FROM item. --- ... unless they're within a sub-select -SELECT sum((3 = ANY(SELECT generate_series(1,4)))::int); - sum ------ - 1 -(1 row) - -SELECT sum((3 = ANY(SELECT lag(x) over(order by x) - FROM generate_series(1,4) x))::int); - sum ------ - 1 -(1 row) - --- SRFs are not allowed in window function arguments, either -SELECT min(generate_series(1, 3)) OVER() FROM few; -ERROR: window function calls cannot contain set-returning function calls -LINE 1: SELECT min(generate_series(1, 3)) OVER() FROM few; - ^ -HINT: You might be able to move the set-returning function into a LATERAL FROM item. --- SRFs are normally computed after window functions -SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few; - id | lag | count | generate_series -----+-----+-------+----------------- - 1 | | 3 | 1 - 1 | | 3 | 2 - 1 | | 3 | 3 - 2 | 1 | 3 | 1 - 2 | 1 | 3 | 2 - 2 | 1 | 3 | 3 - 3 | 2 | 3 | 1 - 3 | 2 | 3 | 2 - 3 | 2 | 3 | 3 -(9 rows) - --- unless referencing SRFs -SELECT SUM(count(*)) OVER(PARTITION BY generate_series(1,3) ORDER BY generate_series(1,3)), generate_series(1,3) g FROM few GROUP BY g; - sum | g ------+--- - 3 | 1 - 3 | 2 - 3 | 3 -(3 rows) - --- sorting + grouping -SELECT few.dataa, count(*), min(id), max(id), generate_series(1,3) FROM few GROUP BY few.dataa ORDER BY 5, 1; - dataa | count | min | max | generate_series --------+-------+-----+-----+----------------- - a | 2 | 1 | 2 | 1 - b | 1 | 3 | 3 | 1 - a | 2 | 1 | 2 | 2 - b | 1 | 3 | 3 | 2 - a | 2 | 1 | 2 | 3 - b | 1 | 3 | 3 | 3 -(6 rows) - --- grouping sets are a bit special, they produce NULLs in columns not actually NULL -set enable_hashagg = false; -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab); - dataa | b | g | count --------+-----+---+------- - a | bar | 1 | 1 - a | bar | 2 | 1 - a | foo | 1 | 1 - a | foo | 2 | 1 - a | | 1 | 2 - a | | 2 | 2 - b | bar | 1 | 1 - b | bar | 2 | 1 - b | | 1 | 1 - b | | 2 | 1 - | | 1 | 3 - | | 2 | 3 - | bar | 1 | 2 - | bar | 2 | 2 - | foo | 1 | 1 - | foo | 2 | 1 -(16 rows) - -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY dataa; - dataa | b | g | count --------+-----+---+------- - a | bar | 1 | 1 - a | bar | 2 | 1 - a | foo | 1 | 1 - a | foo | 2 | 1 - a | | 1 | 2 - a | | 2 | 2 - b | bar | 1 | 1 - b | bar | 2 | 1 - b | | 1 | 1 - b | | 2 | 1 - | | 1 | 3 - | | 2 | 3 - | bar | 1 | 2 - | bar | 2 | 2 - | foo | 1 | 1 - | foo | 2 | 1 -(16 rows) - -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY g; - dataa | b | g | count --------+-----+---+------- - a | bar | 1 | 1 - a | foo | 1 | 1 - a | | 1 | 2 - b | bar | 1 | 1 - b | | 1 | 1 - | | 1 | 3 - | bar | 1 | 2 - | foo | 1 | 1 - | foo | 2 | 1 - a | bar | 2 | 1 - b | | 2 | 1 - a | foo | 2 | 1 - | bar | 2 | 2 - a | | 2 | 2 - | | 2 | 3 - b | bar | 2 | 1 -(16 rows) - -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g); - dataa | b | g | count --------+-----+---+------- - a | bar | 1 | 1 - a | bar | 2 | 1 - a | bar | | 2 - a | foo | 1 | 1 - a | foo | 2 | 1 - a | foo | | 2 - a | | | 4 - b | bar | 1 | 1 - b | bar | 2 | 1 - b | bar | | 2 - b | | | 2 - | | | 6 - | bar | 1 | 2 - | bar | 2 | 2 - | bar | | 4 - | foo | 1 | 1 - | foo | 2 | 1 - | foo | | 2 - a | | 1 | 2 - b | | 1 | 1 - | | 1 | 3 - a | | 2 | 2 - b | | 2 | 1 - | | 2 | 3 -(24 rows) - -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY dataa; - dataa | b | g | count --------+-----+---+------- - a | foo | | 2 - a | | | 4 - a | | 2 | 2 - a | bar | 1 | 1 - a | bar | 2 | 1 - a | bar | | 2 - a | foo | 1 | 1 - a | foo | 2 | 1 - a | | 1 | 2 - b | bar | 1 | 1 - b | | | 2 - b | | 1 | 1 - b | bar | 2 | 1 - b | bar | | 2 - b | | 2 | 1 - | | 2 | 3 - | | | 6 - | bar | 1 | 2 - | bar | 2 | 2 - | bar | | 4 - | foo | 1 | 1 - | foo | 2 | 1 - | foo | | 2 - | | 1 | 3 -(24 rows) - -SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY g; - dataa | b | g | count --------+-----+---+------- - a | bar | 1 | 1 - a | foo | 1 | 1 - b | bar | 1 | 1 - | bar | 1 | 2 - | foo | 1 | 1 - a | | 1 | 2 - b | | 1 | 1 - | | 1 | 3 - a | | 2 | 2 - b | | 2 | 1 - | bar | 2 | 2 - | | 2 | 3 - | foo | 2 | 1 - a | bar | 2 | 1 - a | foo | 2 | 1 - b | bar | 2 | 1 - a | | | 4 - b | bar | | 2 - b | | | 2 - | | | 6 - a | foo | | 2 - a | bar | | 2 - | bar | | 4 - | foo | | 2 -(24 rows) - -reset enable_hashagg; --- case with degenerate ORDER BY -explain (verbose, costs off) -select 'foo' as f, generate_series(1,2) as g from few order by 1; - QUERY PLAN ----------------------------------------------- - ProjectSet - Output: 'foo'::text, generate_series(1, 2) - -> Seq Scan on public.few - Output: id, dataa, datab -(4 rows) - -select 'foo' as f, generate_series(1,2) as g from few order by 1; - f | g ------+--- - foo | 1 - foo | 2 - foo | 1 - foo | 2 - foo | 1 - foo | 2 -(6 rows) - --- data modification -CREATE TABLE fewmore AS SELECT generate_series(1,3) AS data; -INSERT INTO fewmore VALUES(generate_series(4,5)); -SELECT * FROM fewmore; - data ------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - --- SRFs are not allowed in UPDATE (they once were, but it was nonsense) -UPDATE fewmore SET data = generate_series(4,9); -ERROR: set-returning functions are not allowed in UPDATE -LINE 1: UPDATE fewmore SET data = generate_series(4,9); - ^ --- SRFs are not allowed in RETURNING -INSERT INTO fewmore VALUES(1) RETURNING generate_series(1,3); -ERROR: set-returning functions are not allowed in RETURNING -LINE 1: INSERT INTO fewmore VALUES(1) RETURNING generate_series(1,3)... - ^ --- nor standalone VALUES (but surely this is a bug?) -VALUES(1, generate_series(1,2)); -ERROR: set-returning functions are not allowed in VALUES -LINE 1: VALUES(1, generate_series(1,2)); - ^ --- We allow tSRFs that are not at top level -SELECT int4mul(generate_series(1,2), 10); - int4mul ---------- - 10 - 20 -(2 rows) - -SELECT generate_series(1,3) IS DISTINCT FROM 2; - ?column? ----------- - t - f - t -(3 rows) - --- but SRFs in function RTEs must be at top level (annoying restriction) -SELECT * FROM int4mul(generate_series(1,2), 10); -ERROR: set-returning functions must appear at top level of FROM -LINE 1: SELECT * FROM int4mul(generate_series(1,2), 10); - ^ --- DISTINCT ON is evaluated before tSRF evaluation if SRF is not --- referenced either in ORDER BY or in the DISTINCT ON list. The ORDER --- BY reference can be implicitly generated, if there's no other ORDER BY. --- implicit reference (via implicit ORDER) to all columns -SELECT DISTINCT ON (a) a, b, generate_series(1,3) g -FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b); - a | b | g ----+---+--- - 1 | 1 | 1 - 3 | 2 | 1 - 5 | 3 | 1 -(3 rows) - --- unreferenced in DISTINCT ON or ORDER BY -SELECT DISTINCT ON (a) a, b, generate_series(1,3) g -FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) -ORDER BY a, b DESC; - a | b | g ----+---+--- - 1 | 4 | 1 - 1 | 4 | 2 - 1 | 4 | 3 - 3 | 2 | 1 - 3 | 2 | 2 - 3 | 2 | 3 - 5 | 3 | 1 - 5 | 3 | 2 - 5 | 3 | 3 -(9 rows) - --- referenced in ORDER BY -SELECT DISTINCT ON (a) a, b, generate_series(1,3) g -FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) -ORDER BY a, b DESC, g DESC; - a | b | g ----+---+--- - 1 | 4 | 3 - 3 | 2 | 3 - 5 | 3 | 3 -(3 rows) - --- referenced in ORDER BY and DISTINCT ON -SELECT DISTINCT ON (a, b, g) a, b, generate_series(1,3) g -FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) -ORDER BY a, b DESC, g DESC; - a | b | g ----+---+--- - 1 | 4 | 3 - 1 | 4 | 2 - 1 | 4 | 1 - 1 | 1 | 3 - 1 | 1 | 2 - 1 | 1 | 1 - 3 | 2 | 3 - 3 | 2 | 2 - 3 | 2 | 1 - 3 | 1 | 3 - 3 | 1 | 2 - 3 | 1 | 1 - 5 | 3 | 3 - 5 | 3 | 2 - 5 | 3 | 1 - 5 | 1 | 3 - 5 | 1 | 2 - 5 | 1 | 1 -(18 rows) - --- only SRF mentioned in DISTINCT ON -SELECT DISTINCT ON (g) a, b, generate_series(1,3) g -FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b); - a | b | g ----+---+--- - 3 | 2 | 1 - 5 | 1 | 2 - 3 | 1 | 3 -(3 rows) - --- LIMIT / OFFSET is evaluated after SRF evaluation -SELECT a, generate_series(1,2) FROM (VALUES(1),(2),(3)) r(a) LIMIT 2 OFFSET 2; - a | generate_series ----+----------------- - 2 | 1 - 2 | 2 -(2 rows) - --- SRFs are not allowed in LIMIT. -SELECT 1 LIMIT generate_series(1,3); -ERROR: set-returning functions are not allowed in LIMIT -LINE 1: SELECT 1 LIMIT generate_series(1,3); - ^ --- tSRF in correlated subquery, referencing table outside -SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET few.id) FROM few; - generate_series ------------------ - 2 - 3 - -(3 rows) - --- tSRF in correlated subquery, referencing SRF outside -SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET g.i) FROM generate_series(0,3) g(i); - generate_series ------------------ - 1 - 2 - 3 - -(4 rows) - --- Operators can return sets too -CREATE OPERATOR |@| (PROCEDURE = unnest, RIGHTARG = ANYARRAY); -SELECT |@|ARRAY[1,2,3]; - ?column? ----------- - 1 - 2 - 3 -(3 rows) - --- Some fun cases involving duplicate SRF calls -explain (verbose, costs off) -select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1; - QUERY PLAN ------------------------------------------------------------------- - Result - Output: (generate_series(1, 3)), ((generate_series(1, 3)) + 1) - -> ProjectSet - Output: generate_series(1, 3) - -> Result -(5 rows) - -select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1; - x | xp1 ----+----- - 1 | 2 - 2 | 3 - 3 | 4 -(3 rows) - -explain (verbose, costs off) -select generate_series(1,3)+1 order by generate_series(1,3); - QUERY PLAN ------------------------------------------------------------------------- - Sort - Output: (((generate_series(1, 3)) + 1)), (generate_series(1, 3)) - Sort Key: (generate_series(1, 3)) - -> Result - Output: ((generate_series(1, 3)) + 1), (generate_series(1, 3)) - -> ProjectSet - Output: generate_series(1, 3) - -> Result -(8 rows) - -select generate_series(1,3)+1 order by generate_series(1,3); - ?column? ----------- - 2 - 3 - 4 -(3 rows) - --- Check that SRFs of same nesting level run in lockstep -explain (verbose, costs off) -select generate_series(1,3) as x, generate_series(3,6) + 1 as y; - QUERY PLAN ------------------------------------------------------------------- - Result - Output: (generate_series(1, 3)), ((generate_series(3, 6)) + 1) - -> ProjectSet - Output: generate_series(1, 3), generate_series(3, 6) - -> Result -(5 rows) - -select generate_series(1,3) as x, generate_series(3,6) + 1 as y; - x | y ----+--- - 1 | 4 - 2 | 5 - 3 | 6 - | 7 -(4 rows) - --- Clean up -DROP TABLE few; -DROP TABLE fewmore; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tid.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tid.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tid.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tid.out 2021-10-30 22:04:15 -0600 @@ -1,76 +1,2 @@ --- tests for functions related to TID handling -CREATE TABLE tid_tab (a int); --- min() and max() for TIDs -INSERT INTO tid_tab VALUES (1), (2); -SELECT min(ctid) FROM tid_tab; - min -------- - (0,1) -(1 row) - -SELECT max(ctid) FROM tid_tab; - max -------- - (0,2) -(1 row) - -TRUNCATE tid_tab; --- Tests for currtid2() with various relation kinds --- Materialized view -CREATE MATERIALIZED VIEW tid_matview AS SELECT a FROM tid_tab; -SELECT currtid2('tid_matview'::text, '(0,1)'::tid); -- fails -ERROR: tid (0, 1) is not valid for relation "tid_matview" -INSERT INTO tid_tab VALUES (1); -REFRESH MATERIALIZED VIEW tid_matview; -SELECT currtid2('tid_matview'::text, '(0,1)'::tid); -- ok - currtid2 ----------- - (0,1) -(1 row) - -DROP MATERIALIZED VIEW tid_matview; -TRUNCATE tid_tab; --- Sequence -CREATE SEQUENCE tid_seq; -SELECT currtid2('tid_seq'::text, '(0,1)'::tid); -- ok - currtid2 ----------- - (0,1) -(1 row) - -DROP SEQUENCE tid_seq; --- Index, fails with incorrect relation type -CREATE INDEX tid_ind ON tid_tab(a); -SELECT currtid2('tid_ind'::text, '(0,1)'::tid); -- fails -ERROR: "tid_ind" is an index -DROP INDEX tid_ind; --- Partitioned table, no storage -CREATE TABLE tid_part (a int) PARTITION BY RANGE (a); -SELECT currtid2('tid_part'::text, '(0,1)'::tid); -- fails -ERROR: cannot look at latest visible tid for relation "public.tid_part" -DROP TABLE tid_part; --- Views --- ctid not defined in the view -CREATE VIEW tid_view_no_ctid AS SELECT a FROM tid_tab; -SELECT currtid2('tid_view_no_ctid'::text, '(0,1)'::tid); -- fails -ERROR: currtid cannot handle views with no CTID -DROP VIEW tid_view_no_ctid; --- ctid fetched directly from the source table. -CREATE VIEW tid_view_with_ctid AS SELECT ctid, a FROM tid_tab; -SELECT currtid2('tid_view_with_ctid'::text, '(0,1)'::tid); -- fails -ERROR: tid (0, 1) is not valid for relation "tid_tab" -INSERT INTO tid_tab VALUES (1); -SELECT currtid2('tid_view_with_ctid'::text, '(0,1)'::tid); -- ok - currtid2 ----------- - (0,1) -(1 row) - -DROP VIEW tid_view_with_ctid; -TRUNCATE tid_tab; --- ctid attribute with incorrect data type -CREATE VIEW tid_view_fake_ctid AS SELECT 1 AS ctid, 2 AS a; -SELECT currtid2('tid_view_fake_ctid'::text, '(0,1)'::tid); -- fails -ERROR: ctid isn't of type TID -DROP VIEW tid_view_fake_ctid; -DROP TABLE tid_tab CASCADE; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tidscan.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tidscan.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tidscan.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tidscan.out 2021-10-30 22:04:15 -0600 @@ -1,296 +1,2 @@ --- tests for tidscans -CREATE TABLE tidscan(id integer); --- only insert a few rows, we don't want to spill onto a second table page -INSERT INTO tidscan VALUES (1), (2), (3); --- show ctids -SELECT ctid, * FROM tidscan; - ctid | id --------+---- - (0,1) | 1 - (0,2) | 2 - (0,3) | 3 -(3 rows) - --- ctid equality - implemented as tidscan -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; - QUERY PLAN ------------------------------------ - Tid Scan on tidscan - TID Cond: (ctid = '(0,1)'::tid) -(2 rows) - -SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; - ctid | id --------+---- - (0,1) | 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; - QUERY PLAN ------------------------------------ - Tid Scan on tidscan - TID Cond: ('(0,1)'::tid = ctid) -(2 rows) - -SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; - ctid | id --------+---- - (0,1) | 1 -(1 row) - --- OR'd clauses -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; - QUERY PLAN --------------------------------------------------------------- - Tid Scan on tidscan - TID Cond: ((ctid = '(0,2)'::tid) OR ('(0,1)'::tid = ctid)) -(2 rows) - -SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; - ctid | id --------+---- - (0,1) | 1 - (0,2) | 2 -(2 rows) - --- ctid = ScalarArrayOp - implemented as tidscan -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); - QUERY PLAN -------------------------------------------------------- - Tid Scan on tidscan - TID Cond: (ctid = ANY ('{"(0,1)","(0,2)"}'::tid[])) -(2 rows) - -SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); - ctid | id --------+---- - (0,1) | 1 - (0,2) | 2 -(2 rows) - --- ctid != ScalarArrayOp - can't be implemented as tidscan -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan WHERE ctid != ANY(ARRAY['(0,1)', '(0,2)']::tid[]); - QUERY PLAN ------------------------------------------------------- - Seq Scan on tidscan - Filter: (ctid <> ANY ('{"(0,1)","(0,2)"}'::tid[])) -(2 rows) - -SELECT ctid, * FROM tidscan WHERE ctid != ANY(ARRAY['(0,1)', '(0,2)']::tid[]); - ctid | id --------+---- - (0,1) | 1 - (0,2) | 2 - (0,3) | 3 -(3 rows) - --- tid equality extracted from sub-AND clauses -EXPLAIN (COSTS OFF) -SELECT ctid, * FROM tidscan -WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); - QUERY PLAN --------------------------------------------------------------------------------------------------------------- - Tid Scan on tidscan - TID Cond: ((ctid = ANY ('{"(0,2)","(0,3)"}'::tid[])) OR (ctid = '(0,1)'::tid)) - Filter: (((id = 3) AND (ctid = ANY ('{"(0,2)","(0,3)"}'::tid[]))) OR ((ctid = '(0,1)'::tid) AND (id = 1))) -(3 rows) - -SELECT ctid, * FROM tidscan -WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); - ctid | id --------+---- - (0,1) | 1 - (0,3) | 3 -(2 rows) - --- nestloop-with-inner-tidscan joins on tid -SET enable_hashjoin TO off; -- otherwise hash join might win -EXPLAIN (COSTS OFF) -SELECT t1.ctid, t1.*, t2.ctid, t2.* -FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; - QUERY PLAN ------------------------------------- - Nested Loop - -> Seq Scan on tidscan t1 - Filter: (id = 1) - -> Tid Scan on tidscan t2 - TID Cond: (ctid = t1.ctid) -(5 rows) - -SELECT t1.ctid, t1.*, t2.ctid, t2.* -FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; - ctid | id | ctid | id --------+----+-------+---- - (0,1) | 1 | (0,1) | 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT t1.ctid, t1.*, t2.ctid, t2.* -FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; - QUERY PLAN ------------------------------------- - Nested Loop Left Join - -> Seq Scan on tidscan t1 - Filter: (id = 1) - -> Tid Scan on tidscan t2 - TID Cond: (t1.ctid = ctid) -(5 rows) - -SELECT t1.ctid, t1.*, t2.ctid, t2.* -FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; - ctid | id | ctid | id --------+----+-------+---- - (0,1) | 1 | (0,1) | 1 -(1 row) - -RESET enable_hashjoin; --- exercise backward scan and rewind -BEGIN; -DECLARE c CURSOR FOR -SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); -FETCH ALL FROM c; - ctid | id --------+---- - (0,1) | 1 - (0,2) | 2 -(2 rows) - -FETCH BACKWARD 1 FROM c; - ctid | id --------+---- - (0,2) | 2 -(1 row) - -FETCH FIRST FROM c; - ctid | id --------+---- - (0,1) | 1 -(1 row) - -ROLLBACK; --- tidscan via CURRENT OF -BEGIN; -DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan; -FETCH NEXT FROM c; -- skip one row - ctid | id --------+---- - (0,1) | 1 -(1 row) - -FETCH NEXT FROM c; - ctid | id --------+---- - (0,2) | 2 -(1 row) - --- perform update -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) -UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; - QUERY PLAN ---------------------------------------------------- - Update on tidscan (actual rows=1 loops=1) - -> Tid Scan on tidscan (actual rows=1 loops=1) - TID Cond: CURRENT OF c -(3 rows) - -FETCH NEXT FROM c; - ctid | id --------+---- - (0,3) | 3 -(1 row) - --- perform update -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) -UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; - QUERY PLAN ---------------------------------------------------- - Update on tidscan (actual rows=1 loops=1) - -> Tid Scan on tidscan (actual rows=1 loops=1) - TID Cond: CURRENT OF c -(3 rows) - -SELECT * FROM tidscan; - id ----- - 1 - -2 - -3 -(3 rows) - --- position cursor past any rows -FETCH NEXT FROM c; - ctid | id -------+---- -(0 rows) - --- should error out -EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) -UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; -ERROR: cursor "c" is not positioned on a row -ROLLBACK; --- bulk joins on CTID --- (these plans don't use TID scans, but this still seems like an --- appropriate place for these tests) -EXPLAIN (COSTS OFF) -SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (t1.ctid = t2.ctid) - -> Seq Scan on tenk1 t1 - -> Hash - -> Seq Scan on tenk1 t2 -(6 rows) - -SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; - count -------- - 10000 -(1 row) - -SET enable_hashjoin TO off; -EXPLAIN (COSTS OFF) -SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; - QUERY PLAN ------------------------------------------ - Aggregate - -> Merge Join - Merge Cond: (t1.ctid = t2.ctid) - -> Sort - Sort Key: t1.ctid - -> Seq Scan on tenk1 t1 - -> Sort - Sort Key: t2.ctid - -> Seq Scan on tenk1 t2 -(9 rows) - -SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; - count -------- - 10000 -(1 row) - -RESET enable_hashjoin; --- check predicate lock on CTID -BEGIN ISOLATION LEVEL SERIALIZABLE; -SELECT * FROM tidscan WHERE ctid = '(0,1)'; - id ----- - 1 -(1 row) - --- locktype should be 'tuple' -SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND mode = 'SIReadLock'; - locktype | mode -----------+------------ - tuple | SIReadLock -(1 row) - -ROLLBACK; -DROP TABLE tidscan; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tidrangescan.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tidrangescan.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tidrangescan.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tidrangescan.out 2021-10-30 22:04:15 -0600 @@ -1,300 +1,2 @@ --- tests for tidrangescans -SET enable_seqscan TO off; -CREATE TABLE tidrangescan(id integer, data text); --- empty table -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid < '(1, 0)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid < '(1,0)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid < '(1, 0)'; - ctid ------- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid > '(9, 0)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid > '(9,0)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid > '(9, 0)'; - ctid ------- -(0 rows) - --- insert enough tuples to fill at least two pages -INSERT INTO tidrangescan SELECT i,repeat('x', 100) FROM generate_series(1,200) AS s(i); --- remove all tuples after the 10th tuple on each page. Trying to ensure --- we get the same layout with all CPU architectures and smaller than standard --- page sizes. -DELETE FROM tidrangescan -WHERE substring(ctid::text FROM ',(\d+)\)')::integer > 10 OR substring(ctid::text FROM '\((\d+),')::integer > 2; -VACUUM tidrangescan; --- range scans with upper bound -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid < '(1,0)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid < '(1,0)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid < '(1,0)'; - ctid --------- - (0,1) - (0,2) - (0,3) - (0,4) - (0,5) - (0,6) - (0,7) - (0,8) - (0,9) - (0,10) -(10 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid <= '(1,5)'; - QUERY PLAN ------------------------------------- - Tid Range Scan on tidrangescan - TID Cond: (ctid <= '(1,5)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid <= '(1,5)'; - ctid --------- - (0,1) - (0,2) - (0,3) - (0,4) - (0,5) - (0,6) - (0,7) - (0,8) - (0,9) - (0,10) - (1,1) - (1,2) - (1,3) - (1,4) - (1,5) -(15 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid < '(0,0)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid < '(0,0)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid < '(0,0)'; - ctid ------- -(0 rows) - --- range scans with lower bound -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid > '(2,8)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid > '(2,8)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid > '(2,8)'; - ctid --------- - (2,9) - (2,10) -(2 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE '(2,8)' < ctid; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: ('(2,8)'::tid < ctid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE '(2,8)' < ctid; - ctid --------- - (2,9) - (2,10) -(2 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid >= '(2,8)'; - QUERY PLAN ------------------------------------- - Tid Range Scan on tidrangescan - TID Cond: (ctid >= '(2,8)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid >= '(2,8)'; - ctid --------- - (2,8) - (2,9) - (2,10) -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid >= '(100,0)'; - QUERY PLAN --------------------------------------- - Tid Range Scan on tidrangescan - TID Cond: (ctid >= '(100,0)'::tid) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid >= '(100,0)'; - ctid ------- -(0 rows) - --- range scans with both bounds -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE ctid > '(1,4)' AND '(1,7)' >= ctid; - QUERY PLAN ----------------------------------------------------------------- - Tid Range Scan on tidrangescan - TID Cond: ((ctid > '(1,4)'::tid) AND ('(1,7)'::tid >= ctid)) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE ctid > '(1,4)' AND '(1,7)' >= ctid; - ctid -------- - (1,5) - (1,6) - (1,7) -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT ctid FROM tidrangescan WHERE '(1,7)' >= ctid AND ctid > '(1,4)'; - QUERY PLAN ----------------------------------------------------------------- - Tid Range Scan on tidrangescan - TID Cond: (('(1,7)'::tid >= ctid) AND (ctid > '(1,4)'::tid)) -(2 rows) - -SELECT ctid FROM tidrangescan WHERE '(1,7)' >= ctid AND ctid > '(1,4)'; - ctid -------- - (1,5) - (1,6) - (1,7) -(3 rows) - --- extreme offsets -SELECT ctid FROM tidrangescan WHERE ctid > '(0,65535)' AND ctid < '(1,0)' LIMIT 1; - ctid ------- -(0 rows) - -SELECT ctid FROM tidrangescan WHERE ctid < '(0,0)' LIMIT 1; - ctid ------- -(0 rows) - -SELECT ctid FROM tidrangescan WHERE ctid > '(4294967295,65535)'; - ctid ------- -(0 rows) - -SELECT ctid FROM tidrangescan WHERE ctid < '(0,0)'; - ctid ------- -(0 rows) - --- NULLs in the range cannot return tuples -SELECT ctid FROM tidrangescan WHERE ctid >= (SELECT NULL::tid); - ctid ------- -(0 rows) - --- rescans -EXPLAIN (COSTS OFF) -SELECT t.ctid,t2.c FROM tidrangescan t, -LATERAL (SELECT count(*) c FROM tidrangescan t2 WHERE t2.ctid <= t.ctid) t2 -WHERE t.ctid < '(1,0)'; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Tid Range Scan on tidrangescan t - TID Cond: (ctid < '(1,0)'::tid) - -> Aggregate - -> Tid Range Scan on tidrangescan t2 - TID Cond: (ctid <= t.ctid) -(6 rows) - -SELECT t.ctid,t2.c FROM tidrangescan t, -LATERAL (SELECT count(*) c FROM tidrangescan t2 WHERE t2.ctid <= t.ctid) t2 -WHERE t.ctid < '(1,0)'; - ctid | c ---------+---- - (0,1) | 1 - (0,2) | 2 - (0,3) | 3 - (0,4) | 4 - (0,5) | 5 - (0,6) | 6 - (0,7) | 7 - (0,8) | 8 - (0,9) | 9 - (0,10) | 10 -(10 rows) - --- cursors --- Ensure we get a TID Range scan without a Materialize node. -EXPLAIN (COSTS OFF) -DECLARE c SCROLL CURSOR FOR SELECT ctid FROM tidrangescan WHERE ctid < '(1,0)'; - QUERY PLAN ------------------------------------ - Tid Range Scan on tidrangescan - TID Cond: (ctid < '(1,0)'::tid) -(2 rows) - -BEGIN; -DECLARE c SCROLL CURSOR FOR SELECT ctid FROM tidrangescan WHERE ctid < '(1,0)'; -FETCH NEXT c; - ctid -------- - (0,1) -(1 row) - -FETCH NEXT c; - ctid -------- - (0,2) -(1 row) - -FETCH PRIOR c; - ctid -------- - (0,1) -(1 row) - -FETCH FIRST c; - ctid -------- - (0,1) -(1 row) - -FETCH LAST c; - ctid --------- - (0,10) -(1 row) - -COMMIT; -DROP TABLE tidrangescan; -RESET enable_seqscan; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.icu.utf8_1.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.icu.utf8.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.icu.utf8_1.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.icu.utf8.out 2021-10-30 22:04:15 -0600 @@ -1,9 +1,2 @@ -/* - * This test is for ICU collations. - */ -/* skip test if not UTF8 server encoding or no ICU collations installed */ -SELECT getdatabaseencoding() <> 'UTF8' OR - (SELECT count(*) FROM pg_collation WHERE collprovider = 'i') = 0 - AS skip_test \gset -\if :skip_test -\quit +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/incremental_sort.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/incremental_sort.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/incremental_sort.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/incremental_sort.out 2021-10-30 22:04:15 -0600 @@ -1,1688 +1,2 @@ --- When we have to sort the entire table, incremental sort will --- be slower than plain sort, so it should not be used. -explain (costs off) -select * from (select * from tenk1 order by four) t order by four, ten; - QUERY PLAN ------------------------------------ - Sort - Sort Key: tenk1.four, tenk1.ten - -> Sort - Sort Key: tenk1.four - -> Seq Scan on tenk1 -(5 rows) - --- When there is a LIMIT clause, incremental sort is beneficial because --- it only has to sort some of the groups, and not the entire table. -explain (costs off) -select * from (select * from tenk1 order by four) t order by four, ten -limit 1; - QUERY PLAN ------------------------------------------ - Limit - -> Incremental Sort - Sort Key: tenk1.four, tenk1.ten - Presorted Key: tenk1.four - -> Sort - Sort Key: tenk1.four - -> Seq Scan on tenk1 -(7 rows) - --- When work_mem is not enough to sort the entire table, incremental sort --- may be faster if individual groups still fit into work_mem. -set work_mem to '2MB'; -explain (costs off) -select * from (select * from tenk1 order by four) t order by four, ten; - QUERY PLAN ------------------------------------ - Incremental Sort - Sort Key: tenk1.four, tenk1.ten - Presorted Key: tenk1.four - -> Sort - Sort Key: tenk1.four - -> Seq Scan on tenk1 -(6 rows) - -reset work_mem; -create table t(a integer, b integer); -create or replace function explain_analyze_without_memory(query text) -returns table (out_line text) language plpgsql -as -$$ -declare - line text; -begin - for line in - execute 'explain (analyze, costs off, summary off, timing off) ' || query - loop - out_line := regexp_replace(line, '\d+kB', 'NNkB', 'g'); - return next; - end loop; -end; -$$; -create or replace function explain_analyze_inc_sort_nodes(query text) -returns jsonb language plpgsql -as -$$ -declare - elements jsonb; - element jsonb; - matching_nodes jsonb := '[]'::jsonb; -begin - execute 'explain (analyze, costs off, summary off, timing off, format ''json'') ' || query into strict elements; - while jsonb_array_length(elements) > 0 loop - element := elements->0; - elements := elements - 0; - case jsonb_typeof(element) - when 'array' then - if jsonb_array_length(element) > 0 then - elements := elements || element; - end if; - when 'object' then - if element ? 'Plan' then - elements := elements || jsonb_build_array(element->'Plan'); - element := element - 'Plan'; - else - if element ? 'Plans' then - elements := elements || jsonb_build_array(element->'Plans'); - element := element - 'Plans'; - end if; - if (element->>'Node Type')::text = 'Incremental Sort' then - matching_nodes := matching_nodes || element; - end if; - end if; - end case; - end loop; - return matching_nodes; -end; -$$; -create or replace function explain_analyze_inc_sort_nodes_without_memory(query text) -returns jsonb language plpgsql -as -$$ -declare - nodes jsonb := '[]'::jsonb; - node jsonb; - group_key text; - space_key text; -begin - for node in select * from jsonb_array_elements(explain_analyze_inc_sort_nodes(query)) t loop - for group_key in select unnest(array['Full-sort Groups', 'Pre-sorted Groups']::text[]) t loop - for space_key in select unnest(array['Sort Space Memory', 'Sort Space Disk']::text[]) t loop - node := jsonb_set(node, array[group_key, space_key, 'Average Sort Space Used'], '"NN"', false); - node := jsonb_set(node, array[group_key, space_key, 'Peak Sort Space Used'], '"NN"', false); - end loop; - end loop; - nodes := nodes || node; - end loop; - return nodes; -end; -$$; -create or replace function explain_analyze_inc_sort_nodes_verify_invariants(query text) -returns bool language plpgsql -as -$$ -declare - node jsonb; - group_stats jsonb; - group_key text; - space_key text; -begin - for node in select * from jsonb_array_elements(explain_analyze_inc_sort_nodes(query)) t loop - for group_key in select unnest(array['Full-sort Groups', 'Pre-sorted Groups']::text[]) t loop - group_stats := node->group_key; - for space_key in select unnest(array['Sort Space Memory', 'Sort Space Disk']::text[]) t loop - if (group_stats->space_key->'Peak Sort Space Used')::bigint < (group_stats->space_key->'Peak Sort Space Used')::bigint then - raise exception '% has invalid max space < average space', group_key; - end if; - end loop; - end loop; - end loop; - return true; -end; -$$; --- A single large group tested around each mode transition point. -insert into t(a, b) select i/100 + 1, i + 1 from generate_series(0, 999) n(i); -analyze t; -explain (costs off) select * from (select * from t order by a) s order by a, b limit 31; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 31; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 -(31 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 32; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 32; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 - 1 | 32 -(32 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 33; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 33; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 - 1 | 32 - 1 | 33 -(33 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 65; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 65; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 - 1 | 32 - 1 | 33 - 1 | 34 - 1 | 35 - 1 | 36 - 1 | 37 - 1 | 38 - 1 | 39 - 1 | 40 - 1 | 41 - 1 | 42 - 1 | 43 - 1 | 44 - 1 | 45 - 1 | 46 - 1 | 47 - 1 | 48 - 1 | 49 - 1 | 50 - 1 | 51 - 1 | 52 - 1 | 53 - 1 | 54 - 1 | 55 - 1 | 56 - 1 | 57 - 1 | 58 - 1 | 59 - 1 | 60 - 1 | 61 - 1 | 62 - 1 | 63 - 1 | 64 - 1 | 65 -(65 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 66; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 66; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 - 1 | 32 - 1 | 33 - 1 | 34 - 1 | 35 - 1 | 36 - 1 | 37 - 1 | 38 - 1 | 39 - 1 | 40 - 1 | 41 - 1 | 42 - 1 | 43 - 1 | 44 - 1 | 45 - 1 | 46 - 1 | 47 - 1 | 48 - 1 | 49 - 1 | 50 - 1 | 51 - 1 | 52 - 1 | 53 - 1 | 54 - 1 | 55 - 1 | 56 - 1 | 57 - 1 | 58 - 1 | 59 - 1 | 60 - 1 | 61 - 1 | 62 - 1 | 63 - 1 | 64 - 1 | 65 - 1 | 66 -(66 rows) - -delete from t; --- An initial large group followed by a small group. -insert into t(a, b) select i/50 + 1, i + 1 from generate_series(0, 999) n(i); -analyze t; -explain (costs off) select * from (select * from t order by a) s order by a, b limit 55; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 55; - a | b ----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 - 1 | 5 - 1 | 6 - 1 | 7 - 1 | 8 - 1 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 1 | 20 - 1 | 21 - 1 | 22 - 1 | 23 - 1 | 24 - 1 | 25 - 1 | 26 - 1 | 27 - 1 | 28 - 1 | 29 - 1 | 30 - 1 | 31 - 1 | 32 - 1 | 33 - 1 | 34 - 1 | 35 - 1 | 36 - 1 | 37 - 1 | 38 - 1 | 39 - 1 | 40 - 1 | 41 - 1 | 42 - 1 | 43 - 1 | 44 - 1 | 45 - 1 | 46 - 1 | 47 - 1 | 48 - 1 | 49 - 1 | 50 - 2 | 51 - 2 | 52 - 2 | 53 - 2 | 54 - 2 | 55 -(55 rows) - --- Test EXPLAIN ANALYZE with only a fullsort group. -select explain_analyze_without_memory('select * from (select * from t order by a) s order by a, b limit 55'); - explain_analyze_without_memory ---------------------------------------------------------------------------------------------------------------- - Limit (actual rows=55 loops=1) - -> Incremental Sort (actual rows=55 loops=1) - Sort Key: t.a, t.b - Presorted Key: t.a - Full-sort Groups: 2 Sort Methods: top-N heapsort, quicksort Average Memory: NNkB Peak Memory: NNkB - -> Sort (actual rows=101 loops=1) - Sort Key: t.a - Sort Method: quicksort Memory: NNkB - -> Seq Scan on t (actual rows=1000 loops=1) -(9 rows) - -select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from (select * from t order by a) s order by a, b limit 55')); - jsonb_pretty -------------------------------------------------- - [ + - { + - "Sort Key": [ + - "t.a", + - "t.b" + - ], + - "Node Type": "Incremental Sort", + - "Actual Rows": 55, + - "Actual Loops": 1, + - "Async Capable": false, + - "Presorted Key": [ + - "t.a" + - ], + - "Parallel Aware": false, + - "Full-sort Groups": { + - "Group Count": 2, + - "Sort Methods Used": [ + - "top-N heapsort", + - "quicksort" + - ], + - "Sort Space Memory": { + - "Peak Sort Space Used": "NN", + - "Average Sort Space Used": "NN"+ - } + - }, + - "Parent Relationship": "Outer" + - } + - ] -(1 row) - -select explain_analyze_inc_sort_nodes_verify_invariants('select * from (select * from t order by a) s order by a, b limit 55'); - explain_analyze_inc_sort_nodes_verify_invariants --------------------------------------------------- - t -(1 row) - -delete from t; --- An initial small group followed by a large group. -insert into t(a, b) select (case when i < 5 then i else 9 end), i from generate_series(1, 1000) n(i); -analyze t; -explain (costs off) select * from (select * from t order by a) s order by a, b limit 70; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 70; - a | b ----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 9 | 5 - 9 | 6 - 9 | 7 - 9 | 8 - 9 | 9 - 9 | 10 - 9 | 11 - 9 | 12 - 9 | 13 - 9 | 14 - 9 | 15 - 9 | 16 - 9 | 17 - 9 | 18 - 9 | 19 - 9 | 20 - 9 | 21 - 9 | 22 - 9 | 23 - 9 | 24 - 9 | 25 - 9 | 26 - 9 | 27 - 9 | 28 - 9 | 29 - 9 | 30 - 9 | 31 - 9 | 32 - 9 | 33 - 9 | 34 - 9 | 35 - 9 | 36 - 9 | 37 - 9 | 38 - 9 | 39 - 9 | 40 - 9 | 41 - 9 | 42 - 9 | 43 - 9 | 44 - 9 | 45 - 9 | 46 - 9 | 47 - 9 | 48 - 9 | 49 - 9 | 50 - 9 | 51 - 9 | 52 - 9 | 53 - 9 | 54 - 9 | 55 - 9 | 56 - 9 | 57 - 9 | 58 - 9 | 59 - 9 | 60 - 9 | 61 - 9 | 62 - 9 | 63 - 9 | 64 - 9 | 65 - 9 | 66 - 9 | 67 - 9 | 68 - 9 | 69 - 9 | 70 -(70 rows) - --- Checks case where we hit a group boundary at the last tuple of a batch. --- Because the full sort state is bounded, we scan 64 tuples (the mode --- transition point) but only retain 5. Thus when we transition modes, all --- tuples in the full sort state have different prefix keys. -explain (costs off) select * from (select * from t order by a) s order by a, b limit 5; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 5; - a | b ----+--- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 9 | 5 -(5 rows) - --- Test rescan. -begin; --- We force the planner to choose a plan with incremental sort on the right side --- of a nested loop join node. That way we trigger the rescan code path. -set local enable_hashjoin = off; -set local enable_mergejoin = off; -set local enable_material = off; -set local enable_sort = off; -explain (costs off) select * from t left join (select * from (select * from t order by a) v order by a, b) s on s.a = t.a where t.a in (1, 2); - QUERY PLAN ------------------------------------------------- - Nested Loop Left Join - Join Filter: (t_1.a = t.a) - -> Seq Scan on t - Filter: (a = ANY ('{1,2}'::integer[])) - -> Incremental Sort - Sort Key: t_1.a, t_1.b - Presorted Key: t_1.a - -> Sort - Sort Key: t_1.a - -> Seq Scan on t t_1 -(10 rows) - -select * from t left join (select * from (select * from t order by a) v order by a, b) s on s.a = t.a where t.a in (1, 2); - a | b | a | b ----+---+---+--- - 1 | 1 | 1 | 1 - 2 | 2 | 2 | 2 -(2 rows) - -rollback; --- Test EXPLAIN ANALYZE with both fullsort and presorted groups. -select explain_analyze_without_memory('select * from (select * from t order by a) s order by a, b limit 70'); - explain_analyze_without_memory ----------------------------------------------------------------------------------------------------------------- - Limit (actual rows=70 loops=1) - -> Incremental Sort (actual rows=70 loops=1) - Sort Key: t.a, t.b - Presorted Key: t.a - Full-sort Groups: 1 Sort Method: quicksort Average Memory: NNkB Peak Memory: NNkB - Pre-sorted Groups: 5 Sort Methods: top-N heapsort, quicksort Average Memory: NNkB Peak Memory: NNkB - -> Sort (actual rows=1000 loops=1) - Sort Key: t.a - Sort Method: quicksort Memory: NNkB - -> Seq Scan on t (actual rows=1000 loops=1) -(10 rows) - -select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from (select * from t order by a) s order by a, b limit 70')); - jsonb_pretty -------------------------------------------------- - [ + - { + - "Sort Key": [ + - "t.a", + - "t.b" + - ], + - "Node Type": "Incremental Sort", + - "Actual Rows": 70, + - "Actual Loops": 1, + - "Async Capable": false, + - "Presorted Key": [ + - "t.a" + - ], + - "Parallel Aware": false, + - "Full-sort Groups": { + - "Group Count": 1, + - "Sort Methods Used": [ + - "quicksort" + - ], + - "Sort Space Memory": { + - "Peak Sort Space Used": "NN", + - "Average Sort Space Used": "NN"+ - } + - }, + - "Pre-sorted Groups": { + - "Group Count": 5, + - "Sort Methods Used": [ + - "top-N heapsort", + - "quicksort" + - ], + - "Sort Space Memory": { + - "Peak Sort Space Used": "NN", + - "Average Sort Space Used": "NN"+ - } + - }, + - "Parent Relationship": "Outer" + - } + - ] -(1 row) - -select explain_analyze_inc_sort_nodes_verify_invariants('select * from (select * from t order by a) s order by a, b limit 70'); - explain_analyze_inc_sort_nodes_verify_invariants --------------------------------------------------- - t -(1 row) - -delete from t; --- Small groups of 10 tuples each tested around each mode transition point. -insert into t(a, b) select i / 10, i from generate_series(1, 1000) n(i); -analyze t; -explain (costs off) select * from (select * from t order by a) s order by a, b limit 31; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 31; - a | b ----+---- - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 - 0 | 6 - 0 | 7 - 0 | 8 - 0 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 20 - 2 | 21 - 2 | 22 - 2 | 23 - 2 | 24 - 2 | 25 - 2 | 26 - 2 | 27 - 2 | 28 - 2 | 29 - 3 | 30 - 3 | 31 -(31 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 32; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 32; - a | b ----+---- - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 - 0 | 6 - 0 | 7 - 0 | 8 - 0 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 20 - 2 | 21 - 2 | 22 - 2 | 23 - 2 | 24 - 2 | 25 - 2 | 26 - 2 | 27 - 2 | 28 - 2 | 29 - 3 | 30 - 3 | 31 - 3 | 32 -(32 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 33; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 33; - a | b ----+---- - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 - 0 | 6 - 0 | 7 - 0 | 8 - 0 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 20 - 2 | 21 - 2 | 22 - 2 | 23 - 2 | 24 - 2 | 25 - 2 | 26 - 2 | 27 - 2 | 28 - 2 | 29 - 3 | 30 - 3 | 31 - 3 | 32 - 3 | 33 -(33 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 65; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 65; - a | b ----+---- - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 - 0 | 6 - 0 | 7 - 0 | 8 - 0 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 20 - 2 | 21 - 2 | 22 - 2 | 23 - 2 | 24 - 2 | 25 - 2 | 26 - 2 | 27 - 2 | 28 - 2 | 29 - 3 | 30 - 3 | 31 - 3 | 32 - 3 | 33 - 3 | 34 - 3 | 35 - 3 | 36 - 3 | 37 - 3 | 38 - 3 | 39 - 4 | 40 - 4 | 41 - 4 | 42 - 4 | 43 - 4 | 44 - 4 | 45 - 4 | 46 - 4 | 47 - 4 | 48 - 4 | 49 - 5 | 50 - 5 | 51 - 5 | 52 - 5 | 53 - 5 | 54 - 5 | 55 - 5 | 56 - 5 | 57 - 5 | 58 - 5 | 59 - 6 | 60 - 6 | 61 - 6 | 62 - 6 | 63 - 6 | 64 - 6 | 65 -(65 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 66; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 66; - a | b ----+---- - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 - 0 | 6 - 0 | 7 - 0 | 8 - 0 | 9 - 1 | 10 - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 20 - 2 | 21 - 2 | 22 - 2 | 23 - 2 | 24 - 2 | 25 - 2 | 26 - 2 | 27 - 2 | 28 - 2 | 29 - 3 | 30 - 3 | 31 - 3 | 32 - 3 | 33 - 3 | 34 - 3 | 35 - 3 | 36 - 3 | 37 - 3 | 38 - 3 | 39 - 4 | 40 - 4 | 41 - 4 | 42 - 4 | 43 - 4 | 44 - 4 | 45 - 4 | 46 - 4 | 47 - 4 | 48 - 4 | 49 - 5 | 50 - 5 | 51 - 5 | 52 - 5 | 53 - 5 | 54 - 5 | 55 - 5 | 56 - 5 | 57 - 5 | 58 - 5 | 59 - 6 | 60 - 6 | 61 - 6 | 62 - 6 | 63 - 6 | 64 - 6 | 65 - 6 | 66 -(66 rows) - -delete from t; --- Small groups of only 1 tuple each tested around each mode transition point. -insert into t(a, b) select i, i from generate_series(1, 1000) n(i); -analyze t; -explain (costs off) select * from (select * from t order by a) s order by a, b limit 31; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 31; - a | b -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 - 20 | 20 - 21 | 21 - 22 | 22 - 23 | 23 - 24 | 24 - 25 | 25 - 26 | 26 - 27 | 27 - 28 | 28 - 29 | 29 - 30 | 30 - 31 | 31 -(31 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 32; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 32; - a | b -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 - 20 | 20 - 21 | 21 - 22 | 22 - 23 | 23 - 24 | 24 - 25 | 25 - 26 | 26 - 27 | 27 - 28 | 28 - 29 | 29 - 30 | 30 - 31 | 31 - 32 | 32 -(32 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 33; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 33; - a | b -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 - 20 | 20 - 21 | 21 - 22 | 22 - 23 | 23 - 24 | 24 - 25 | 25 - 26 | 26 - 27 | 27 - 28 | 28 - 29 | 29 - 30 | 30 - 31 | 31 - 32 | 32 - 33 | 33 -(33 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 65; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 65; - a | b -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 - 20 | 20 - 21 | 21 - 22 | 22 - 23 | 23 - 24 | 24 - 25 | 25 - 26 | 26 - 27 | 27 - 28 | 28 - 29 | 29 - 30 | 30 - 31 | 31 - 32 | 32 - 33 | 33 - 34 | 34 - 35 | 35 - 36 | 36 - 37 | 37 - 38 | 38 - 39 | 39 - 40 | 40 - 41 | 41 - 42 | 42 - 43 | 43 - 44 | 44 - 45 | 45 - 46 | 46 - 47 | 47 - 48 | 48 - 49 | 49 - 50 | 50 - 51 | 51 - 52 | 52 - 53 | 53 - 54 | 54 - 55 | 55 - 56 | 56 - 57 | 57 - 58 | 58 - 59 | 59 - 60 | 60 - 61 | 61 - 62 | 62 - 63 | 63 - 64 | 64 - 65 | 65 -(65 rows) - -explain (costs off) select * from (select * from t order by a) s order by a, b limit 66; - QUERY PLAN ---------------------------------- - Limit - -> Incremental Sort - Sort Key: t.a, t.b - Presorted Key: t.a - -> Sort - Sort Key: t.a - -> Seq Scan on t -(7 rows) - -select * from (select * from t order by a) s order by a, b limit 66; - a | b -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 6 - 7 | 7 - 8 | 8 - 9 | 9 - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 - 20 | 20 - 21 | 21 - 22 | 22 - 23 | 23 - 24 | 24 - 25 | 25 - 26 | 26 - 27 | 27 - 28 | 28 - 29 | 29 - 30 | 30 - 31 | 31 - 32 | 32 - 33 | 33 - 34 | 34 - 35 | 35 - 36 | 36 - 37 | 37 - 38 | 38 - 39 | 39 - 40 | 40 - 41 | 41 - 42 | 42 - 43 | 43 - 44 | 44 - 45 | 45 - 46 | 46 - 47 | 47 - 48 | 48 - 49 | 49 - 50 | 50 - 51 | 51 - 52 | 52 - 53 | 53 - 54 | 54 - 55 | 55 - 56 | 56 - 57 | 57 - 58 | 58 - 59 | 59 - 60 | 60 - 61 | 61 - 62 | 62 - 63 | 63 - 64 | 64 - 65 | 65 - 66 | 66 -(66 rows) - -delete from t; -drop table t; --- Incremental sort vs. parallel queries -set min_parallel_table_scan_size = '1kB'; -set min_parallel_index_scan_size = '1kB'; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -create table t (a int, b int, c int); -insert into t select mod(i,10),mod(i,10),i from generate_series(1,10000) s(i); -create index on t (a); -analyze t; -set enable_incremental_sort = off; -explain (costs off) select a,b,sum(c) from t group by 1,2 order by 1,2,3 limit 1; - QUERY PLAN ------------------------------------------------------- - Limit - -> Sort - Sort Key: a, b, (sum(c)) - -> Finalize HashAggregate - Group Key: a, b - -> Gather - Workers Planned: 2 - -> Partial HashAggregate - Group Key: a, b - -> Parallel Seq Scan on t -(10 rows) - -set enable_incremental_sort = on; -explain (costs off) select a,b,sum(c) from t group by 1,2 order by 1,2,3 limit 1; - QUERY PLAN ----------------------------------------------------------------------- - Limit - -> Incremental Sort - Sort Key: a, b, (sum(c)) - Presorted Key: a, b - -> GroupAggregate - Group Key: a, b - -> Gather Merge - Workers Planned: 2 - -> Incremental Sort - Sort Key: a, b - Presorted Key: a - -> Parallel Index Scan using t_a_idx on t -(12 rows) - --- Incremental sort vs. set operations with varno 0 -set enable_hashagg to off; -explain (costs off) select * from t union select * from t order by 1,3; - QUERY PLAN ----------------------------------------------------------- - Incremental Sort - Sort Key: t.a, t.c - Presorted Key: t.a - -> Unique - -> Sort - Sort Key: t.a, t.b, t.c - -> Append - -> Gather - Workers Planned: 2 - -> Parallel Seq Scan on t - -> Gather - Workers Planned: 2 - -> Parallel Seq Scan on t t_1 -(13 rows) - --- Full sort, not just incremental sort can be pushed below a gather merge path --- by generate_useful_gather_paths. -explain (costs off) select distinct a,b from t; - QUERY PLAN ------------------------------------------- - Unique - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: a, b - -> Parallel Seq Scan on t -(6 rows) - -drop table t; --- Sort pushdown can't go below where expressions are part of the rel target. --- In particular this is interesting for volatile expressions which have to --- go above joins since otherwise we'll incorrectly use expression evaluations --- across multiple rows. -set enable_hashagg=off; -set enable_seqscan=off; -set enable_incremental_sort = off; -set parallel_tuple_cost=0; -set parallel_setup_cost=0; -set min_parallel_table_scan_size = 0; -set min_parallel_index_scan_size = 0; --- Parallel sort below join. -explain (costs off) select distinct sub.unique1, stringu1 -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub; - QUERY PLAN --------------------------------------------------------------------------- - Unique - -> Nested Loop - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: tenk1.unique1, tenk1.stringu1 - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(8 rows) - -explain (costs off) select sub.unique1, stringu1 -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub -order by 1, 2; - QUERY PLAN --------------------------------------------------------------------- - Nested Loop - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: tenk1.unique1, tenk1.stringu1 - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(7 rows) - --- Parallel sort but with expression that can be safely generated at the base rel. -explain (costs off) select distinct sub.unique1, md5(stringu1) -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub; - QUERY PLAN ----------------------------------------------------------------------------------------- - Unique - -> Nested Loop - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: tenk1.unique1, (md5((tenk1.stringu1)::text)) COLLATE "C" - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(8 rows) - -explain (costs off) select sub.unique1, md5(stringu1) -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub -order by 1, 2; - QUERY PLAN ----------------------------------------------------------------------------------- - Nested Loop - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: tenk1.unique1, (md5((tenk1.stringu1)::text)) COLLATE "C" - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(7 rows) - --- Parallel sort with an aggregate that can be safely generated in parallel, --- but we can't sort by partial aggregate values. -explain (costs off) select count(*) -from tenk1 t1 -join tenk1 t2 on t1.unique1 = t2.unique2 -join tenk1 t3 on t2.unique1 = t3.unique1 -order by count(*); - QUERY PLAN ------------------------------------------------------------------------------------------------ - Sort - Sort Key: (count(*)) - -> Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (t2.unique1 = t3.unique1) - -> Parallel Hash Join - Hash Cond: (t1.unique1 = t2.unique2) - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 t1 - -> Parallel Hash - -> Parallel Index Scan using tenk1_unique2 on tenk1 t2 - -> Parallel Hash - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 t3 -(15 rows) - --- Parallel sort but with expression (correlated subquery) that --- is prohibited in parallel plans. -explain (costs off) select distinct - unique1, - (select t.unique1 from tenk1 where tenk1.unique1 = t.unique1) -from tenk1 t, generate_series(1, 1000); - QUERY PLAN ---------------------------------------------------------------------------------- - Unique - -> Sort - Sort Key: t.unique1, ((SubPlan 1)) - -> Gather - Workers Planned: 2 - -> Nested Loop - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 t - -> Function Scan on generate_series - SubPlan 1 - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = t.unique1) -(11 rows) - -explain (costs off) select - unique1, - (select t.unique1 from tenk1 where tenk1.unique1 = t.unique1) -from tenk1 t, generate_series(1, 1000) -order by 1, 2; - QUERY PLAN ---------------------------------------------------------------------------- - Sort - Sort Key: t.unique1, ((SubPlan 1)) - -> Gather - Workers Planned: 2 - -> Nested Loop - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 t - -> Function Scan on generate_series - SubPlan 1 - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = t.unique1) -(10 rows) - --- Parallel sort but with expression not available until the upper rel. -explain (costs off) select distinct sub.unique1, stringu1 || random()::text -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub; - QUERY PLAN ---------------------------------------------------------------------------------------------- - Unique - -> Sort - Sort Key: tenk1.unique1, (((tenk1.stringu1)::text || (random())::text)) COLLATE "C" - -> Gather - Workers Planned: 2 - -> Nested Loop - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(8 rows) - -explain (costs off) select sub.unique1, stringu1 || random()::text -from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub -order by 1, 2; - QUERY PLAN ---------------------------------------------------------------------------------------- - Sort - Sort Key: tenk1.unique1, (((tenk1.stringu1)::text || (random())::text)) COLLATE "C" - -> Gather - Workers Planned: 2 - -> Nested Loop - -> Parallel Index Scan using tenk1_unique1 on tenk1 - -> Function Scan on generate_series -(7 rows) - --- Disallow pushing down sort when pathkey is an SRF. -explain (costs off) select unique1 from tenk1 order by unnest('{1,2}'::int[]); - QUERY PLAN -------------------------------------------------------------------------- - Sort - Sort Key: (unnest('{1,2}'::integer[])) - -> Gather - Workers Planned: 2 - -> ProjectSet - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 -(6 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rules.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rules.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rules.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rules.out 2021-10-30 22:04:16 -0600 @@ -1,3701 +1,2 @@ --- --- RULES --- From Jan's original setup_ruletest.sql and run_ruletest.sql --- - thomas 1998-09-13 --- --- --- Tables and rules for the view test --- -create table rtest_t1 (a int4, b int4); -create table rtest_t2 (a int4, b int4); -create table rtest_t3 (a int4, b int4); -create view rtest_v1 as select * from rtest_t1; -create rule rtest_v1_ins as on insert to rtest_v1 do instead - insert into rtest_t1 values (new.a, new.b); -create rule rtest_v1_upd as on update to rtest_v1 do instead - update rtest_t1 set a = new.a, b = new.b - where a = old.a; -create rule rtest_v1_del as on delete to rtest_v1 do instead - delete from rtest_t1 where a = old.a; --- Test comments -COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule'; -ERROR: rule "rtest_v1_bad" for relation "rtest_v1" does not exist -COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule'; -COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL; --- --- Tables and rules for the constraint update/delete test --- --- Note: --- Now that we have multiple action rule support, we check --- both possible syntaxes to define them (The last action --- can but must not have a semicolon at the end). --- -create table rtest_system (sysname text, sysdesc text); -create table rtest_interface (sysname text, ifname text); -create table rtest_person (pname text, pdesc text); -create table rtest_admin (pname text, sysname text); -create rule rtest_sys_upd as on update to rtest_system do also ( - update rtest_interface set sysname = new.sysname - where sysname = old.sysname; - update rtest_admin set sysname = new.sysname - where sysname = old.sysname - ); -create rule rtest_sys_del as on delete to rtest_system do also ( - delete from rtest_interface where sysname = old.sysname; - delete from rtest_admin where sysname = old.sysname; - ); -create rule rtest_pers_upd as on update to rtest_person do also - update rtest_admin set pname = new.pname where pname = old.pname; -create rule rtest_pers_del as on delete to rtest_person do also - delete from rtest_admin where pname = old.pname; --- --- Tables and rules for the logging test --- -create table rtest_emp (ename char(20), salary money); -create table rtest_emplog (ename char(20), who name, action char(10), newsal money, oldsal money); -create table rtest_empmass (ename char(20), salary money); -create rule rtest_emp_ins as on insert to rtest_emp do - insert into rtest_emplog values (new.ename, current_user, - 'hired', new.salary, '0.00'); -create rule rtest_emp_upd as on update to rtest_emp where new.salary != old.salary do - insert into rtest_emplog values (new.ename, current_user, - 'honored', new.salary, old.salary); -create rule rtest_emp_del as on delete to rtest_emp do - insert into rtest_emplog values (old.ename, current_user, - 'fired', '0.00', old.salary); --- --- Tables and rules for the multiple cascaded qualified instead --- rule test --- -create table rtest_t4 (a int4, b text); -create table rtest_t5 (a int4, b text); -create table rtest_t6 (a int4, b text); -create table rtest_t7 (a int4, b text); -create table rtest_t8 (a int4, b text); -create table rtest_t9 (a int4, b text); -create rule rtest_t4_ins1 as on insert to rtest_t4 - where new.a >= 10 and new.a < 20 do instead - insert into rtest_t5 values (new.a, new.b); -create rule rtest_t4_ins2 as on insert to rtest_t4 - where new.a >= 20 and new.a < 30 do - insert into rtest_t6 values (new.a, new.b); -create rule rtest_t5_ins as on insert to rtest_t5 - where new.a > 15 do - insert into rtest_t7 values (new.a, new.b); -create rule rtest_t6_ins as on insert to rtest_t6 - where new.a > 25 do instead - insert into rtest_t8 values (new.a, new.b); --- --- Tables and rules for the rule fire order test --- --- As of PG 7.3, the rules should fire in order by name, regardless --- of INSTEAD attributes or creation order. --- -create table rtest_order1 (a int4); -create table rtest_order2 (a int4, b int4, c text); -create sequence rtest_seq; -create rule rtest_order_r3 as on insert to rtest_order1 do instead - insert into rtest_order2 values (new.a, nextval('rtest_seq'), - 'rule 3 - this should run 3rd'); -create rule rtest_order_r4 as on insert to rtest_order1 - where a < 100 do instead - insert into rtest_order2 values (new.a, nextval('rtest_seq'), - 'rule 4 - this should run 4th'); -create rule rtest_order_r2 as on insert to rtest_order1 do - insert into rtest_order2 values (new.a, nextval('rtest_seq'), - 'rule 2 - this should run 2nd'); -create rule rtest_order_r1 as on insert to rtest_order1 do instead - insert into rtest_order2 values (new.a, nextval('rtest_seq'), - 'rule 1 - this should run 1st'); --- --- Tables and rules for the instead nothing test --- -create table rtest_nothn1 (a int4, b text); -create table rtest_nothn2 (a int4, b text); -create table rtest_nothn3 (a int4, b text); -create table rtest_nothn4 (a int4, b text); -create rule rtest_nothn_r1 as on insert to rtest_nothn1 - where new.a >= 10 and new.a < 20 do instead nothing; -create rule rtest_nothn_r2 as on insert to rtest_nothn1 - where new.a >= 30 and new.a < 40 do instead nothing; -create rule rtest_nothn_r3 as on insert to rtest_nothn2 - where new.a >= 100 do instead - insert into rtest_nothn3 values (new.a, new.b); -create rule rtest_nothn_r4 as on insert to rtest_nothn2 - do instead nothing; --- --- Tests on a view that is select * of a table --- and has insert/update/delete instead rules to --- behave close like the real table. --- --- --- We need test date later --- -insert into rtest_t2 values (1, 21); -insert into rtest_t2 values (2, 22); -insert into rtest_t2 values (3, 23); -insert into rtest_t3 values (1, 31); -insert into rtest_t3 values (2, 32); -insert into rtest_t3 values (3, 33); -insert into rtest_t3 values (4, 34); -insert into rtest_t3 values (5, 35); --- insert values -insert into rtest_v1 values (1, 11); -insert into rtest_v1 values (2, 12); -select * from rtest_v1; - a | b ----+---- - 1 | 11 - 2 | 12 -(2 rows) - --- delete with constant expression -delete from rtest_v1 where a = 1; -select * from rtest_v1; - a | b ----+---- - 2 | 12 -(1 row) - -insert into rtest_v1 values (1, 11); -delete from rtest_v1 where b = 12; -select * from rtest_v1; - a | b ----+---- - 1 | 11 -(1 row) - -insert into rtest_v1 values (2, 12); -insert into rtest_v1 values (2, 13); -select * from rtest_v1; - a | b ----+---- - 1 | 11 - 2 | 12 - 2 | 13 -(3 rows) - -** Remember the delete rule on rtest_v1: It says -** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a -** So this time both rows with a = 2 must get deleted -\p -** Remember the delete rule on rtest_v1: It says -** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a -** So this time both rows with a = 2 must get deleted -\r -delete from rtest_v1 where b = 12; -select * from rtest_v1; - a | b ----+---- - 1 | 11 -(1 row) - -delete from rtest_v1; --- insert select -insert into rtest_v1 select * from rtest_t2; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 -(3 rows) - -delete from rtest_v1; --- same with swapped targetlist -insert into rtest_v1 (b, a) select b, a from rtest_t2; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 -(3 rows) - --- now with only one target attribute -insert into rtest_v1 (a) select a from rtest_t3; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 - 1 | - 2 | - 3 | - 4 | - 5 | -(8 rows) - -select * from rtest_v1 where b isnull; - a | b ----+--- - 1 | - 2 | - 3 | - 4 | - 5 | -(5 rows) - --- let attribute a differ (must be done on rtest_t1 - see above) -update rtest_t1 set a = a + 10 where b isnull; -delete from rtest_v1 where b isnull; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 -(3 rows) - --- now updates with constant expression -update rtest_v1 set b = 42 where a = 2; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 3 | 23 - 2 | 42 -(3 rows) - -update rtest_v1 set b = 99 where b = 42; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 3 | 23 - 2 | 99 -(3 rows) - -update rtest_v1 set b = 88 where b < 50; -select * from rtest_v1; - a | b ----+---- - 2 | 99 - 1 | 88 - 3 | 88 -(3 rows) - -delete from rtest_v1; -insert into rtest_v1 select rtest_t2.a, rtest_t3.b - from rtest_t2, rtest_t3 - where rtest_t2.a = rtest_t3.a; -select * from rtest_v1; - a | b ----+---- - 1 | 31 - 2 | 32 - 3 | 33 -(3 rows) - --- updates in a mergejoin -update rtest_v1 set b = rtest_t2.b from rtest_t2 where rtest_v1.a = rtest_t2.a; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 -(3 rows) - -insert into rtest_v1 select * from rtest_t3; -select * from rtest_v1; - a | b ----+---- - 1 | 21 - 2 | 22 - 3 | 23 - 1 | 31 - 2 | 32 - 3 | 33 - 4 | 34 - 5 | 35 -(8 rows) - -update rtest_t1 set a = a + 10 where b > 30; -select * from rtest_v1; - a | b -----+---- - 1 | 21 - 2 | 22 - 3 | 23 - 11 | 31 - 12 | 32 - 13 | 33 - 14 | 34 - 15 | 35 -(8 rows) - -update rtest_v1 set a = rtest_t3.a + 20 from rtest_t3 where rtest_v1.b = rtest_t3.b; -select * from rtest_v1; - a | b -----+---- - 1 | 21 - 2 | 22 - 3 | 23 - 21 | 31 - 22 | 32 - 23 | 33 - 24 | 34 - 25 | 35 -(8 rows) - --- --- Test for constraint updates/deletes --- -insert into rtest_system values ('orion', 'Linux Jan Wieck'); -insert into rtest_system values ('notjw', 'WinNT Jan Wieck (notebook)'); -insert into rtest_system values ('neptun', 'Fileserver'); -insert into rtest_interface values ('orion', 'eth0'); -insert into rtest_interface values ('orion', 'eth1'); -insert into rtest_interface values ('notjw', 'eth0'); -insert into rtest_interface values ('neptun', 'eth0'); -insert into rtest_person values ('jw', 'Jan Wieck'); -insert into rtest_person values ('bm', 'Bruce Momjian'); -insert into rtest_admin values ('jw', 'orion'); -insert into rtest_admin values ('jw', 'notjw'); -insert into rtest_admin values ('bm', 'neptun'); -update rtest_system set sysname = 'pluto' where sysname = 'neptun'; -select * from rtest_interface; - sysname | ifname ----------+-------- - orion | eth0 - orion | eth1 - notjw | eth0 - pluto | eth0 -(4 rows) - -select * from rtest_admin; - pname | sysname --------+--------- - jw | orion - jw | notjw - bm | pluto -(3 rows) - -update rtest_person set pname = 'jwieck' where pdesc = 'Jan Wieck'; --- Note: use ORDER BY here to ensure consistent output across all systems. --- The above UPDATE affects two rows with equal keys, so they could be --- updated in either order depending on the whim of the local qsort(). -select * from rtest_admin order by pname, sysname; - pname | sysname ---------+--------- - bm | pluto - jwieck | notjw - jwieck | orion -(3 rows) - -delete from rtest_system where sysname = 'orion'; -select * from rtest_interface; - sysname | ifname ----------+-------- - notjw | eth0 - pluto | eth0 -(2 rows) - -select * from rtest_admin; - pname | sysname ---------+--------- - bm | pluto - jwieck | notjw -(2 rows) - --- --- Rule qualification test --- -insert into rtest_emp values ('wiecc', '5000.00'); -insert into rtest_emp values ('gates', '80000.00'); -update rtest_emp set ename = 'wiecx' where ename = 'wiecc'; -update rtest_emp set ename = 'wieck', salary = '6000.00' where ename = 'wiecx'; -update rtest_emp set salary = '7000.00' where ename = 'wieck'; -delete from rtest_emp where ename = 'gates'; -select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; - ename | matches user | action | newsal | oldsal -----------------------+--------------+------------+------------+------------ - gates | t | fired | $0.00 | $80,000.00 - gates | t | hired | $80,000.00 | $0.00 - wiecc | t | hired | $5,000.00 | $0.00 - wieck | t | honored | $6,000.00 | $5,000.00 - wieck | t | honored | $7,000.00 | $6,000.00 -(5 rows) - -insert into rtest_empmass values ('meyer', '4000.00'); -insert into rtest_empmass values ('maier', '5000.00'); -insert into rtest_empmass values ('mayr', '6000.00'); -insert into rtest_emp select * from rtest_empmass; -select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; - ename | matches user | action | newsal | oldsal -----------------------+--------------+------------+------------+------------ - gates | t | fired | $0.00 | $80,000.00 - gates | t | hired | $80,000.00 | $0.00 - maier | t | hired | $5,000.00 | $0.00 - mayr | t | hired | $6,000.00 | $0.00 - meyer | t | hired | $4,000.00 | $0.00 - wiecc | t | hired | $5,000.00 | $0.00 - wieck | t | honored | $6,000.00 | $5,000.00 - wieck | t | honored | $7,000.00 | $6,000.00 -(8 rows) - -update rtest_empmass set salary = salary + '1000.00'; -update rtest_emp set salary = rtest_empmass.salary from rtest_empmass where rtest_emp.ename = rtest_empmass.ename; -select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; - ename | matches user | action | newsal | oldsal -----------------------+--------------+------------+------------+------------ - gates | t | fired | $0.00 | $80,000.00 - gates | t | hired | $80,000.00 | $0.00 - maier | t | hired | $5,000.00 | $0.00 - maier | t | honored | $6,000.00 | $5,000.00 - mayr | t | hired | $6,000.00 | $0.00 - mayr | t | honored | $7,000.00 | $6,000.00 - meyer | t | hired | $4,000.00 | $0.00 - meyer | t | honored | $5,000.00 | $4,000.00 - wiecc | t | hired | $5,000.00 | $0.00 - wieck | t | honored | $6,000.00 | $5,000.00 - wieck | t | honored | $7,000.00 | $6,000.00 -(11 rows) - -delete from rtest_emp using rtest_empmass where rtest_emp.ename = rtest_empmass.ename; -select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; - ename | matches user | action | newsal | oldsal -----------------------+--------------+------------+------------+------------ - gates | t | fired | $0.00 | $80,000.00 - gates | t | hired | $80,000.00 | $0.00 - maier | t | fired | $0.00 | $6,000.00 - maier | t | hired | $5,000.00 | $0.00 - maier | t | honored | $6,000.00 | $5,000.00 - mayr | t | fired | $0.00 | $7,000.00 - mayr | t | hired | $6,000.00 | $0.00 - mayr | t | honored | $7,000.00 | $6,000.00 - meyer | t | fired | $0.00 | $5,000.00 - meyer | t | hired | $4,000.00 | $0.00 - meyer | t | honored | $5,000.00 | $4,000.00 - wiecc | t | hired | $5,000.00 | $0.00 - wieck | t | honored | $6,000.00 | $5,000.00 - wieck | t | honored | $7,000.00 | $6,000.00 -(14 rows) - --- --- Multiple cascaded qualified instead rule test --- -insert into rtest_t4 values (1, 'Record should go to rtest_t4'); -insert into rtest_t4 values (2, 'Record should go to rtest_t4'); -insert into rtest_t4 values (10, 'Record should go to rtest_t5'); -insert into rtest_t4 values (15, 'Record should go to rtest_t5'); -insert into rtest_t4 values (19, 'Record should go to rtest_t5 and t7'); -insert into rtest_t4 values (20, 'Record should go to rtest_t4 and t6'); -insert into rtest_t4 values (26, 'Record should go to rtest_t4 and t8'); -insert into rtest_t4 values (28, 'Record should go to rtest_t4 and t8'); -insert into rtest_t4 values (30, 'Record should go to rtest_t4'); -insert into rtest_t4 values (40, 'Record should go to rtest_t4'); -select * from rtest_t4; - a | b -----+------------------------------------- - 1 | Record should go to rtest_t4 - 2 | Record should go to rtest_t4 - 20 | Record should go to rtest_t4 and t6 - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 - 30 | Record should go to rtest_t4 - 40 | Record should go to rtest_t4 -(7 rows) - -select * from rtest_t5; - a | b -----+------------------------------------- - 10 | Record should go to rtest_t5 - 15 | Record should go to rtest_t5 - 19 | Record should go to rtest_t5 and t7 -(3 rows) - -select * from rtest_t6; - a | b -----+------------------------------------- - 20 | Record should go to rtest_t4 and t6 -(1 row) - -select * from rtest_t7; - a | b -----+------------------------------------- - 19 | Record should go to rtest_t5 and t7 -(1 row) - -select * from rtest_t8; - a | b -----+------------------------------------- - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 -(2 rows) - -delete from rtest_t4; -delete from rtest_t5; -delete from rtest_t6; -delete from rtest_t7; -delete from rtest_t8; -insert into rtest_t9 values (1, 'Record should go to rtest_t4'); -insert into rtest_t9 values (2, 'Record should go to rtest_t4'); -insert into rtest_t9 values (10, 'Record should go to rtest_t5'); -insert into rtest_t9 values (15, 'Record should go to rtest_t5'); -insert into rtest_t9 values (19, 'Record should go to rtest_t5 and t7'); -insert into rtest_t9 values (20, 'Record should go to rtest_t4 and t6'); -insert into rtest_t9 values (26, 'Record should go to rtest_t4 and t8'); -insert into rtest_t9 values (28, 'Record should go to rtest_t4 and t8'); -insert into rtest_t9 values (30, 'Record should go to rtest_t4'); -insert into rtest_t9 values (40, 'Record should go to rtest_t4'); -insert into rtest_t4 select * from rtest_t9 where a < 20; -select * from rtest_t4; - a | b ----+------------------------------ - 1 | Record should go to rtest_t4 - 2 | Record should go to rtest_t4 -(2 rows) - -select * from rtest_t5; - a | b -----+------------------------------------- - 10 | Record should go to rtest_t5 - 15 | Record should go to rtest_t5 - 19 | Record should go to rtest_t5 and t7 -(3 rows) - -select * from rtest_t6; - a | b ----+--- -(0 rows) - -select * from rtest_t7; - a | b -----+------------------------------------- - 19 | Record should go to rtest_t5 and t7 -(1 row) - -select * from rtest_t8; - a | b ----+--- -(0 rows) - -insert into rtest_t4 select * from rtest_t9 where b ~ 'and t8'; -select * from rtest_t4; - a | b -----+------------------------------------- - 1 | Record should go to rtest_t4 - 2 | Record should go to rtest_t4 - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 -(4 rows) - -select * from rtest_t5; - a | b -----+------------------------------------- - 10 | Record should go to rtest_t5 - 15 | Record should go to rtest_t5 - 19 | Record should go to rtest_t5 and t7 -(3 rows) - -select * from rtest_t6; - a | b ----+--- -(0 rows) - -select * from rtest_t7; - a | b -----+------------------------------------- - 19 | Record should go to rtest_t5 and t7 -(1 row) - -select * from rtest_t8; - a | b -----+------------------------------------- - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 -(2 rows) - -insert into rtest_t4 select a + 1, b from rtest_t9 where a in (20, 30, 40); -select * from rtest_t4; - a | b -----+------------------------------------- - 1 | Record should go to rtest_t4 - 2 | Record should go to rtest_t4 - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 - 21 | Record should go to rtest_t4 and t6 - 31 | Record should go to rtest_t4 - 41 | Record should go to rtest_t4 -(7 rows) - -select * from rtest_t5; - a | b -----+------------------------------------- - 10 | Record should go to rtest_t5 - 15 | Record should go to rtest_t5 - 19 | Record should go to rtest_t5 and t7 -(3 rows) - -select * from rtest_t6; - a | b -----+------------------------------------- - 21 | Record should go to rtest_t4 and t6 -(1 row) - -select * from rtest_t7; - a | b -----+------------------------------------- - 19 | Record should go to rtest_t5 and t7 -(1 row) - -select * from rtest_t8; - a | b -----+------------------------------------- - 26 | Record should go to rtest_t4 and t8 - 28 | Record should go to rtest_t4 and t8 -(2 rows) - --- --- Check that the ordering of rules fired is correct --- -insert into rtest_order1 values (1); -select * from rtest_order2; - a | b | c ----+---+------------------------------ - 1 | 1 | rule 1 - this should run 1st - 1 | 2 | rule 2 - this should run 2nd - 1 | 3 | rule 3 - this should run 3rd - 1 | 4 | rule 4 - this should run 4th -(4 rows) - --- --- Check if instead nothing w/without qualification works --- -insert into rtest_nothn1 values (1, 'want this'); -insert into rtest_nothn1 values (2, 'want this'); -insert into rtest_nothn1 values (10, 'don''t want this'); -insert into rtest_nothn1 values (19, 'don''t want this'); -insert into rtest_nothn1 values (20, 'want this'); -insert into rtest_nothn1 values (29, 'want this'); -insert into rtest_nothn1 values (30, 'don''t want this'); -insert into rtest_nothn1 values (39, 'don''t want this'); -insert into rtest_nothn1 values (40, 'want this'); -insert into rtest_nothn1 values (50, 'want this'); -insert into rtest_nothn1 values (60, 'want this'); -select * from rtest_nothn1; - a | b -----+----------- - 1 | want this - 2 | want this - 20 | want this - 29 | want this - 40 | want this - 50 | want this - 60 | want this -(7 rows) - -insert into rtest_nothn2 values (10, 'too small'); -insert into rtest_nothn2 values (50, 'too small'); -insert into rtest_nothn2 values (100, 'OK'); -insert into rtest_nothn2 values (200, 'OK'); -select * from rtest_nothn2; - a | b ----+--- -(0 rows) - -select * from rtest_nothn3; - a | b ------+---- - 100 | OK - 200 | OK -(2 rows) - -delete from rtest_nothn1; -delete from rtest_nothn2; -delete from rtest_nothn3; -insert into rtest_nothn4 values (1, 'want this'); -insert into rtest_nothn4 values (2, 'want this'); -insert into rtest_nothn4 values (10, 'don''t want this'); -insert into rtest_nothn4 values (19, 'don''t want this'); -insert into rtest_nothn4 values (20, 'want this'); -insert into rtest_nothn4 values (29, 'want this'); -insert into rtest_nothn4 values (30, 'don''t want this'); -insert into rtest_nothn4 values (39, 'don''t want this'); -insert into rtest_nothn4 values (40, 'want this'); -insert into rtest_nothn4 values (50, 'want this'); -insert into rtest_nothn4 values (60, 'want this'); -insert into rtest_nothn1 select * from rtest_nothn4; -select * from rtest_nothn1; - a | b -----+----------- - 1 | want this - 2 | want this - 20 | want this - 29 | want this - 40 | want this - 50 | want this - 60 | want this -(7 rows) - -delete from rtest_nothn4; -insert into rtest_nothn4 values (10, 'too small'); -insert into rtest_nothn4 values (50, 'too small'); -insert into rtest_nothn4 values (100, 'OK'); -insert into rtest_nothn4 values (200, 'OK'); -insert into rtest_nothn2 select * from rtest_nothn4; -select * from rtest_nothn2; - a | b ----+--- -(0 rows) - -select * from rtest_nothn3; - a | b ------+---- - 100 | OK - 200 | OK -(2 rows) - -create table rtest_view1 (a int4, b text, v bool); -create table rtest_view2 (a int4); -create table rtest_view3 (a int4, b text); -create table rtest_view4 (a int4, b text, c int4); -create view rtest_vview1 as select a, b from rtest_view1 X - where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); -create view rtest_vview2 as select a, b from rtest_view1 where v; -create view rtest_vview3 as select a, b from rtest_vview2 X - where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); -create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount - from rtest_view1 X, rtest_view2 Y - where X.a = Y.a - group by X.a, X.b; -create function rtest_viewfunc1(int4) returns int4 as - 'select count(*)::int4 from rtest_view2 where a = $1' - language sql; -create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount - from rtest_view1; -insert into rtest_view1 values (1, 'item 1', 't'); -insert into rtest_view1 values (2, 'item 2', 't'); -insert into rtest_view1 values (3, 'item 3', 't'); -insert into rtest_view1 values (4, 'item 4', 'f'); -insert into rtest_view1 values (5, 'item 5', 't'); -insert into rtest_view1 values (6, 'item 6', 'f'); -insert into rtest_view1 values (7, 'item 7', 't'); -insert into rtest_view1 values (8, 'item 8', 't'); -insert into rtest_view2 values (2); -insert into rtest_view2 values (2); -insert into rtest_view2 values (4); -insert into rtest_view2 values (5); -insert into rtest_view2 values (7); -insert into rtest_view2 values (7); -insert into rtest_view2 values (7); -insert into rtest_view2 values (7); -select * from rtest_vview1; - a | b ----+-------- - 2 | item 2 - 4 | item 4 - 5 | item 5 - 7 | item 7 -(4 rows) - -select * from rtest_vview2; - a | b ----+-------- - 1 | item 1 - 2 | item 2 - 3 | item 3 - 5 | item 5 - 7 | item 7 - 8 | item 8 -(6 rows) - -select * from rtest_vview3; - a | b ----+-------- - 2 | item 2 - 5 | item 5 - 7 | item 7 -(3 rows) - -select * from rtest_vview4 order by a, b; - a | b | refcount ----+--------+---------- - 2 | item 2 | 2 - 4 | item 4 | 1 - 5 | item 5 | 1 - 7 | item 7 | 4 -(4 rows) - -select * from rtest_vview5; - a | b | refcount ----+--------+---------- - 1 | item 1 | 0 - 2 | item 2 | 2 - 3 | item 3 | 0 - 4 | item 4 | 1 - 5 | item 5 | 1 - 6 | item 6 | 0 - 7 | item 7 | 4 - 8 | item 8 | 0 -(8 rows) - -insert into rtest_view3 select * from rtest_vview1 where a < 7; -select * from rtest_view3; - a | b ----+-------- - 2 | item 2 - 4 | item 4 - 5 | item 5 -(3 rows) - -delete from rtest_view3; -insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2'; -select * from rtest_view3; - a | b ----+-------- - 1 | item 1 - 3 | item 3 - 7 | item 7 - 8 | item 8 -(4 rows) - -delete from rtest_view3; -insert into rtest_view3 select * from rtest_vview3; -select * from rtest_view3; - a | b ----+-------- - 2 | item 2 - 5 | item 5 - 7 | item 7 -(3 rows) - -delete from rtest_view3; -insert into rtest_view4 select * from rtest_vview4 where 3 > refcount; -select * from rtest_view4 order by a, b; - a | b | c ----+--------+--- - 2 | item 2 | 2 - 4 | item 4 | 1 - 5 | item 5 | 1 -(3 rows) - -delete from rtest_view4; -insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0; -select * from rtest_view4; - a | b | c ----+--------+--- - 3 | item 3 | 0 - 6 | item 6 | 0 - 8 | item 8 | 0 -(3 rows) - -delete from rtest_view4; --- --- Test for computations in views --- -create table rtest_comp ( - part text, - unit char(4), - size float -); -create table rtest_unitfact ( - unit char(4), - factor float -); -create view rtest_vcomp as - select X.part, (X.size * Y.factor) as size_in_cm - from rtest_comp X, rtest_unitfact Y - where X.unit = Y.unit; -insert into rtest_unitfact values ('m', 100.0); -insert into rtest_unitfact values ('cm', 1.0); -insert into rtest_unitfact values ('inch', 2.54); -insert into rtest_comp values ('p1', 'm', 5.0); -insert into rtest_comp values ('p2', 'm', 3.0); -insert into rtest_comp values ('p3', 'cm', 5.0); -insert into rtest_comp values ('p4', 'cm', 15.0); -insert into rtest_comp values ('p5', 'inch', 7.0); -insert into rtest_comp values ('p6', 'inch', 4.4); -select * from rtest_vcomp order by part; - part | size_in_cm -------+-------------------- - p1 | 500 - p2 | 300 - p3 | 5 - p4 | 15 - p5 | 17.78 - p6 | 11.176000000000002 -(6 rows) - -select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >; - part | size_in_cm -------+-------------------- - p1 | 500 - p2 | 300 - p5 | 17.78 - p4 | 15 - p6 | 11.176000000000002 -(5 rows) - --- --- In addition run the (slightly modified) queries from the --- programmers manual section on the rule system. --- -CREATE TABLE shoe_data ( - shoename char(10), -- primary key - sh_avail integer, -- available # of pairs - slcolor char(10), -- preferred shoelace color - slminlen float, -- minimum shoelace length - slmaxlen float, -- maximum shoelace length - slunit char(8) -- length unit -); -CREATE TABLE shoelace_data ( - sl_name char(10), -- primary key - sl_avail integer, -- available # of pairs - sl_color char(10), -- shoelace color - sl_len float, -- shoelace length - sl_unit char(8) -- length unit -); -CREATE TABLE unit ( - un_name char(8), -- the primary key - un_fact float -- factor to transform to cm -); -CREATE VIEW shoe AS - SELECT sh.shoename, - sh.sh_avail, - sh.slcolor, - sh.slminlen, - sh.slminlen * un.un_fact AS slminlen_cm, - sh.slmaxlen, - sh.slmaxlen * un.un_fact AS slmaxlen_cm, - sh.slunit - FROM shoe_data sh, unit un - WHERE sh.slunit = un.un_name; -CREATE VIEW shoelace AS - SELECT s.sl_name, - s.sl_avail, - s.sl_color, - s.sl_len, - s.sl_unit, - s.sl_len * u.un_fact AS sl_len_cm - FROM shoelace_data s, unit u - WHERE s.sl_unit = u.un_name; -CREATE VIEW shoe_ready AS - SELECT rsh.shoename, - rsh.sh_avail, - rsl.sl_name, - rsl.sl_avail, - int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail - FROM shoe rsh, shoelace rsl - WHERE rsl.sl_color = rsh.slcolor - AND rsl.sl_len_cm >= rsh.slminlen_cm - AND rsl.sl_len_cm <= rsh.slmaxlen_cm; -INSERT INTO unit VALUES ('cm', 1.0); -INSERT INTO unit VALUES ('m', 100.0); -INSERT INTO unit VALUES ('inch', 2.54); -INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm'); -INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch'); -INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm'); -INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch'); -INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm'); -INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm'); -INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0 , 'inch'); -INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0 , 'inch'); -INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0 , 'm'); -INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9 , 'm'); -INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60 , 'cm'); -INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40 , 'inch'); --- SELECTs in doc -SELECT * FROM shoelace ORDER BY sl_name; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl1 | 5 | black | 80 | cm | 80 - sl2 | 6 | black | 100 | cm | 100 - sl3 | 0 | black | 35 | inch | 88.9 - sl4 | 8 | black | 40 | inch | 101.6 - sl5 | 4 | brown | 1 | m | 100 - sl6 | 0 | brown | 0.9 | m | 90 - sl7 | 7 | brown | 60 | cm | 60 - sl8 | 1 | brown | 40 | inch | 101.6 -(8 rows) - -SELECT * FROM shoe_ready WHERE total_avail >= 2 ORDER BY 1; - shoename | sh_avail | sl_name | sl_avail | total_avail -------------+----------+------------+----------+------------- - sh1 | 2 | sl1 | 5 | 2 - sh3 | 4 | sl7 | 7 | 4 -(2 rows) - - CREATE TABLE shoelace_log ( - sl_name char(10), -- shoelace changed - sl_avail integer, -- new available value - log_who name, -- who did it - log_when timestamp -- when - ); --- Want "log_who" to be CURRENT_USER, --- but that is non-portable for the regression test --- - thomas 1999-02-21 - CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data - WHERE NEW.sl_avail != OLD.sl_avail - DO INSERT INTO shoelace_log VALUES ( - NEW.sl_name, - NEW.sl_avail, - 'Al Bundy', - 'epoch' - ); -UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7'; -SELECT * FROM shoelace_log; - sl_name | sl_avail | log_who | log_when -------------+----------+----------+-------------------------- - sl7 | 6 | Al Bundy | Thu Jan 01 00:00:00 1970 -(1 row) - - CREATE RULE shoelace_ins AS ON INSERT TO shoelace - DO INSTEAD - INSERT INTO shoelace_data VALUES ( - NEW.sl_name, - NEW.sl_avail, - NEW.sl_color, - NEW.sl_len, - NEW.sl_unit); - CREATE RULE shoelace_upd AS ON UPDATE TO shoelace - DO INSTEAD - UPDATE shoelace_data SET - sl_name = NEW.sl_name, - sl_avail = NEW.sl_avail, - sl_color = NEW.sl_color, - sl_len = NEW.sl_len, - sl_unit = NEW.sl_unit - WHERE sl_name = OLD.sl_name; - CREATE RULE shoelace_del AS ON DELETE TO shoelace - DO INSTEAD - DELETE FROM shoelace_data - WHERE sl_name = OLD.sl_name; - CREATE TABLE shoelace_arrive ( - arr_name char(10), - arr_quant integer - ); - CREATE TABLE shoelace_ok ( - ok_name char(10), - ok_quant integer - ); - CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok - DO INSTEAD - UPDATE shoelace SET - sl_avail = sl_avail + NEW.ok_quant - WHERE sl_name = NEW.ok_name; -INSERT INTO shoelace_arrive VALUES ('sl3', 10); -INSERT INTO shoelace_arrive VALUES ('sl6', 20); -INSERT INTO shoelace_arrive VALUES ('sl8', 20); -SELECT * FROM shoelace ORDER BY sl_name; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl1 | 5 | black | 80 | cm | 80 - sl2 | 6 | black | 100 | cm | 100 - sl3 | 0 | black | 35 | inch | 88.9 - sl4 | 8 | black | 40 | inch | 101.6 - sl5 | 4 | brown | 1 | m | 100 - sl6 | 0 | brown | 0.9 | m | 90 - sl7 | 6 | brown | 60 | cm | 60 - sl8 | 1 | brown | 40 | inch | 101.6 -(8 rows) - -insert into shoelace_ok select * from shoelace_arrive; -SELECT * FROM shoelace ORDER BY sl_name; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl1 | 5 | black | 80 | cm | 80 - sl2 | 6 | black | 100 | cm | 100 - sl3 | 10 | black | 35 | inch | 88.9 - sl4 | 8 | black | 40 | inch | 101.6 - sl5 | 4 | brown | 1 | m | 100 - sl6 | 20 | brown | 0.9 | m | 90 - sl7 | 6 | brown | 60 | cm | 60 - sl8 | 21 | brown | 40 | inch | 101.6 -(8 rows) - -SELECT * FROM shoelace_log ORDER BY sl_name; - sl_name | sl_avail | log_who | log_when -------------+----------+----------+-------------------------- - sl3 | 10 | Al Bundy | Thu Jan 01 00:00:00 1970 - sl6 | 20 | Al Bundy | Thu Jan 01 00:00:00 1970 - sl7 | 6 | Al Bundy | Thu Jan 01 00:00:00 1970 - sl8 | 21 | Al Bundy | Thu Jan 01 00:00:00 1970 -(4 rows) - - CREATE VIEW shoelace_obsolete AS - SELECT * FROM shoelace WHERE NOT EXISTS - (SELECT shoename FROM shoe WHERE slcolor = sl_color); - CREATE VIEW shoelace_candelete AS - SELECT * FROM shoelace_obsolete WHERE sl_avail = 0; -insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0); -insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0); --- Unsupported (even though a similar updatable view construct is) -insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0) - on conflict do nothing; -ERROR: INSERT with ON CONFLICT clause cannot be used with table that has INSERT or UPDATE rules -SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl9 | 0 | pink | 35 | inch | 88.9 - sl10 | 1000 | magenta | 40 | inch | 101.6 -(2 rows) - -SELECT * FROM shoelace_candelete; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl9 | 0 | pink | 35 | inch | 88.9 -(1 row) - -DELETE FROM shoelace WHERE EXISTS - (SELECT * FROM shoelace_candelete - WHERE sl_name = shoelace.sl_name); -SELECT * FROM shoelace ORDER BY sl_name; - sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -------------+----------+------------+--------+----------+----------- - sl1 | 5 | black | 80 | cm | 80 - sl10 | 1000 | magenta | 40 | inch | 101.6 - sl2 | 6 | black | 100 | cm | 100 - sl3 | 10 | black | 35 | inch | 88.9 - sl4 | 8 | black | 40 | inch | 101.6 - sl5 | 4 | brown | 1 | m | 100 - sl6 | 20 | brown | 0.9 | m | 90 - sl7 | 6 | brown | 60 | cm | 60 - sl8 | 21 | brown | 40 | inch | 101.6 -(9 rows) - -SELECT * FROM shoe ORDER BY shoename; - shoename | sh_avail | slcolor | slminlen | slminlen_cm | slmaxlen | slmaxlen_cm | slunit -------------+----------+------------+----------+-------------+----------+-------------+---------- - sh1 | 2 | black | 70 | 70 | 90 | 90 | cm - sh2 | 0 | black | 30 | 76.2 | 40 | 101.6 | inch - sh3 | 4 | brown | 50 | 50 | 65 | 65 | cm - sh4 | 3 | brown | 40 | 101.6 | 50 | 127 | inch -(4 rows) - -SELECT count(*) FROM shoe; - count -------- - 4 -(1 row) - --- --- Simple test of qualified ON INSERT ... this did not work in 7.0 ... --- -create table rules_foo (f1 int); -create table rules_foo2 (f1 int); -create rule rules_foorule as on insert to rules_foo where f1 < 100 -do instead nothing; -insert into rules_foo values(1); -insert into rules_foo values(1001); -select * from rules_foo; - f1 ------- - 1001 -(1 row) - -drop rule rules_foorule on rules_foo; --- this should fail because f1 is not exposed for unqualified reference: -create rule rules_foorule as on insert to rules_foo where f1 < 100 -do instead insert into rules_foo2 values (f1); -ERROR: column "f1" does not exist -LINE 2: do instead insert into rules_foo2 values (f1); - ^ -HINT: There is a column named "f1" in table "old", but it cannot be referenced from this part of the query. --- this is the correct way: -create rule rules_foorule as on insert to rules_foo where f1 < 100 -do instead insert into rules_foo2 values (new.f1); -insert into rules_foo values(2); -insert into rules_foo values(100); -select * from rules_foo; - f1 ------- - 1001 - 100 -(2 rows) - -select * from rules_foo2; - f1 ----- - 2 -(1 row) - -drop rule rules_foorule on rules_foo; -drop table rules_foo; -drop table rules_foo2; --- --- Test rules containing INSERT ... SELECT, which is a very ugly special --- case as of 7.1. Example is based on bug report from Joel Burton. --- -create table pparent (pid int, txt text); -insert into pparent values (1,'parent1'); -insert into pparent values (2,'parent2'); -create table cchild (pid int, descrip text); -insert into cchild values (1,'descrip1'); -create view vview as - select pparent.pid, txt, descrip from - pparent left join cchild using (pid); -create rule rrule as - on update to vview do instead -( - insert into cchild (pid, descrip) - select old.pid, new.descrip where old.descrip isnull; - update cchild set descrip = new.descrip where cchild.pid = old.pid; -); -select * from vview; - pid | txt | descrip ------+---------+---------- - 1 | parent1 | descrip1 - 2 | parent2 | -(2 rows) - -update vview set descrip='test1' where pid=1; -select * from vview; - pid | txt | descrip ------+---------+--------- - 1 | parent1 | test1 - 2 | parent2 | -(2 rows) - -update vview set descrip='test2' where pid=2; -select * from vview; - pid | txt | descrip ------+---------+--------- - 1 | parent1 | test1 - 2 | parent2 | test2 -(2 rows) - -update vview set descrip='test3' where pid=3; -select * from vview; - pid | txt | descrip ------+---------+--------- - 1 | parent1 | test1 - 2 | parent2 | test2 -(2 rows) - -select * from cchild; - pid | descrip ------+--------- - 1 | test1 - 2 | test2 -(2 rows) - -drop rule rrule on vview; -drop view vview; -drop table pparent; -drop table cchild; --- --- Check that ruleutils are working --- --- temporarily disable fancy output, so view changes create less diff noise -\a\t -SELECT viewname, definition FROM pg_views -WHERE schemaname IN ('pg_catalog', 'public') -ORDER BY viewname; -iexit| SELECT ih.name, - ih.thepath, - interpt_pp(ih.thepath, r.thepath) AS exit - FROM ihighway ih, - ramp r - WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data - FROM view_base_table - GROUP BY view_base_table.key; -key_dependent_view_no_cols| SELECT - FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt - FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot - FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot - FROM mvtest_tvvm; -pg_available_extension_versions| SELECT e.name, - e.version, - (x.extname IS NOT NULL) AS installed, - e.superuser, - e.trusted, - e.relocatable, - e.schema, - e.requires, - e.comment - FROM (pg_available_extension_versions() e(name, version, superuser, trusted, relocatable, schema, requires, comment) - LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion)))); -pg_available_extensions| SELECT e.name, - e.default_version, - x.extversion AS installed_version, - e.comment - FROM (pg_available_extensions() e(name, default_version, comment) - LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes - FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting - FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time - FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error - FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, - ARRAY( SELECT pg_auth_members.member - FROM pg_auth_members - WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist - FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error - FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_indexes| SELECT n.nspname AS schemaname, - c.relname AS tablename, - i.relname AS indexname, - t.spcname AS tablespace, - pg_get_indexdef(i.oid) AS indexdef - FROM ((((pg_index x - JOIN pg_class c ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) - WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart - FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); -pg_matviews| SELECT n.nspname AS schemaname, - c.relname AS matviewname, - pg_get_userbyid(c.relowner) AS matviewowner, - t.spcname AS tablespace, - c.relhasindex AS hasindexes, - c.relispopulated AS ispopulated, - pg_get_viewdef(c.oid) AS definition - FROM ((pg_class c - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) - WHERE (c.relkind = 'm'::"char"); -pg_policies| SELECT n.nspname AS schemaname, - c.relname AS tablename, - pol.polname AS policyname, - CASE - WHEN pol.polpermissive THEN 'PERMISSIVE'::text - ELSE 'RESTRICTIVE'::text - END AS permissive, - CASE - WHEN (pol.polroles = '{0}'::oid[]) THEN (string_to_array('public'::text, ''::text))::name[] - ELSE ARRAY( SELECT pg_authid.rolname - FROM pg_authid - WHERE (pg_authid.oid = ANY (pol.polroles)) - ORDER BY pg_authid.rolname) - END AS roles, - CASE pol.polcmd - WHEN 'r'::"char" THEN 'SELECT'::text - WHEN 'a'::"char" THEN 'INSERT'::text - WHEN 'w'::"char" THEN 'UPDATE'::text - WHEN 'd'::"char" THEN 'DELETE'::text - WHEN '*'::"char" THEN 'ALL'::text - ELSE NULL::text - END AS cmd, - pg_get_expr(pol.polqual, pol.polrelid) AS qual, - pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check - FROM ((pg_policy pol - JOIN pg_class c ON ((c.oid = pol.polrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans - FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); -pg_prepared_xacts| SELECT p.transaction, - p.gid, - p.prepared, - u.rolname AS owner, - d.datname AS database - FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) - LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) - LEFT JOIN pg_database d ON ((p.dbid = d.oid))); -pg_publication_tables| SELECT p.pubname, - n.nspname AS schemaname, - c.relname AS tablename - FROM pg_publication p, - LATERAL pg_get_publication_tables((p.pubname)::text) gpt(relid), - (pg_class c - JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn - FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); -pg_replication_slots| SELECT l.slot_name, - l.plugin, - l.slot_type, - l.datoid, - d.datname AS database, - l.temporary, - l.active, - l.active_pid, - l.xmin, - l.catalog_xmin, - l.restart_lsn, - l.confirmed_flush_lsn, - l.wal_status, - l.safe_wal_size, - l.two_phase - FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, temporary, active, active_pid, xmin, catalog_xmin, restart_lsn, confirmed_flush_lsn, wal_status, safe_wal_size, two_phase) - LEFT JOIN pg_database d ON ((l.datoid = d.oid))); -pg_roles| SELECT pg_authid.rolname, - pg_authid.rolsuper, - pg_authid.rolinherit, - pg_authid.rolcreaterole, - pg_authid.rolcreatedb, - pg_authid.rolcanlogin, - pg_authid.rolreplication, - pg_authid.rolconnlimit, - '********'::text AS rolpassword, - pg_authid.rolvaliduntil, - pg_authid.rolbypassrls, - s.setconfig AS rolconfig, - pg_authid.oid - FROM (pg_authid - LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))); -pg_rules| SELECT n.nspname AS schemaname, - c.relname AS tablename, - r.rulename, - pg_get_ruledef(r.oid) AS definition - FROM ((pg_rewrite r - JOIN pg_class c ON ((c.oid = r.ev_class))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (r.rulename <> '_RETURN'::name); -pg_seclabels| SELECT l.objoid, - l.classoid, - l.objsubid, - CASE - WHEN (rel.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])) THEN 'table'::text - WHEN (rel.relkind = 'v'::"char") THEN 'view'::text - WHEN (rel.relkind = 'm'::"char") THEN 'materialized view'::text - WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text - WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text - ELSE NULL::text - END AS objtype, - rel.relnamespace AS objnamespace, - CASE - WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) - ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) - END AS objname, - l.provider, - l.label - FROM ((pg_seclabel l - JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) - JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'column'::text AS objtype, - rel.relnamespace AS objnamespace, - (( - CASE - WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) - ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) - END || '.'::text) || (att.attname)::text) AS objname, - l.provider, - l.label - FROM (((pg_seclabel l - JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) - JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) - JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) - WHERE (l.objsubid <> 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - CASE pro.prokind - WHEN 'a'::"char" THEN 'aggregate'::text - WHEN 'f'::"char" THEN 'function'::text - WHEN 'p'::"char" THEN 'procedure'::text - WHEN 'w'::"char" THEN 'window'::text - ELSE NULL::text - END AS objtype, - pro.pronamespace AS objnamespace, - ((( - CASE - WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) - ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) - END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, - l.provider, - l.label - FROM ((pg_seclabel l - JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) - JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - CASE - WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text - ELSE 'type'::text - END AS objtype, - typ.typnamespace AS objnamespace, - CASE - WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) - ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) - END AS objname, - l.provider, - l.label - FROM ((pg_seclabel l - JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) - JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'large object'::text AS objtype, - NULL::oid AS objnamespace, - (l.objoid)::text AS objname, - l.provider, - l.label - FROM (pg_seclabel l - JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) - WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0)) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'language'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((lan.lanname)::text) AS objname, - l.provider, - l.label - FROM (pg_seclabel l - JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'schema'::text AS objtype, - nsp.oid AS objnamespace, - quote_ident((nsp.nspname)::text) AS objname, - l.provider, - l.label - FROM (pg_seclabel l - JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'event trigger'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((evt.evtname)::text) AS objname, - l.provider, - l.label - FROM (pg_seclabel l - JOIN pg_event_trigger evt ON (((l.classoid = evt.tableoid) AND (l.objoid = evt.oid)))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - l.objsubid, - 'publication'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((p.pubname)::text) AS objname, - l.provider, - l.label - FROM (pg_seclabel l - JOIN pg_publication p ON (((l.classoid = p.tableoid) AND (l.objoid = p.oid)))) - WHERE (l.objsubid = 0) -UNION ALL - SELECT l.objoid, - l.classoid, - 0 AS objsubid, - 'subscription'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((s.subname)::text) AS objname, - l.provider, - l.label - FROM (pg_shseclabel l - JOIN pg_subscription s ON (((l.classoid = s.tableoid) AND (l.objoid = s.oid)))) -UNION ALL - SELECT l.objoid, - l.classoid, - 0 AS objsubid, - 'database'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((dat.datname)::text) AS objname, - l.provider, - l.label - FROM (pg_shseclabel l - JOIN pg_database dat ON (((l.classoid = dat.tableoid) AND (l.objoid = dat.oid)))) -UNION ALL - SELECT l.objoid, - l.classoid, - 0 AS objsubid, - 'tablespace'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((spc.spcname)::text) AS objname, - l.provider, - l.label - FROM (pg_shseclabel l - JOIN pg_tablespace spc ON (((l.classoid = spc.tableoid) AND (l.objoid = spc.oid)))) -UNION ALL - SELECT l.objoid, - l.classoid, - 0 AS objsubid, - 'role'::text AS objtype, - NULL::oid AS objnamespace, - quote_ident((rol.rolname)::text) AS objname, - l.provider, - l.label - FROM (pg_shseclabel l - JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid)))); -pg_sequences| SELECT n.nspname AS schemaname, - c.relname AS sequencename, - pg_get_userbyid(c.relowner) AS sequenceowner, - (s.seqtypid)::regtype AS data_type, - s.seqstart AS start_value, - s.seqmin AS min_value, - s.seqmax AS max_value, - s.seqincrement AS increment_by, - s.seqcycle AS cycle, - s.seqcache AS cache_size, - CASE - WHEN has_sequence_privilege(c.oid, 'SELECT,USAGE'::text) THEN pg_sequence_last_value((c.oid)::regclass) - ELSE NULL::bigint - END AS last_value - FROM ((pg_sequence s - JOIN pg_class c ON ((c.oid = s.seqrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart - FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); -pg_shadow| SELECT pg_authid.rolname AS usename, - pg_authid.oid AS usesysid, - pg_authid.rolcreatedb AS usecreatedb, - pg_authid.rolsuper AS usesuper, - pg_authid.rolreplication AS userepl, - pg_authid.rolbypassrls AS usebypassrls, - pg_authid.rolpassword AS passwd, - pg_authid.rolvaliduntil AS valuntil, - s.setconfig AS useconfig - FROM (pg_authid - LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) - WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size - FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); -pg_stat_activity| SELECT s.datid, - d.datname, - s.pid, - s.leader_pid, - s.usesysid, - u.rolname AS usename, - s.application_name, - s.client_addr, - s.client_hostname, - s.client_port, - s.backend_start, - s.xact_start, - s.query_start, - s.state_change, - s.wait_event_type, - s.wait_event, - s.state, - s.backend_xid, - s.backend_xmin, - s.query_id, - s.query, - s.backend_type - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - LEFT JOIN pg_database d ON ((s.datid = d.oid))) - LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); -pg_stat_all_indexes| SELECT c.oid AS relid, - i.oid AS indexrelid, - n.nspname AS schemaname, - c.relname, - i.relname AS indexrelname, - pg_stat_get_numscans(i.oid) AS idx_scan, - pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, - pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch - FROM (((pg_class c - JOIN pg_index x ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_stat_all_tables| SELECT c.oid AS relid, - n.nspname AS schemaname, - c.relname, - pg_stat_get_numscans(c.oid) AS seq_scan, - pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, - ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, - pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, - pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, - pg_stat_get_live_tuples(c.oid) AS n_live_tup, - pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, - pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze, - pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum, - pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum, - pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum, - pg_stat_get_last_analyze_time(c.oid) AS last_analyze, - pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze, - pg_stat_get_vacuum_count(c.oid) AS vacuum_count, - pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count, - pg_stat_get_analyze_count(c.oid) AS analyze_count, - pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count - FROM ((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) - GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset - FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); -pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, - pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, - pg_stat_get_checkpoint_write_time() AS checkpoint_write_time, - pg_stat_get_checkpoint_sync_time() AS checkpoint_sync_time, - pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint, - pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean, - pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, - pg_stat_get_buf_written_backend() AS buffers_backend, - pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, - pg_stat_get_buf_alloc() AS buffers_alloc, - pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, - CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) - END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset - FROM ( SELECT 0 AS oid, - NULL::name AS datname - UNION ALL - SELECT pg_database.oid, - pg_database.datname - FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock - FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); -pg_stat_progress_analyze| SELECT s.pid, - s.datid, - d.datname, - s.relid, - CASE s.param1 - WHEN 0 THEN 'initializing'::text - WHEN 1 THEN 'acquiring sample rows'::text - WHEN 2 THEN 'acquiring inherited sample rows'::text - WHEN 3 THEN 'computing statistics'::text - WHEN 4 THEN 'computing extended statistics'::text - WHEN 5 THEN 'finalizing analyze'::text - ELSE NULL::text - END AS phase, - s.param2 AS sample_blks_total, - s.param3 AS sample_blks_scanned, - s.param4 AS ext_stats_total, - s.param5 AS ext_stats_computed, - s.param6 AS child_tables_total, - s.param7 AS child_tables_done, - (s.param8)::oid AS current_child_table_relid - FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) - LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 - WHEN 0 THEN 'initializing'::text - WHEN 1 THEN 'waiting for checkpoint to finish'::text - WHEN 2 THEN 'estimating backup size'::text - WHEN 3 THEN 'streaming database files'::text - WHEN 4 THEN 'waiting for wal archiving to finish'::text - WHEN 5 THEN 'transferring wal files'::text - ELSE NULL::text - END AS phase, - CASE s.param2 - WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 - END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed - FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); -pg_stat_progress_cluster| SELECT s.pid, - s.datid, - d.datname, - s.relid, - CASE s.param1 - WHEN 1 THEN 'CLUSTER'::text - WHEN 2 THEN 'VACUUM FULL'::text - ELSE NULL::text - END AS command, - CASE s.param2 - WHEN 0 THEN 'initializing'::text - WHEN 1 THEN 'seq scanning heap'::text - WHEN 2 THEN 'index scanning heap'::text - WHEN 3 THEN 'sorting tuples'::text - WHEN 4 THEN 'writing new heap'::text - WHEN 5 THEN 'swapping relation files'::text - WHEN 6 THEN 'rebuilding index'::text - WHEN 7 THEN 'performing final cleanup'::text - ELSE NULL::text - END AS phase, - (s.param3)::oid AS cluster_index_relid, - s.param4 AS heap_tuples_scanned, - s.param5 AS heap_tuples_written, - s.param6 AS heap_blks_total, - s.param7 AS heap_blks_scanned, - s.param8 AS index_rebuild_count - FROM (pg_stat_get_progress_info('CLUSTER'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) - LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_copy| SELECT s.pid, - s.datid, - d.datname, - s.relid, - CASE s.param5 - WHEN 1 THEN 'COPY FROM'::text - WHEN 2 THEN 'COPY TO'::text - ELSE NULL::text - END AS command, - CASE s.param6 - WHEN 1 THEN 'FILE'::text - WHEN 2 THEN 'PROGRAM'::text - WHEN 3 THEN 'PIPE'::text - WHEN 4 THEN 'CALLBACK'::text - ELSE NULL::text - END AS type, - s.param1 AS bytes_processed, - s.param2 AS bytes_total, - s.param3 AS tuples_processed, - s.param4 AS tuples_excluded - FROM (pg_stat_get_progress_info('COPY'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) - LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_create_index| SELECT s.pid, - s.datid, - d.datname, - s.relid, - (s.param7)::oid AS index_relid, - CASE s.param1 - WHEN 1 THEN 'CREATE INDEX'::text - WHEN 2 THEN 'CREATE INDEX CONCURRENTLY'::text - WHEN 3 THEN 'REINDEX'::text - WHEN 4 THEN 'REINDEX CONCURRENTLY'::text - ELSE NULL::text - END AS command, - CASE s.param10 - WHEN 0 THEN 'initializing'::text - WHEN 1 THEN 'waiting for writers before build'::text - WHEN 2 THEN ('building index'::text || COALESCE((': '::text || pg_indexam_progress_phasename((s.param9)::oid, s.param11)), ''::text)) - WHEN 3 THEN 'waiting for writers before validation'::text - WHEN 4 THEN 'index validation: scanning index'::text - WHEN 5 THEN 'index validation: sorting tuples'::text - WHEN 6 THEN 'index validation: scanning table'::text - WHEN 7 THEN 'waiting for old snapshots'::text - WHEN 8 THEN 'waiting for readers before marking dead'::text - WHEN 9 THEN 'waiting for readers before dropping'::text - ELSE NULL::text - END AS phase, - s.param4 AS lockers_total, - s.param5 AS lockers_done, - s.param6 AS current_locker_pid, - s.param16 AS blocks_total, - s.param17 AS blocks_done, - s.param12 AS tuples_total, - s.param13 AS tuples_done, - s.param14 AS partitions_total, - s.param15 AS partitions_done - FROM (pg_stat_get_progress_info('CREATE INDEX'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) - LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_vacuum| SELECT s.pid, - s.datid, - d.datname, - s.relid, - CASE s.param1 - WHEN 0 THEN 'initializing'::text - WHEN 1 THEN 'scanning heap'::text - WHEN 2 THEN 'vacuuming indexes'::text - WHEN 3 THEN 'vacuuming heap'::text - WHEN 4 THEN 'cleaning up indexes'::text - WHEN 5 THEN 'truncating heap'::text - WHEN 6 THEN 'performing final cleanup'::text - ELSE NULL::text - END AS phase, - s.param2 AS heap_blks_total, - s.param3 AS heap_blks_scanned, - s.param4 AS heap_blks_vacuumed, - s.param5 AS index_vacuum_count, - s.param6 AS max_dead_tuples, - s.param7 AS num_dead_tuples - FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) - LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_replication| SELECT s.pid, - s.usesysid, - u.rolname AS usename, - s.application_name, - s.client_addr, - s.client_hostname, - s.client_port, - s.backend_start, - s.backend_xmin, - w.state, - w.sent_lsn, - w.write_lsn, - w.flush_lsn, - w.replay_lsn, - w.write_lag, - w.flush_lag, - w.replay_lag, - w.sync_priority, - w.sync_state, - w.reply_time - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid))) - LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); -pg_stat_replication_slots| SELECT s.slot_name, - s.spill_txns, - s.spill_count, - s.spill_bytes, - s.stream_txns, - s.stream_count, - s.stream_bytes, - s.total_txns, - s.total_bytes, - s.stats_reset - FROM pg_replication_slots r, - LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) - WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset - FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); -pg_stat_subscription| SELECT su.oid AS subid, - su.subname, - st.pid, - st.relid, - st.received_lsn, - st.last_msg_send_time, - st.last_msg_receipt_time, - st.latest_end_lsn, - st.latest_end_time - FROM (pg_subscription su - LEFT JOIN pg_stat_get_subscription(NULL::oid) st(subid, relid, pid, received_lsn, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time) ON ((st.subid = su.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch - FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count - FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); -pg_stat_user_functions| SELECT p.oid AS funcid, - n.nspname AS schemaname, - p.proname AS funcname, - pg_stat_get_function_calls(p.oid) AS calls, - pg_stat_get_function_total_time(p.oid) AS total_time, - pg_stat_get_function_self_time(p.oid) AS self_time - FROM (pg_proc p - LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) - WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch - FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count - FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset - FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo - FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); -pg_stat_xact_all_tables| SELECT c.oid AS relid, - n.nspname AS schemaname, - c.relname, - pg_stat_get_xact_numscans(c.oid) AS seq_scan, - pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan, - ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, - pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, - pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, - pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, - pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd - FROM ((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) - GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd - FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); -pg_stat_xact_user_functions| SELECT p.oid AS funcid, - n.nspname AS schemaname, - p.proname AS funcname, - pg_stat_get_xact_function_calls(p.oid) AS calls, - pg_stat_get_xact_function_total_time(p.oid) AS total_time, - pg_stat_get_xact_function_self_time(p.oid) AS self_time - FROM (pg_proc p - LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) - WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd - FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); -pg_statio_all_indexes| SELECT c.oid AS relid, - i.oid AS indexrelid, - n.nspname AS schemaname, - c.relname, - i.relname AS indexrelname, - (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS idx_blks_read, - pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit - FROM (((pg_class c - JOIN pg_index x ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_all_sequences| SELECT c.oid AS relid, - n.nspname AS schemaname, - c.relname, - (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS blks_read, - pg_stat_get_blocks_hit(c.oid) AS blks_hit - FROM (pg_class c - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = 'S'::"char"); -pg_statio_all_tables| SELECT c.oid AS relid, - n.nspname AS schemaname, - c.relname, - (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS heap_blks_read, - pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit, - (sum((pg_stat_get_blocks_fetched(i.indexrelid) - pg_stat_get_blocks_hit(i.indexrelid))))::bigint AS idx_blks_read, - (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit, - (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read, - pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, - (pg_stat_get_blocks_fetched(x.indexrelid) - pg_stat_get_blocks_hit(x.indexrelid)) AS tidx_blks_read, - pg_stat_get_blocks_hit(x.indexrelid) AS tidx_blks_hit - FROM ((((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) - LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) - LEFT JOIN pg_index x ON ((t.oid = x.indrelid))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) - GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit - FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit - FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit - FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit - FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit - FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit - FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stats| SELECT n.nspname AS schemaname, - c.relname AS tablename, - a.attname, - s.stainherit AS inherited, - s.stanullfrac AS null_frac, - s.stawidth AS avg_width, - s.stadistinct AS n_distinct, - CASE - WHEN (s.stakind1 = 1) THEN s.stavalues1 - WHEN (s.stakind2 = 1) THEN s.stavalues2 - WHEN (s.stakind3 = 1) THEN s.stavalues3 - WHEN (s.stakind4 = 1) THEN s.stavalues4 - WHEN (s.stakind5 = 1) THEN s.stavalues5 - ELSE NULL::anyarray - END AS most_common_vals, - CASE - WHEN (s.stakind1 = 1) THEN s.stanumbers1 - WHEN (s.stakind2 = 1) THEN s.stanumbers2 - WHEN (s.stakind3 = 1) THEN s.stanumbers3 - WHEN (s.stakind4 = 1) THEN s.stanumbers4 - WHEN (s.stakind5 = 1) THEN s.stanumbers5 - ELSE NULL::real[] - END AS most_common_freqs, - CASE - WHEN (s.stakind1 = 2) THEN s.stavalues1 - WHEN (s.stakind2 = 2) THEN s.stavalues2 - WHEN (s.stakind3 = 2) THEN s.stavalues3 - WHEN (s.stakind4 = 2) THEN s.stavalues4 - WHEN (s.stakind5 = 2) THEN s.stavalues5 - ELSE NULL::anyarray - END AS histogram_bounds, - CASE - WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] - WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] - WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] - WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] - WHEN (s.stakind5 = 3) THEN s.stanumbers5[1] - ELSE NULL::real - END AS correlation, - CASE - WHEN (s.stakind1 = 4) THEN s.stavalues1 - WHEN (s.stakind2 = 4) THEN s.stavalues2 - WHEN (s.stakind3 = 4) THEN s.stavalues3 - WHEN (s.stakind4 = 4) THEN s.stavalues4 - WHEN (s.stakind5 = 4) THEN s.stavalues5 - ELSE NULL::anyarray - END AS most_common_elems, - CASE - WHEN (s.stakind1 = 4) THEN s.stanumbers1 - WHEN (s.stakind2 = 4) THEN s.stanumbers2 - WHEN (s.stakind3 = 4) THEN s.stanumbers3 - WHEN (s.stakind4 = 4) THEN s.stanumbers4 - WHEN (s.stakind5 = 4) THEN s.stanumbers5 - ELSE NULL::real[] - END AS most_common_elem_freqs, - CASE - WHEN (s.stakind1 = 5) THEN s.stanumbers1 - WHEN (s.stakind2 = 5) THEN s.stanumbers2 - WHEN (s.stakind3 = 5) THEN s.stanumbers3 - WHEN (s.stakind4 = 5) THEN s.stanumbers4 - WHEN (s.stakind5 = 5) THEN s.stanumbers5 - ELSE NULL::real[] - END AS elem_count_histogram - FROM (((pg_statistic s - JOIN pg_class c ON ((c.oid = s.starelid))) - JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); -pg_stats_ext| SELECT cn.nspname AS schemaname, - c.relname AS tablename, - sn.nspname AS statistics_schemaname, - s.stxname AS statistics_name, - pg_get_userbyid(s.stxowner) AS statistics_owner, - ( SELECT array_agg(a.attname ORDER BY a.attnum) AS array_agg - FROM (unnest(s.stxkeys) k(k) - JOIN pg_attribute a ON (((a.attrelid = s.stxrelid) AND (a.attnum = k.k))))) AS attnames, - pg_get_statisticsobjdef_expressions(s.oid) AS exprs, - s.stxkind AS kinds, - sd.stxdndistinct AS n_distinct, - sd.stxddependencies AS dependencies, - m.most_common_vals, - m.most_common_val_nulls, - m.most_common_freqs, - m.most_common_base_freqs - FROM (((((pg_statistic_ext s - JOIN pg_class c ON ((c.oid = s.stxrelid))) - JOIN pg_statistic_ext_data sd ON ((s.oid = sd.stxoid))) - LEFT JOIN pg_namespace cn ON ((cn.oid = c.relnamespace))) - LEFT JOIN pg_namespace sn ON ((sn.oid = s.stxnamespace))) - LEFT JOIN LATERAL ( SELECT array_agg(pg_mcv_list_items."values") AS most_common_vals, - array_agg(pg_mcv_list_items.nulls) AS most_common_val_nulls, - array_agg(pg_mcv_list_items.frequency) AS most_common_freqs, - array_agg(pg_mcv_list_items.base_frequency) AS most_common_base_freqs - FROM pg_mcv_list_items(sd.stxdmcv) pg_mcv_list_items(index, "values", nulls, frequency, base_frequency)) m ON ((sd.stxdmcv IS NOT NULL))) - WHERE ((NOT (EXISTS ( SELECT 1 - FROM (unnest(s.stxkeys) k(k) - JOIN pg_attribute a ON (((a.attrelid = s.stxrelid) AND (a.attnum = k.k)))) - WHERE (NOT has_column_privilege(c.oid, a.attnum, 'select'::text))))) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); -pg_stats_ext_exprs| SELECT cn.nspname AS schemaname, - c.relname AS tablename, - sn.nspname AS statistics_schemaname, - s.stxname AS statistics_name, - pg_get_userbyid(s.stxowner) AS statistics_owner, - stat.expr, - (stat.a).stanullfrac AS null_frac, - (stat.a).stawidth AS avg_width, - (stat.a).stadistinct AS n_distinct, - CASE - WHEN ((stat.a).stakind1 = 1) THEN (stat.a).stavalues1 - WHEN ((stat.a).stakind2 = 1) THEN (stat.a).stavalues2 - WHEN ((stat.a).stakind3 = 1) THEN (stat.a).stavalues3 - WHEN ((stat.a).stakind4 = 1) THEN (stat.a).stavalues4 - WHEN ((stat.a).stakind5 = 1) THEN (stat.a).stavalues5 - ELSE NULL::anyarray - END AS most_common_vals, - CASE - WHEN ((stat.a).stakind1 = 1) THEN (stat.a).stanumbers1 - WHEN ((stat.a).stakind2 = 1) THEN (stat.a).stanumbers2 - WHEN ((stat.a).stakind3 = 1) THEN (stat.a).stanumbers3 - WHEN ((stat.a).stakind4 = 1) THEN (stat.a).stanumbers4 - WHEN ((stat.a).stakind5 = 1) THEN (stat.a).stanumbers5 - ELSE NULL::real[] - END AS most_common_freqs, - CASE - WHEN ((stat.a).stakind1 = 2) THEN (stat.a).stavalues1 - WHEN ((stat.a).stakind2 = 2) THEN (stat.a).stavalues2 - WHEN ((stat.a).stakind3 = 2) THEN (stat.a).stavalues3 - WHEN ((stat.a).stakind4 = 2) THEN (stat.a).stavalues4 - WHEN ((stat.a).stakind5 = 2) THEN (stat.a).stavalues5 - ELSE NULL::anyarray - END AS histogram_bounds, - CASE - WHEN ((stat.a).stakind1 = 3) THEN (stat.a).stanumbers1[1] - WHEN ((stat.a).stakind2 = 3) THEN (stat.a).stanumbers2[1] - WHEN ((stat.a).stakind3 = 3) THEN (stat.a).stanumbers3[1] - WHEN ((stat.a).stakind4 = 3) THEN (stat.a).stanumbers4[1] - WHEN ((stat.a).stakind5 = 3) THEN (stat.a).stanumbers5[1] - ELSE NULL::real - END AS correlation, - CASE - WHEN ((stat.a).stakind1 = 4) THEN (stat.a).stavalues1 - WHEN ((stat.a).stakind2 = 4) THEN (stat.a).stavalues2 - WHEN ((stat.a).stakind3 = 4) THEN (stat.a).stavalues3 - WHEN ((stat.a).stakind4 = 4) THEN (stat.a).stavalues4 - WHEN ((stat.a).stakind5 = 4) THEN (stat.a).stavalues5 - ELSE NULL::anyarray - END AS most_common_elems, - CASE - WHEN ((stat.a).stakind1 = 4) THEN (stat.a).stanumbers1 - WHEN ((stat.a).stakind2 = 4) THEN (stat.a).stanumbers2 - WHEN ((stat.a).stakind3 = 4) THEN (stat.a).stanumbers3 - WHEN ((stat.a).stakind4 = 4) THEN (stat.a).stanumbers4 - WHEN ((stat.a).stakind5 = 4) THEN (stat.a).stanumbers5 - ELSE NULL::real[] - END AS most_common_elem_freqs, - CASE - WHEN ((stat.a).stakind1 = 5) THEN (stat.a).stanumbers1 - WHEN ((stat.a).stakind2 = 5) THEN (stat.a).stanumbers2 - WHEN ((stat.a).stakind3 = 5) THEN (stat.a).stanumbers3 - WHEN ((stat.a).stakind4 = 5) THEN (stat.a).stanumbers4 - WHEN ((stat.a).stakind5 = 5) THEN (stat.a).stanumbers5 - ELSE NULL::real[] - END AS elem_count_histogram - FROM (((((pg_statistic_ext s - JOIN pg_class c ON ((c.oid = s.stxrelid))) - LEFT JOIN pg_statistic_ext_data sd ON ((s.oid = sd.stxoid))) - LEFT JOIN pg_namespace cn ON ((cn.oid = c.relnamespace))) - LEFT JOIN pg_namespace sn ON ((sn.oid = s.stxnamespace))) - JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr, - unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL))); -pg_tables| SELECT n.nspname AS schemaname, - c.relname AS tablename, - pg_get_userbyid(c.relowner) AS tableowner, - t.spcname AS tablespace, - c.relhasindex AS hasindexes, - c.relhasrules AS hasrules, - c.relhastriggers AS hastriggers, - c.relrowsecurity AS rowsecurity - FROM ((pg_class c - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst - FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst - FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, - '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig - FROM pg_shadow; -pg_user_mappings| SELECT u.oid AS umid, - s.oid AS srvid, - s.srvname, - u.umuser, - CASE - WHEN (u.umuser = (0)::oid) THEN 'public'::name - ELSE a.rolname - END AS usename, - CASE - WHEN (((u.umuser <> (0)::oid) AND (a.rolname = CURRENT_USER) AND (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text))) OR ((u.umuser = (0)::oid) AND pg_has_role(s.srvowner, 'USAGE'::text)) OR ( SELECT pg_authid.rolsuper - FROM pg_authid - WHERE (pg_authid.rolname = CURRENT_USER))) THEN u.umoptions - ELSE NULL::text[] - END AS umoptions - FROM ((pg_user_mapping u - JOIN pg_foreign_server s ON ((u.umserver = s.oid))) - LEFT JOIN pg_authid a ON ((a.oid = u.umuser))); -pg_views| SELECT n.nspname AS schemaname, - c.relname AS viewname, - pg_get_userbyid(c.relowner) AS viewowner, - pg_get_viewdef(c.oid) AS definition - FROM (pg_class c - LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) - WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b - FROM rtest_t1; -rtest_vcomp| SELECT x.part, - (x.size * y.factor) AS size_in_cm - FROM rtest_comp x, - rtest_unitfact y - WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b - FROM rtest_view1 x - WHERE (0 < ( SELECT count(*) AS count - FROM rtest_view2 y - WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b - FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b - FROM rtest_vview2 x - WHERE (0 < ( SELECT count(*) AS count - FROM rtest_view2 y - WHERE (y.a = x.a))); -rtest_vview4| SELECT x.a, - x.b, - count(y.a) AS refcount - FROM rtest_view1 x, - rtest_view2 y - WHERE (x.a = y.a) - GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount - FROM rtest_view1; -shoe| SELECT sh.shoename, - sh.sh_avail, - sh.slcolor, - sh.slminlen, - (sh.slminlen * un.un_fact) AS slminlen_cm, - sh.slmaxlen, - (sh.slmaxlen * un.un_fact) AS slmaxlen_cm, - sh.slunit - FROM shoe_data sh, - unit un - WHERE (sh.slunit = un.un_name); -shoe_ready| SELECT rsh.shoename, - rsh.sh_avail, - rsl.sl_name, - rsl.sl_avail, - int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail - FROM shoe rsh, - shoelace rsl - WHERE ((rsl.sl_color = rsh.slcolor) AND (rsl.sl_len_cm >= rsh.slminlen_cm) AND (rsl.sl_len_cm <= rsh.slmaxlen_cm)); -shoelace| SELECT s.sl_name, - s.sl_avail, - s.sl_color, - s.sl_len, - s.sl_unit, - (s.sl_len * u.un_fact) AS sl_len_cm - FROM shoelace_data s, - unit u - WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm - FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm - FROM shoelace - WHERE (NOT (EXISTS ( SELECT shoe.shoename - FROM shoe - WHERE (shoe.slcolor = shoelace.sl_color)))); -street| SELECT r.name, - r.thepath, - c.cname - FROM ONLY road r, - real_city c - WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id - FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id - FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal - FROM emp; -SELECT tablename, rulename, definition FROM pg_rules -WHERE schemaname IN ('pg_catalog', 'public') -ORDER BY tablename, rulename; -pg_settings|pg_settings_n|CREATE RULE pg_settings_n AS - ON UPDATE TO pg_catalog.pg_settings DO INSTEAD NOTHING; -pg_settings|pg_settings_u|CREATE RULE pg_settings_u AS - ON UPDATE TO pg_catalog.pg_settings - WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config; -rtest_emp|rtest_emp_del|CREATE RULE rtest_emp_del AS - ON DELETE TO public.rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) - VALUES (old.ename, CURRENT_USER, 'fired'::bpchar, '$0.00'::money, old.salary); -rtest_emp|rtest_emp_ins|CREATE RULE rtest_emp_ins AS - ON INSERT TO public.rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) - VALUES (new.ename, CURRENT_USER, 'hired'::bpchar, new.salary, '$0.00'::money); -rtest_emp|rtest_emp_upd|CREATE RULE rtest_emp_upd AS - ON UPDATE TO public.rtest_emp - WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) - VALUES (new.ename, CURRENT_USER, 'honored'::bpchar, new.salary, old.salary); -rtest_nothn1|rtest_nothn_r1|CREATE RULE rtest_nothn_r1 AS - ON INSERT TO public.rtest_nothn1 - WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD NOTHING; -rtest_nothn1|rtest_nothn_r2|CREATE RULE rtest_nothn_r2 AS - ON INSERT TO public.rtest_nothn1 - WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING; -rtest_nothn2|rtest_nothn_r3|CREATE RULE rtest_nothn_r3 AS - ON INSERT TO public.rtest_nothn2 - WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) - VALUES (new.a, new.b); -rtest_nothn2|rtest_nothn_r4|CREATE RULE rtest_nothn_r4 AS - ON INSERT TO public.rtest_nothn2 DO INSTEAD NOTHING; -rtest_order1|rtest_order_r1|CREATE RULE rtest_order_r1 AS - ON INSERT TO public.rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) - VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 1 - this should run 1st'::text); -rtest_order1|rtest_order_r2|CREATE RULE rtest_order_r2 AS - ON INSERT TO public.rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) - VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 2 - this should run 2nd'::text); -rtest_order1|rtest_order_r3|CREATE RULE rtest_order_r3 AS - ON INSERT TO public.rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) - VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 3 - this should run 3rd'::text); -rtest_order1|rtest_order_r4|CREATE RULE rtest_order_r4 AS - ON INSERT TO public.rtest_order1 - WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) - VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 4 - this should run 4th'::text); -rtest_person|rtest_pers_del|CREATE RULE rtest_pers_del AS - ON DELETE TO public.rtest_person DO DELETE FROM rtest_admin - WHERE (rtest_admin.pname = old.pname); -rtest_person|rtest_pers_upd|CREATE RULE rtest_pers_upd AS - ON UPDATE TO public.rtest_person DO UPDATE rtest_admin SET pname = new.pname - WHERE (rtest_admin.pname = old.pname); -rtest_system|rtest_sys_del|CREATE RULE rtest_sys_del AS - ON DELETE TO public.rtest_system DO ( DELETE FROM rtest_interface - WHERE (rtest_interface.sysname = old.sysname); - DELETE FROM rtest_admin - WHERE (rtest_admin.sysname = old.sysname); -); -rtest_system|rtest_sys_upd|CREATE RULE rtest_sys_upd AS - ON UPDATE TO public.rtest_system DO ( UPDATE rtest_interface SET sysname = new.sysname - WHERE (rtest_interface.sysname = old.sysname); - UPDATE rtest_admin SET sysname = new.sysname - WHERE (rtest_admin.sysname = old.sysname); -); -rtest_t4|rtest_t4_ins1|CREATE RULE rtest_t4_ins1 AS - ON INSERT TO public.rtest_t4 - WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD INSERT INTO rtest_t5 (a, b) - VALUES (new.a, new.b); -rtest_t4|rtest_t4_ins2|CREATE RULE rtest_t4_ins2 AS - ON INSERT TO public.rtest_t4 - WHERE ((new.a >= 20) AND (new.a < 30)) DO INSERT INTO rtest_t6 (a, b) - VALUES (new.a, new.b); -rtest_t5|rtest_t5_ins|CREATE RULE rtest_t5_ins AS - ON INSERT TO public.rtest_t5 - WHERE (new.a > 15) DO INSERT INTO rtest_t7 (a, b) - VALUES (new.a, new.b); -rtest_t6|rtest_t6_ins|CREATE RULE rtest_t6_ins AS - ON INSERT TO public.rtest_t6 - WHERE (new.a > 25) DO INSTEAD INSERT INTO rtest_t8 (a, b) - VALUES (new.a, new.b); -rtest_v1|rtest_v1_del|CREATE RULE rtest_v1_del AS - ON DELETE TO public.rtest_v1 DO INSTEAD DELETE FROM rtest_t1 - WHERE (rtest_t1.a = old.a); -rtest_v1|rtest_v1_ins|CREATE RULE rtest_v1_ins AS - ON INSERT TO public.rtest_v1 DO INSTEAD INSERT INTO rtest_t1 (a, b) - VALUES (new.a, new.b); -rtest_v1|rtest_v1_upd|CREATE RULE rtest_v1_upd AS - ON UPDATE TO public.rtest_v1 DO INSTEAD UPDATE rtest_t1 SET a = new.a, b = new.b - WHERE (rtest_t1.a = old.a); -shoelace|shoelace_del|CREATE RULE shoelace_del AS - ON DELETE TO public.shoelace DO INSTEAD DELETE FROM shoelace_data - WHERE (shoelace_data.sl_name = old.sl_name); -shoelace|shoelace_ins|CREATE RULE shoelace_ins AS - ON INSERT TO public.shoelace DO INSTEAD INSERT INTO shoelace_data (sl_name, sl_avail, sl_color, sl_len, sl_unit) - VALUES (new.sl_name, new.sl_avail, new.sl_color, new.sl_len, new.sl_unit); -shoelace|shoelace_upd|CREATE RULE shoelace_upd AS - ON UPDATE TO public.shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = new.sl_name, sl_avail = new.sl_avail, sl_color = new.sl_color, sl_len = new.sl_len, sl_unit = new.sl_unit - WHERE (shoelace_data.sl_name = old.sl_name); -shoelace_data|log_shoelace|CREATE RULE log_shoelace AS - ON UPDATE TO public.shoelace_data - WHERE (new.sl_avail <> old.sl_avail) DO INSERT INTO shoelace_log (sl_name, sl_avail, log_who, log_when) - VALUES (new.sl_name, new.sl_avail, 'Al Bundy'::name, 'Thu Jan 01 00:00:00 1970'::timestamp without time zone); -shoelace_ok|shoelace_ok_ins|CREATE RULE shoelace_ok_ins AS - ON INSERT TO public.shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) - WHERE (shoelace.sl_name = new.ok_name); --- restore normal output mode -\a\t --- --- CREATE OR REPLACE RULE --- -CREATE TABLE ruletest_tbl (a int, b int); -CREATE TABLE ruletest_tbl2 (a int, b int); -CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl - DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (10, 10); -INSERT INTO ruletest_tbl VALUES (99, 99); -CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl - DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (1000, 1000); -INSERT INTO ruletest_tbl VALUES (99, 99); -SELECT * FROM ruletest_tbl2; - a | b -------+------ - 10 | 10 - 1000 | 1000 -(2 rows) - --- Check that rewrite rules splitting one INSERT into multiple --- conditional statements does not disable FK checking. -create table rule_and_refint_t1 ( - id1a integer, - id1b integer, - primary key (id1a, id1b) -); -create table rule_and_refint_t2 ( - id2a integer, - id2c integer, - primary key (id2a, id2c) -); -create table rule_and_refint_t3 ( - id3a integer, - id3b integer, - id3c integer, - data text, - primary key (id3a, id3b, id3c), - foreign key (id3a, id3b) references rule_and_refint_t1 (id1a, id1b), - foreign key (id3a, id3c) references rule_and_refint_t2 (id2a, id2c) -); -insert into rule_and_refint_t1 values (1, 11); -insert into rule_and_refint_t1 values (1, 12); -insert into rule_and_refint_t1 values (2, 21); -insert into rule_and_refint_t1 values (2, 22); -insert into rule_and_refint_t2 values (1, 11); -insert into rule_and_refint_t2 values (1, 12); -insert into rule_and_refint_t2 values (2, 21); -insert into rule_and_refint_t2 values (2, 22); -insert into rule_and_refint_t3 values (1, 11, 11, 'row1'); -insert into rule_and_refint_t3 values (1, 11, 12, 'row2'); -insert into rule_and_refint_t3 values (1, 12, 11, 'row3'); -insert into rule_and_refint_t3 values (1, 12, 12, 'row4'); -insert into rule_and_refint_t3 values (1, 11, 13, 'row5'); -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3c_fkey" -DETAIL: Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2". -insert into rule_and_refint_t3 values (1, 13, 11, 'row6'); -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey" -DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1". --- Ordinary table -insert into rule_and_refint_t3 values (1, 13, 11, 'row6') - on conflict do nothing; -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey" -DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1". --- rule not fired, so fk violation -insert into rule_and_refint_t3 values (1, 13, 11, 'row6') - on conflict (id3a, id3b, id3c) do update - set id3b = excluded.id3b; -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey" -DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1". --- rule fired, so unsupported -insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0) - on conflict (sl_name) do update - set sl_avail = excluded.sl_avail; -ERROR: INSERT with ON CONFLICT clause cannot be used with table that has INSERT or UPDATE rules -create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3 - where (exists (select 1 from rule_and_refint_t3 - where (((rule_and_refint_t3.id3a = new.id3a) - and (rule_and_refint_t3.id3b = new.id3b)) - and (rule_and_refint_t3.id3c = new.id3c)))) - do instead update rule_and_refint_t3 set data = new.data - where (((rule_and_refint_t3.id3a = new.id3a) - and (rule_and_refint_t3.id3b = new.id3b)) - and (rule_and_refint_t3.id3c = new.id3c)); -insert into rule_and_refint_t3 values (1, 11, 13, 'row7'); -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3c_fkey" -DETAIL: Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2". -insert into rule_and_refint_t3 values (1, 13, 11, 'row8'); -ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey" -DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1". --- --- disallow dropping a view's rule (bug #5072) --- -create view rules_fooview as select 'rules_foo'::text; -drop rule "_RETURN" on rules_fooview; -ERROR: cannot drop rule _RETURN on view rules_fooview because view rules_fooview requires it -HINT: You can drop view rules_fooview instead. -drop view rules_fooview; --- --- test conversion of table to view (needed to load some pg_dump files) --- -create table rules_fooview (x int, y text); -select xmin, * from rules_fooview; - xmin | x | y -------+---+--- -(0 rows) - -create rule "_RETURN" as on select to rules_fooview do instead - select 1 as x, 'aaa'::text as y; -select * from rules_fooview; - x | y ----+----- - 1 | aaa -(1 row) - -select xmin, * from rules_fooview; -- fail, views don't have such a column -ERROR: column "xmin" does not exist -LINE 1: select xmin, * from rules_fooview; - ^ -select reltoastrelid, relkind, relfrozenxid - from pg_class where oid = 'rules_fooview'::regclass; - reltoastrelid | relkind | relfrozenxid ----------------+---------+-------------- - 0 | v | 0 -(1 row) - -drop view rules_fooview; --- cannot convert an inheritance parent or child to a view, though -create table rules_fooview (x int, y text); -create table rules_fooview_child () inherits (rules_fooview); -create rule "_RETURN" as on select to rules_fooview do instead - select 1 as x, 'aaa'::text as y; -ERROR: could not convert table "rules_fooview" to a view because it has child tables -create rule "_RETURN" as on select to rules_fooview_child do instead - select 1 as x, 'aaa'::text as y; -ERROR: could not convert table "rules_fooview_child" to a view because it has parent tables -drop table rules_fooview cascade; -NOTICE: drop cascades to table rules_fooview_child --- likewise, converting a partitioned table or partition to view is not allowed -create table rules_fooview (x int, y text) partition by list (x); -create rule "_RETURN" as on select to rules_fooview do instead - select 1 as x, 'aaa'::text as y; -ERROR: cannot convert partitioned table "rules_fooview" to a view -create table rules_fooview_part partition of rules_fooview for values in (1); -create rule "_RETURN" as on select to rules_fooview_part do instead - select 1 as x, 'aaa'::text as y; -ERROR: cannot convert partition "rules_fooview_part" to a view -drop table rules_fooview; --- --- check for planner problems with complex inherited UPDATES --- -create table id (id serial primary key, name text); --- currently, must respecify PKEY for each inherited subtable -create table test_1 (id integer primary key) inherits (id); -NOTICE: merging column "id" with inherited definition -create table test_2 (id integer primary key) inherits (id); -NOTICE: merging column "id" with inherited definition -create table test_3 (id integer primary key) inherits (id); -NOTICE: merging column "id" with inherited definition -insert into test_1 (name) values ('Test 1'); -insert into test_1 (name) values ('Test 2'); -insert into test_2 (name) values ('Test 3'); -insert into test_2 (name) values ('Test 4'); -insert into test_3 (name) values ('Test 5'); -insert into test_3 (name) values ('Test 6'); -create view id_ordered as select * from id order by id; -create rule update_id_ordered as on update to id_ordered - do instead update id set name = new.name where id = old.id; -select * from id_ordered; - id | name -----+-------- - 1 | Test 1 - 2 | Test 2 - 3 | Test 3 - 4 | Test 4 - 5 | Test 5 - 6 | Test 6 -(6 rows) - -update id_ordered set name = 'update 2' where id = 2; -update id_ordered set name = 'update 4' where id = 4; -update id_ordered set name = 'update 5' where id = 5; -select * from id_ordered; - id | name -----+---------- - 1 | Test 1 - 2 | update 2 - 3 | Test 3 - 4 | update 4 - 5 | update 5 - 6 | Test 6 -(6 rows) - -drop table id cascade; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table test_1 -drop cascades to table test_2 -drop cascades to table test_3 -drop cascades to view id_ordered --- --- check corner case where an entirely-dummy subplan is created by --- constraint exclusion --- -create temp table t1 (a integer primary key); -create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1); -create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1); -create rule t1_ins_1 as on insert to t1 - where new.a >= 0 and new.a < 10 - do instead - insert into t1_1 values (new.a); -create rule t1_ins_2 as on insert to t1 - where new.a >= 10 and new.a < 20 - do instead - insert into t1_2 values (new.a); -create rule t1_upd_1 as on update to t1 - where old.a >= 0 and old.a < 10 - do instead - update t1_1 set a = new.a where a = old.a; -create rule t1_upd_2 as on update to t1 - where old.a >= 10 and old.a < 20 - do instead - update t1_2 set a = new.a where a = old.a; -set constraint_exclusion = on; -insert into t1 select * from generate_series(5,19,1) g; -update t1 set a = 4 where a = 5; -select * from only t1; - a ---- -(0 rows) - -select * from only t1_1; - a ---- - 6 - 7 - 8 - 9 - 4 -(5 rows) - -select * from only t1_2; - a ----- - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 -(10 rows) - -reset constraint_exclusion; --- test FOR UPDATE in rules -create table rules_base(f1 int, f2 int); -insert into rules_base values(1,2), (11,12); -create rule r1 as on update to rules_base do instead - select * from rules_base where f1 = 1 for update; -update rules_base set f2 = f2 + 1; - f1 | f2 -----+---- - 1 | 2 -(1 row) - -create or replace rule r1 as on update to rules_base do instead - select * from rules_base where f1 = 11 for update of rules_base; -update rules_base set f2 = f2 + 1; - f1 | f2 -----+---- - 11 | 12 -(1 row) - -create or replace rule r1 as on update to rules_base do instead - select * from rules_base where f1 = 11 for update of old; -- error -ERROR: relation "old" in FOR UPDATE clause not found in FROM clause -LINE 2: select * from rules_base where f1 = 11 for update of old; - ^ -drop table rules_base; --- test various flavors of pg_get_viewdef() -select pg_get_viewdef('shoe'::regclass) as unpretty; - unpretty ------------------------------------------------- - SELECT sh.shoename, + - sh.sh_avail, + - sh.slcolor, + - sh.slminlen, + - (sh.slminlen * un.un_fact) AS slminlen_cm,+ - sh.slmaxlen, + - (sh.slmaxlen * un.un_fact) AS slmaxlen_cm,+ - sh.slunit + - FROM shoe_data sh, + - unit un + - WHERE (sh.slunit = un.un_name); -(1 row) - -select pg_get_viewdef('shoe'::regclass,true) as pretty; - pretty ----------------------------------------------- - SELECT sh.shoename, + - sh.sh_avail, + - sh.slcolor, + - sh.slminlen, + - sh.slminlen * un.un_fact AS slminlen_cm,+ - sh.slmaxlen, + - sh.slmaxlen * un.un_fact AS slmaxlen_cm,+ - sh.slunit + - FROM shoe_data sh, + - unit un + - WHERE sh.slunit = un.un_name; -(1 row) - -select pg_get_viewdef('shoe'::regclass,0) as prettier; - prettier ----------------------------------------------- - SELECT sh.shoename, + - sh.sh_avail, + - sh.slcolor, + - sh.slminlen, + - sh.slminlen * un.un_fact AS slminlen_cm,+ - sh.slmaxlen, + - sh.slmaxlen * un.un_fact AS slmaxlen_cm,+ - sh.slunit + - FROM shoe_data sh, + - unit un + - WHERE sh.slunit = un.un_name; -(1 row) - --- --- check multi-row VALUES in rules --- -create table rules_src(f1 int, f2 int); -create table rules_log(f1 int, f2 int, tag text); -insert into rules_src values(1,2), (11,12); -create rule r1 as on update to rules_src do also - insert into rules_log values(old.*, 'old'), (new.*, 'new'); -update rules_src set f2 = f2 + 1; -update rules_src set f2 = f2 * 10; -select * from rules_src; - f1 | f2 -----+----- - 1 | 30 - 11 | 130 -(2 rows) - -select * from rules_log; - f1 | f2 | tag -----+-----+----- - 1 | 2 | old - 1 | 3 | new - 11 | 12 | old - 11 | 13 | new - 1 | 3 | old - 1 | 30 | new - 11 | 13 | old - 11 | 130 | new -(8 rows) - -create rule r2 as on update to rules_src do also - values(old.*, 'old'), (new.*, 'new'); -update rules_src set f2 = f2 / 10; - column1 | column2 | column3 ----------+---------+--------- - 1 | 30 | old - 1 | 3 | new - 11 | 130 | old - 11 | 13 | new -(4 rows) - -select * from rules_src; - f1 | f2 -----+---- - 1 | 3 - 11 | 13 -(2 rows) - -select * from rules_log; - f1 | f2 | tag -----+-----+----- - 1 | 2 | old - 1 | 3 | new - 11 | 12 | old - 11 | 13 | new - 1 | 3 | old - 1 | 30 | new - 11 | 13 | old - 11 | 130 | new - 1 | 30 | old - 1 | 3 | new - 11 | 130 | old - 11 | 13 | new -(12 rows) - -create rule r3 as on delete to rules_src do notify rules_src_deletion; -\d+ rules_src - Table "public.rules_src" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - f1 | integer | | | | plain | | - f2 | integer | | | | plain | | -Rules: - r1 AS - ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text) - r2 AS - ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text) - r3 AS - ON DELETE TO rules_src DO - NOTIFY rules_src_deletion - --- --- Ensure an aliased target relation for insert is correctly deparsed. --- -create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2; -create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1; -\d+ rules_src - Table "public.rules_src" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - f1 | integer | | | | plain | | - f2 | integer | | | | plain | | -Rules: - r1 AS - ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text) - r2 AS - ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text) - r3 AS - ON DELETE TO rules_src DO - NOTIFY rules_src_deletion - r4 AS - ON INSERT TO rules_src DO INSTEAD INSERT INTO rules_log AS trgt (f1, f2) SELECT new.f1, - new.f2 - RETURNING trgt.f1, - trgt.f2 - r5 AS - ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text - WHERE trgt.f1 = new.f1 - --- --- Also check multiassignment deparsing. --- -create table rule_t1(f1 int, f2 int); -create table rule_dest(f1 int, f2 int[], tag text); -create rule rr as on update to rule_t1 do instead UPDATE rule_dest trgt - SET (f2[1], f1, tag) = (SELECT new.f2, new.f1, 'updated'::varchar) - WHERE trgt.f1 = new.f1 RETURNING new.*; -\d+ rule_t1 - Table "public.rule_t1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - f1 | integer | | | | plain | | - f2 | integer | | | | plain | | -Rules: - rr AS - ON UPDATE TO rule_t1 DO INSTEAD UPDATE rule_dest trgt SET (f2[1], f1, tag) = ( SELECT new.f2, - new.f1, - 'updated'::character varying AS "varchar") - WHERE trgt.f1 = new.f1 - RETURNING new.f1, - new.f2 - -drop table rule_t1, rule_dest; --- --- check alter rename rule --- -CREATE TABLE rule_t1 (a INT); -CREATE VIEW rule_v1 AS SELECT * FROM rule_t1; -CREATE RULE InsertRule AS - ON INSERT TO rule_v1 - DO INSTEAD - INSERT INTO rule_t1 VALUES(new.a); -ALTER RULE InsertRule ON rule_v1 RENAME to NewInsertRule; -INSERT INTO rule_v1 VALUES(1); -SELECT * FROM rule_v1; - a ---- - 1 -(1 row) - -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+---------+------------- - a | integer | | | | plain | -View definition: - SELECT rule_t1.a - FROM rule_t1; -Rules: - newinsertrule AS - ON INSERT TO rule_v1 DO INSTEAD INSERT INTO rule_t1 (a) - VALUES (new.a) - --- --- error conditions for alter rename rule --- -ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist -ERROR: rule "insertrule" for relation "rule_v1" does not exist -ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists -ERROR: rule "_RETURN" for relation "rule_v1" already exists -ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed -ERROR: renaming an ON SELECT rule is not allowed -DROP VIEW rule_v1; -DROP TABLE rule_t1; --- --- check display of VALUES in view definitions --- -create view rule_v1 as values(1,2); -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ----------+---------+-----------+----------+---------+---------+------------- - column1 | integer | | | | plain | - column2 | integer | | | | plain | -View definition: - VALUES (1,2); - -alter table rule_v1 rename column column2 to q2; -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ----------+---------+-----------+----------+---------+---------+------------- - column1 | integer | | | | plain | - q2 | integer | | | | plain | -View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 - FROM (VALUES (1,2)) "*VALUES*"; - -drop view rule_v1; -create view rule_v1(x) as values(1,2); -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ----------+---------+-----------+----------+---------+---------+------------- - x | integer | | | | plain | - column2 | integer | | | | plain | -View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 - FROM (VALUES (1,2)) "*VALUES*"; - -drop view rule_v1; -create view rule_v1(x) as select * from (values(1,2)) v; -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ----------+---------+-----------+----------+---------+---------+------------- - x | integer | | | | plain | - column2 | integer | | | | plain | -View definition: - SELECT v.column1 AS x, - v.column2 - FROM ( VALUES (1,2)) v; - -drop view rule_v1; -create view rule_v1(x) as select * from (values(1,2)) v(q,w); -\d+ rule_v1 - View "public.rule_v1" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+---------+------------- - x | integer | | | | plain | - w | integer | | | | plain | -View definition: - SELECT v.q AS x, - v.w - FROM ( VALUES (1,2)) v(q, w); - -drop view rule_v1; --- --- Check DO INSTEAD rules with ON CONFLICT --- -CREATE TABLE hats ( - hat_name char(10) primary key, - hat_color char(10) -- hat color -); -CREATE TABLE hat_data ( - hat_name char(10), - hat_color char(10) -- hat color -); -create unique index hat_data_unique_idx - on hat_data (hat_name COLLATE "C" bpchar_pattern_ops); --- DO NOTHING with ON CONFLICT -CREATE RULE hat_nosert AS ON INSERT TO hats - DO INSTEAD - INSERT INTO hat_data VALUES ( - NEW.hat_name, - NEW.hat_color) - ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green' - DO NOTHING - RETURNING *; -SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; - definition ---------------------------------------------------------------------------------------------- - CREATE RULE hat_nosert AS + - ON INSERT TO public.hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) + - VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name COLLATE "C" bpchar_pattern_ops)+ - WHERE (hat_color = 'green'::bpchar) DO NOTHING + - RETURNING hat_data.hat_name, + - hat_data.hat_color; -(1 row) - --- Works (projects row) -INSERT INTO hats VALUES ('h7', 'black') RETURNING *; - hat_name | hat_color -------------+------------ - h7 | black -(1 row) - --- Works (does nothing) -INSERT INTO hats VALUES ('h7', 'black') RETURNING *; - hat_name | hat_color -----------+----------- -(0 rows) - -SELECT tablename, rulename, definition FROM pg_rules - WHERE tablename = 'hats'; - tablename | rulename | definition ------------+------------+--------------------------------------------------------------------------------------------- - hats | hat_nosert | CREATE RULE hat_nosert AS + - | | ON INSERT TO public.hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) + - | | VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name COLLATE "C" bpchar_pattern_ops)+ - | | WHERE (hat_color = 'green'::bpchar) DO NOTHING + - | | RETURNING hat_data.hat_name, + - | | hat_data.hat_color; -(1 row) - -DROP RULE hat_nosert ON hats; --- DO NOTHING without ON CONFLICT -CREATE RULE hat_nosert_all AS ON INSERT TO hats - DO INSTEAD - INSERT INTO hat_data VALUES ( - NEW.hat_name, - NEW.hat_color) - ON CONFLICT - DO NOTHING - RETURNING *; -SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; - definition -------------------------------------------------------------------------------------- - CREATE RULE hat_nosert_all AS + - ON INSERT TO public.hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color)+ - VALUES (new.hat_name, new.hat_color) ON CONFLICT DO NOTHING + - RETURNING hat_data.hat_name, + - hat_data.hat_color; -(1 row) - -DROP RULE hat_nosert_all ON hats; --- Works (does nothing) -INSERT INTO hats VALUES ('h7', 'black') RETURNING *; - hat_name | hat_color -------------+------------ - h7 | black -(1 row) - --- DO UPDATE with a WHERE clause -CREATE RULE hat_upsert AS ON INSERT TO hats - DO INSTEAD - INSERT INTO hat_data VALUES ( - NEW.hat_name, - NEW.hat_color) - ON CONFLICT (hat_name) - DO UPDATE - SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color - WHERE excluded.hat_color <> 'forbidden' AND hat_data.* != excluded.* - RETURNING *; -SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; - definition ------------------------------------------------------------------------------------------------------------------------------------------ - CREATE RULE hat_upsert AS + - ON INSERT TO public.hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) + - VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name) DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color+ - WHERE ((excluded.hat_color <> 'forbidden'::bpchar) AND (hat_data.* <> excluded.*)) + - RETURNING hat_data.hat_name, + - hat_data.hat_color; -(1 row) - --- Works (does upsert) -INSERT INTO hats VALUES ('h8', 'black') RETURNING *; - hat_name | hat_color -------------+------------ - h8 | black -(1 row) - -SELECT * FROM hat_data WHERE hat_name = 'h8'; - hat_name | hat_color -------------+------------ - h8 | black -(1 row) - -INSERT INTO hats VALUES ('h8', 'white') RETURNING *; - hat_name | hat_color -------------+------------ - h8 | white -(1 row) - -SELECT * FROM hat_data WHERE hat_name = 'h8'; - hat_name | hat_color -------------+------------ - h8 | white -(1 row) - -INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; - hat_name | hat_color -----------+----------- -(0 rows) - -SELECT * FROM hat_data WHERE hat_name = 'h8'; - hat_name | hat_color -------------+------------ - h8 | white -(1 row) - -SELECT tablename, rulename, definition FROM pg_rules - WHERE tablename = 'hats'; - tablename | rulename | definition ------------+------------+----------------------------------------------------------------------------------------------------------------------------------------- - hats | hat_upsert | CREATE RULE hat_upsert AS + - | | ON INSERT TO public.hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) + - | | VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name) DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color+ - | | WHERE ((excluded.hat_color <> 'forbidden'::bpchar) AND (hat_data.* <> excluded.*)) + - | | RETURNING hat_data.hat_name, + - | | hat_data.hat_color; -(1 row) - --- ensure explain works for on insert conflict rules -explain (costs off) INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; - QUERY PLAN -------------------------------------------------------------------------------------------------- - Insert on hat_data - Conflict Resolution: UPDATE - Conflict Arbiter Indexes: hat_data_unique_idx - Conflict Filter: ((excluded.hat_color <> 'forbidden'::bpchar) AND (hat_data.* <> excluded.*)) - -> Result -(5 rows) - --- ensure upserting into a rule, with a CTE (different offsets!) works -WITH data(hat_name, hat_color) AS MATERIALIZED ( - VALUES ('h8', 'green'), - ('h9', 'blue'), - ('h7', 'forbidden') -) -INSERT INTO hats - SELECT * FROM data -RETURNING *; - hat_name | hat_color -------------+------------ - h8 | green - h9 | blue -(2 rows) - -EXPLAIN (costs off) -WITH data(hat_name, hat_color) AS MATERIALIZED ( - VALUES ('h8', 'green'), - ('h9', 'blue'), - ('h7', 'forbidden') -) -INSERT INTO hats - SELECT * FROM data -RETURNING *; - QUERY PLAN -------------------------------------------------------------------------------------------------- - Insert on hat_data - Conflict Resolution: UPDATE - Conflict Arbiter Indexes: hat_data_unique_idx - Conflict Filter: ((excluded.hat_color <> 'forbidden'::bpchar) AND (hat_data.* <> excluded.*)) - CTE data - -> Values Scan on "*VALUES*" - -> CTE Scan on data -(7 rows) - -SELECT * FROM hat_data WHERE hat_name IN ('h8', 'h9', 'h7') ORDER BY hat_name; - hat_name | hat_color -------------+------------ - h7 | black - h8 | green - h9 | blue -(3 rows) - -DROP RULE hat_upsert ON hats; -drop table hats; -drop table hat_data; --- test for pg_get_functiondef properly regurgitating SET parameters --- Note that the function is kept around to stress pg_dump. -CREATE FUNCTION func_with_set_params() RETURNS integer - AS 'select 1;' - LANGUAGE SQL - SET search_path TO PG_CATALOG - SET extra_float_digits TO 2 - SET work_mem TO '4MB' - SET datestyle to iso, mdy - SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' - IMMUTABLE STRICT; -SELECT pg_get_functiondef('func_with_set_params()'::regprocedure); - pg_get_functiondef --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CREATE OR REPLACE FUNCTION public.func_with_set_params() + - RETURNS integer + - LANGUAGE sql + - IMMUTABLE STRICT + - SET search_path TO 'pg_catalog' + - SET extra_float_digits TO '2' + - SET work_mem TO '4MB' + - SET "DateStyle" TO 'iso, mdy' + - SET local_preload_libraries TO 'Mixed/Case', 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'+ - AS $function$select 1;$function$ + - -(1 row) - --- tests for pg_get_*def with invalid objects -SELECT pg_get_constraintdef(0); - pg_get_constraintdef ----------------------- - -(1 row) - -SELECT pg_get_functiondef(0); - pg_get_functiondef --------------------- - -(1 row) - -SELECT pg_get_indexdef(0); - pg_get_indexdef ------------------ - -(1 row) - -SELECT pg_get_ruledef(0); - pg_get_ruledef ----------------- - -(1 row) - -SELECT pg_get_statisticsobjdef(0); - pg_get_statisticsobjdef -------------------------- - -(1 row) - -SELECT pg_get_triggerdef(0); - pg_get_triggerdef -------------------- - -(1 row) - -SELECT pg_get_viewdef(0); - pg_get_viewdef ----------------- - -(1 row) - -SELECT pg_get_function_arguments(0); - pg_get_function_arguments ---------------------------- - -(1 row) - -SELECT pg_get_function_identity_arguments(0); - pg_get_function_identity_arguments ------------------------------------- - -(1 row) - -SELECT pg_get_function_result(0); - pg_get_function_result ------------------------- - -(1 row) - -SELECT pg_get_function_arg_default(0, 0); - pg_get_function_arg_default ------------------------------ - -(1 row) - -SELECT pg_get_function_arg_default('pg_class'::regclass, 0); - pg_get_function_arg_default ------------------------------ - -(1 row) - -SELECT pg_get_partkeydef(0); - pg_get_partkeydef -------------------- - -(1 row) - --- test rename for a rule defined on a partitioned table -CREATE TABLE rules_parted_table (a int) PARTITION BY LIST (a); -CREATE TABLE rules_parted_table_1 PARTITION OF rules_parted_table FOR VALUES IN (1); -CREATE RULE rules_parted_table_insert AS ON INSERT to rules_parted_table - DO INSTEAD INSERT INTO rules_parted_table_1 VALUES (NEW.*); -ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect; -DROP TABLE rules_parted_table; --- --- Test enabling/disabling --- -CREATE TABLE ruletest1 (a int); -CREATE TABLE ruletest2 (b int); -CREATE RULE rule1 AS ON INSERT TO ruletest1 - DO INSTEAD INSERT INTO ruletest2 VALUES (NEW.*); -INSERT INTO ruletest1 VALUES (1); -ALTER TABLE ruletest1 DISABLE RULE rule1; -INSERT INTO ruletest1 VALUES (2); -ALTER TABLE ruletest1 ENABLE RULE rule1; -SET session_replication_role = replica; -INSERT INTO ruletest1 VALUES (3); -ALTER TABLE ruletest1 ENABLE REPLICA RULE rule1; -INSERT INTO ruletest1 VALUES (4); -RESET session_replication_role; -INSERT INTO ruletest1 VALUES (5); -SELECT * FROM ruletest1; - a ---- - 2 - 3 - 5 -(3 rows) - -SELECT * FROM ruletest2; - b ---- - 1 - 4 -(2 rows) - -DROP TABLE ruletest1; -DROP TABLE ruletest2; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/psql.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/psql.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/psql.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/psql.out 2021-10-30 22:04:16 -0600 @@ -1,5280 +1,2 @@ --- --- Tests for psql features that aren't closely connected to any --- specific server features --- --- \set --- fail: invalid name -\set invalid/name foo -invalid variable name: "invalid/name" --- fail: invalid value for special variable -\set AUTOCOMMIT foo -unrecognized value "foo" for "AUTOCOMMIT": Boolean expected -\set FETCH_COUNT foo -invalid value "foo" for "FETCH_COUNT": integer expected --- check handling of built-in boolean variable -\echo :ON_ERROR_ROLLBACK -off -\set ON_ERROR_ROLLBACK -\echo :ON_ERROR_ROLLBACK -on -\set ON_ERROR_ROLLBACK foo -unrecognized value "foo" for "ON_ERROR_ROLLBACK" -Available values are: on, off, interactive. -\echo :ON_ERROR_ROLLBACK -on -\set ON_ERROR_ROLLBACK on -\echo :ON_ERROR_ROLLBACK -on -\unset ON_ERROR_ROLLBACK -\echo :ON_ERROR_ROLLBACK -off --- \g and \gx -SELECT 1 as one, 2 as two \g - one | two ------+----- - 1 | 2 -(1 row) - -\gx --[ RECORD 1 ] -one | 1 -two | 2 - -SELECT 3 as three, 4 as four \gx --[ RECORD 1 ] -three | 3 -four | 4 - -\g - three | four --------+------ - 3 | 4 -(1 row) - --- \gx should work in FETCH_COUNT mode too -\set FETCH_COUNT 1 -SELECT 1 as one, 2 as two \g - one | two ------+----- - 1 | 2 -(1 row) - -\gx --[ RECORD 1 ] -one | 1 -two | 2 - -SELECT 3 as three, 4 as four \gx --[ RECORD 1 ] -three | 3 -four | 4 - -\g - three | four --------+------ - 3 | 4 -(1 row) - -\unset FETCH_COUNT --- \g/\gx with pset options -SELECT 1 as one, 2 as two \g (format=csv csv_fieldsep='\t') -one two -1 2 -\g - one | two ------+----- - 1 | 2 -(1 row) - -SELECT 1 as one, 2 as two \gx (title='foo bar') -foo bar --[ RECORD 1 ] -one | 1 -two | 2 - -\g - one | two ------+----- - 1 | 2 -(1 row) - --- \gset -select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_ -\echo :pref01_test01 :pref01_test02 :pref01_test03 -10 20 Hello --- should fail: bad variable name -select 10 as "bad name" -\gset -invalid variable name: "bad name" -select 97 as "EOF", 'ok' as _foo \gset IGNORE -attempt to \gset into specially treated variable "IGNOREEOF" ignored -\echo :IGNORE_foo :IGNOREEOF -ok 0 --- multiple backslash commands in one line -select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x -1 -select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y -3 -4 -select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y - x | y ----+--- - 5 | 6 -(1 row) - -5 6 -select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y - x | y ----+--- - 7 | 8 -(1 row) - -7 8 --- NULL should unset the variable -\set var2 xyz -select 1 as var1, NULL as var2, 3 as var3 \gset -\echo :var1 :var2 :var3 -1 :var2 3 --- \gset requires just one tuple -select 10 as test01, 20 as test02 from generate_series(1,3) \gset -more than one row returned for \gset -select 10 as test01, 20 as test02 from generate_series(1,0) \gset -no rows returned for \gset --- \gset should work in FETCH_COUNT mode too -\set FETCH_COUNT 1 -select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x -1 -select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y -3 -4 -select 10 as test01, 20 as test02 from generate_series(1,3) \gset -more than one row returned for \gset -select 10 as test01, 20 as test02 from generate_series(1,0) \gset -no rows returned for \gset -\unset FETCH_COUNT --- \gdesc -SELECT - NULL AS zero, - 1 AS one, - 2.0 AS two, - 'three' AS three, - $1 AS four, - sin($2) as five, - 'foo'::varchar(4) as six, - CURRENT_DATE AS now -\gdesc - Column | Type ---------+---------------------- - zero | text - one | integer - two | numeric - three | text - four | text - five | double precision - six | character varying(4) - now | date -(8 rows) - --- should work with tuple-returning utilities, such as EXECUTE -PREPARE test AS SELECT 1 AS first, 2 AS second; -EXECUTE test \gdesc - Column | Type ---------+--------- - first | integer - second | integer -(2 rows) - -EXPLAIN EXECUTE test \gdesc - Column | Type -------------+------ - QUERY PLAN | text -(1 row) - --- should fail cleanly - syntax error -SELECT 1 + \gdesc -ERROR: syntax error at end of input -LINE 1: SELECT 1 + - ^ --- check behavior with empty results -SELECT \gdesc -The command has no result, or the result has no columns. -CREATE TABLE bububu(a int) \gdesc -The command has no result, or the result has no columns. --- subject command should not have executed -TABLE bububu; -- fail -ERROR: relation "bububu" does not exist -LINE 1: TABLE bububu; - ^ --- query buffer should remain unchanged -SELECT 1 AS x, 'Hello', 2 AS y, true AS "dirty\name" -\gdesc - Column | Type -------------+--------- - x | integer - ?column? | text - y | integer - dirty\name | boolean -(4 rows) - -\g - x | ?column? | y | dirty\name ----+----------+---+------------ - 1 | Hello | 2 | t -(1 row) - --- all on one line -SELECT 3 AS x, 'Hello', 4 AS y, true AS "dirty\name" \gdesc \g - Column | Type -------------+--------- - x | integer - ?column? | text - y | integer - dirty\name | boolean -(4 rows) - - x | ?column? | y | dirty\name ----+----------+---+------------ - 3 | Hello | 4 | t -(1 row) - --- \gexec -create temporary table gexec_test(a int, b text, c date, d float); -select format('create index on gexec_test(%I)', attname) -from pg_attribute -where attrelid = 'gexec_test'::regclass and attnum > 0 -order by attnum -\gexec -create index on gexec_test(a) -create index on gexec_test(b) -create index on gexec_test(c) -create index on gexec_test(d) --- \gexec should work in FETCH_COUNT mode too --- (though the fetch limit applies to the executed queries not the meta query) -\set FETCH_COUNT 1 -select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)' -union all -select 'drop table gexec_test', NULL -union all -select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over' -\gexec -select 1 as ones - ones ------- - 1 -(1 row) - -select x.y, x.y*2 as double from generate_series(1,4) as x(y) - y | double ----+-------- - 1 | 2 - 2 | 4 - 3 | 6 - 4 | 8 -(4 rows) - -drop table gexec_test -drop table gexec_test -ERROR: table "gexec_test" does not exist -select '2000-01-01'::date as party_over - party_over ------------- - 01-01-2000 -(1 row) - -\unset FETCH_COUNT --- show all pset options -\pset -border 1 -columns 0 -csv_fieldsep ',' -expanded off -fieldsep '|' -fieldsep_zero off -footer on -format aligned -linestyle ascii -null '' -numericlocale off -pager 1 -pager_min_lines 0 -recordsep '\n' -recordsep_zero off -tableattr -title -tuples_only off -unicode_border_linestyle single -unicode_column_linestyle single -unicode_header_linestyle single --- test multi-line headers, wrapping, and newline indicators --- in aligned, unaligned, and wrapped formats -prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab - -c", array_to_string(array_agg(repeat('y',20-2*n)),E'\n') as "a -bc" from generate_series(1,10) as n(n) group by n>1 order by n>1; -\pset linestyle ascii -\pset expanded off -\pset columns 40 -\pset border 0 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; - ab + a + - + bc - c --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx +yyyyyyyyyyyyyyyy + -xxxxxx +yyyyyyyyyyyyyy + -xxxxxxxx +yyyyyyyyyyyy + -xxxxxxxxxx +yyyyyyyyyy + -xxxxxxxxxxxx +yyyyyyyy + -xxxxxxxxxxxxxx +yyyyyy + -xxxxxxxxxxxxxxxx +yyyy + -xxxxxxxxxxxxxxxxxx +yy + -xxxxxxxxxxxxxxxxxxxx -(2 rows) - -\pset format wrapped -execute q; - ab + a + - + bc - c --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx +yyyyyyyyyyyyyyyy + -xxxxxx +yyyyyyyyyyyyyy + -xxxxxxxx +yyyyyyyyyyyy + -xxxxxxxxxx +yyyyyyyyyy + -xxxxxxxxxxxx +yyyyyyyy + -xxxxxxxxxxxxxx +yyyyyy + -xxxxxxxxxxxxxxxx +yyyy + -xxxxxxxxxxxxxxxxxx +yy + -xxxxxxxxxxxxxxxxxxxx -(2 rows) - -\pset border 1 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; - ab +| a + - +| bc - c | -----------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx +| yyyyyyyyyyyyyyyy + - xxxxxx +| yyyyyyyyyyyyyy + - xxxxxxxx +| yyyyyyyyyyyy + - xxxxxxxxxx +| yyyyyyyyyy + - xxxxxxxxxxxx +| yyyyyyyy + - xxxxxxxxxxxxxx +| yyyyyy + - xxxxxxxxxxxxxxxx +| yyyy + - xxxxxxxxxxxxxxxxxx +| yy + - xxxxxxxxxxxxxxxxxxxx | -(2 rows) - -\pset format wrapped -execute q; - ab +| a + - +| bc - c | --------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx +| yyyyyyyyyyyyyyyy + - xxxxxx +| yyyyyyyyyyyyyy + - xxxxxxxx +| yyyyyyyyyyyy + - xxxxxxxxxx +| yyyyyyyyyy + - xxxxxxxxxxxx +| yyyyyyyy + - xxxxxxxxxxxxxx +| yyyyyy + - xxxxxxxxxxxxxxxx +| yyyy + - xxxxxxxxxxxxxxxxx.| yy + -.x +| - xxxxxxxxxxxxxxxxx.| -.xxx | -(2 rows) - -\pset border 2 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; -+----------------------+--------------------+ -| ab +| a +| -| +| bc | -| c | | -+----------------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx +| yyyyyyyyyyyyyyyy +| -| xxxxxx +| yyyyyyyyyyyyyy +| -| xxxxxxxx +| yyyyyyyyyyyy +| -| xxxxxxxxxx +| yyyyyyyyyy +| -| xxxxxxxxxxxx +| yyyyyyyy +| -| xxxxxxxxxxxxxx +| yyyyyy +| -| xxxxxxxxxxxxxxxx +| yyyy +| -| xxxxxxxxxxxxxxxxxx +| yy +| -| xxxxxxxxxxxxxxxxxxxx | | -+----------------------+--------------------+ -(2 rows) - -\pset format wrapped -execute q; -+-----------------+--------------------+ -| ab +| a +| -| +| bc | -| c | | -+-----------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx +| yyyyyyyyyyyyyyyy +| -| xxxxxx +| yyyyyyyyyyyyyy +| -| xxxxxxxx +| yyyyyyyyyyyy +| -| xxxxxxxxxx +| yyyyyyyyyy +| -| xxxxxxxxxxxx +| yyyyyyyy +| -| xxxxxxxxxxxxxx +| yyyyyy +| -| xxxxxxxxxxxxxxx.| yyyy +| -|.x +| yy +| -| xxxxxxxxxxxxxxx.| | -|.xxx +| | -| xxxxxxxxxxxxxxx.| | -|.xxxxx | | -+-----------------+--------------------+ -(2 rows) - -\pset expanded on -\pset columns 20 -\pset border 0 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; -* Record 1 -ab+ xx - + -c -a + yyyyyyyyyyyyyyyyyy -bc -* Record 2 -ab+ xxxx + - + xxxxxx + -c xxxxxxxx + - xxxxxxxxxx + - xxxxxxxxxxxx + - xxxxxxxxxxxxxx + - xxxxxxxxxxxxxxxx + - xxxxxxxxxxxxxxxxxx + - xxxxxxxxxxxxxxxxxxxx -a + yyyyyyyyyyyyyyyy + -bc yyyyyyyyyyyyyy + - yyyyyyyyyyyy + - yyyyyyyyyy + - yyyyyyyy + - yyyyyy + - yyyy + - yy + - - -\pset format wrapped -execute q; -* Record 1 -ab+ xx - + -c -a + yyyyyyyyyyyyyyy. -bc .yyy -* Record 2 -ab+ xxxx + - + xxxxxx + -c xxxxxxxx + - xxxxxxxxxx + - xxxxxxxxxxxx + - xxxxxxxxxxxxxx + - xxxxxxxxxxxxxxx. - .x + - xxxxxxxxxxxxxxx. - .xxx + - xxxxxxxxxxxxxxx. - .xxxxx -a + yyyyyyyyyyyyyyy. -bc .y + - yyyyyyyyyyyyyy + - yyyyyyyyyyyy + - yyyyyyyyyy + - yyyyyyyy + - yyyyyy + - yyyy + - yy + - - -\pset border 1 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; --[ RECORD 1 ]------------ -ab+| xx - +| -c | -a +| yyyyyyyyyyyyyyyyyy -bc | --[ RECORD 2 ]------------ -ab+| xxxx + - +| xxxxxx + -c | xxxxxxxx + - | xxxxxxxxxx + - | xxxxxxxxxxxx + - | xxxxxxxxxxxxxx + - | xxxxxxxxxxxxxxxx + - | xxxxxxxxxxxxxxxxxx + - | xxxxxxxxxxxxxxxxxxxx -a +| yyyyyyyyyyyyyyyy + -bc | yyyyyyyyyyyyyy + - | yyyyyyyyyyyy + - | yyyyyyyyyy + - | yyyyyyyy + - | yyyyyy + - | yyyy + - | yy + - | - -\pset format wrapped -execute q; --[ RECORD 1 ]------ -ab+| xx - +| -c | -a +| yyyyyyyyyyyyyy. -bc |.yyyy --[ RECORD 2 ]------ -ab+| xxxx + - +| xxxxxx + -c | xxxxxxxx + - | xxxxxxxxxx + - | xxxxxxxxxxxx + - | xxxxxxxxxxxxxx+ - | xxxxxxxxxxxxxx. - |.xx + - | xxxxxxxxxxxxxx. - |.xxxx + - | xxxxxxxxxxxxxx. - |.xxxxxx -a +| yyyyyyyyyyyyyy. -bc |.yy + - | yyyyyyyyyyyyyy+ - | yyyyyyyyyyyy + - | yyyyyyyyyy + - | yyyyyyyy + - | yyyyyy + - | yyyy + - | yy + - | - -\pset border 2 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; -+-[ RECORD 1 ]--------------+ -| ab+| xx | -| +| | -| c | | -| a +| yyyyyyyyyyyyyyyyyy | -| bc | | -+-[ RECORD 2 ]--------------+ -| ab+| xxxx +| -| +| xxxxxx +| -| c | xxxxxxxx +| -| | xxxxxxxxxx +| -| | xxxxxxxxxxxx +| -| | xxxxxxxxxxxxxx +| -| | xxxxxxxxxxxxxxxx +| -| | xxxxxxxxxxxxxxxxxx +| -| | xxxxxxxxxxxxxxxxxxxx | -| a +| yyyyyyyyyyyyyyyy +| -| bc | yyyyyyyyyyyyyy +| -| | yyyyyyyyyyyy +| -| | yyyyyyyyyy +| -| | yyyyyyyy +| -| | yyyyyy +| -| | yyyy +| -| | yy +| -| | | -+----+----------------------+ - -\pset format wrapped -execute q; -+-[ RECORD 1 ]-----+ -| ab+| xx | -| +| | -| c | | -| a +| yyyyyyyyyyy.| -| bc |.yyyyyyy | -+-[ RECORD 2 ]-----+ -| ab+| xxxx +| -| +| xxxxxx +| -| c | xxxxxxxx +| -| | xxxxxxxxxx +| -| | xxxxxxxxxxx.| -| |.x +| -| | xxxxxxxxxxx.| -| |.xxx +| -| | xxxxxxxxxxx.| -| |.xxxxx +| -| | xxxxxxxxxxx.| -| |.xxxxxxx +| -| | xxxxxxxxxxx.| -| |.xxxxxxxxx | -| a +| yyyyyyyyyyy.| -| bc |.yyyyy +| -| | yyyyyyyyyyy.| -| |.yyy +| -| | yyyyyyyyyyy.| -| |.y +| -| | yyyyyyyyyy +| -| | yyyyyyyy +| -| | yyyyyy +| -| | yyyy +| -| | yy +| -| | | -+----+-------------+ - -\pset linestyle old-ascii -\pset expanded off -\pset columns 40 -\pset border 0 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; - ab a - + bc - c + --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(2 rows) - -\pset format wrapped -execute q; - ab a - + bc - c + --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(2 rows) - -\pset border 1 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; - ab | a -+ |+ bc -+ c |+ -----------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx : yyyyyyyyyyyyyy - xxxxxxxx : yyyyyyyyyyyy - xxxxxxxxxx : yyyyyyyyyy - xxxxxxxxxxxx : yyyyyyyy - xxxxxxxxxxxxxx : yyyyyy - xxxxxxxxxxxxxxxx : yyyy - xxxxxxxxxxxxxxxxxx : yy - xxxxxxxxxxxxxxxxxxxx : -(2 rows) - -\pset format wrapped -execute q; - ab | a -+ |+ bc -+ c |+ --------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx : yyyyyyyyyyyyyy - xxxxxxxx : yyyyyyyyyyyy - xxxxxxxxxx : yyyyyyyyyy - xxxxxxxxxxxx : yyyyyyyy - xxxxxxxxxxxxxx : yyyyyy - xxxxxxxxxxxxxxxx : yyyy - xxxxxxxxxxxxxxxxx : yy - x : - xxxxxxxxxxxxxxxxx - xxx -(2 rows) - -\pset border 2 -\pset format unaligned -execute q; -ab - -c|a -bc -xx|yyyyyyyyyyyyyyyyyy -xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -(2 rows) -\pset format aligned -execute q; -+----------------------+--------------------+ -| ab | a | -|+ |+ bc | -|+ c |+ | -+----------------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx | yyyyyyyyyyyyyyyy | -| xxxxxx : yyyyyyyyyyyyyy | -| xxxxxxxx : yyyyyyyyyyyy | -| xxxxxxxxxx : yyyyyyyyyy | -| xxxxxxxxxxxx : yyyyyyyy | -| xxxxxxxxxxxxxx : yyyyyy | -| xxxxxxxxxxxxxxxx : yyyy | -| xxxxxxxxxxxxxxxxxx : yy | -| xxxxxxxxxxxxxxxxxxxx : | -+----------------------+--------------------+ -(2 rows) - -\pset format wrapped -execute q; -+-----------------+--------------------+ -| ab | a | -|+ |+ bc | -|+ c |+ | -+-----------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx | yyyyyyyyyyyyyyyy | -| xxxxxx : yyyyyyyyyyyyyy | -| xxxxxxxx : yyyyyyyyyyyy | -| xxxxxxxxxx : yyyyyyyyyy | -| xxxxxxxxxxxx : yyyyyyyy | -| xxxxxxxxxxxxxx : yyyyyy | -| xxxxxxxxxxxxxxx : yyyy | -| x : yy | -| xxxxxxxxxxxxxxx : | -| xxx | -| xxxxxxxxxxxxxxx | -| xxxxx | -+-----------------+--------------------+ -(2 rows) - -\pset expanded on -\pset columns 20 -\pset border 0 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; -* Record 1 - ab xx -+ -+c - a yyyyyyyyyyyyyyyyyy -+bc -* Record 2 - ab xxxx -+ xxxxxx -+c xxxxxxxx - xxxxxxxxxx - xxxxxxxxxxxx - xxxxxxxxxxxxxx - xxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxx - a yyyyyyyyyyyyyyyy -+bc yyyyyyyyyyyyyy - yyyyyyyyyyyy - yyyyyyyyyy - yyyyyyyy - yyyyyy - yyyy - yy - - -\pset format wrapped -execute q; -* Record 1 - ab xx -+ -+c - a yyyyyyyyyyyyyyyy -+bc yy -* Record 2 - ab xxxx -+ xxxxxx -+c xxxxxxxx - xxxxxxxxxx - xxxxxxxxxxxx - xxxxxxxxxxxxxx - xxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxx - xx - xxxxxxxxxxxxxxxx - xxxx - a yyyyyyyyyyyyyyyy -+bc yyyyyyyyyyyyyy - yyyyyyyyyyyy - yyyyyyyyyy - yyyyyyyy - yyyyyy - yyyy - yy - - -\pset border 1 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; --[ RECORD 1 ]------------- - ab | xx -+ ; -+c ; - a | yyyyyyyyyyyyyyyyyy -+bc ; --[ RECORD 2 ]------------- - ab | xxxx -+ : xxxxxx -+c : xxxxxxxx - : xxxxxxxxxx - : xxxxxxxxxxxx - : xxxxxxxxxxxxxx - : xxxxxxxxxxxxxxxx - : xxxxxxxxxxxxxxxxxx - : xxxxxxxxxxxxxxxxxxxx - a | yyyyyyyyyyyyyyyy -+bc : yyyyyyyyyyyyyy - : yyyyyyyyyyyy - : yyyyyyyyyy - : yyyyyyyy - : yyyyyy - : yyyy - : yy - : - -\pset format wrapped -execute q; --[ RECORD 1 ]------- - ab | xx -+ ; -+c ; - a | yyyyyyyyyyyyyy -+bc ; yyyy --[ RECORD 2 ]------- - ab | xxxx -+ : xxxxxx -+c : xxxxxxxx - : xxxxxxxxxx - : xxxxxxxxxxxx - : xxxxxxxxxxxxxx - : xxxxxxxxxxxxxx - ; xx - : xxxxxxxxxxxxxx - ; xxxx - : xxxxxxxxxxxxxx - ; xxxxxx - a | yyyyyyyyyyyyyy -+bc ; yy - : yyyyyyyyyyyyyy - : yyyyyyyyyyyy - : yyyyyyyyyy - : yyyyyyyy - : yyyyyy - : yyyy - : yy - : - -\pset border 2 -\pset format unaligned -execute q; -ab - -c|xx -a -bc|yyyyyyyyyyyyyyyyyy - -ab - -c|xxxx -xxxxxx -xxxxxxxx -xxxxxxxxxx -xxxxxxxxxxxx -xxxxxxxxxxxxxx -xxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxx -a -bc|yyyyyyyyyyyyyyyy -yyyyyyyyyyyyyy -yyyyyyyyyyyy -yyyyyyyyyy -yyyyyyyy -yyyyyy -yyyy -yy - -\pset format aligned -execute q; -+-[ RECORD 1 ]--------------+ -| ab | xx | -|+ ; | -|+c ; | -| a | yyyyyyyyyyyyyyyyyy | -|+bc ; | -+-[ RECORD 2 ]--------------+ -| ab | xxxx | -|+ : xxxxxx | -|+c : xxxxxxxx | -| : xxxxxxxxxx | -| : xxxxxxxxxxxx | -| : xxxxxxxxxxxxxx | -| : xxxxxxxxxxxxxxxx | -| : xxxxxxxxxxxxxxxxxx | -| : xxxxxxxxxxxxxxxxxxxx | -| a | yyyyyyyyyyyyyyyy | -|+bc : yyyyyyyyyyyyyy | -| : yyyyyyyyyyyy | -| : yyyyyyyyyy | -| : yyyyyyyy | -| : yyyyyy | -| : yyyy | -| : yy | -| : | -+----+----------------------+ - -\pset format wrapped -execute q; -+-[ RECORD 1 ]-----+ -| ab | xx | -|+ ; | -|+c ; | -| a | yyyyyyyyyyy | -|+bc ; yyyyyyy | -+-[ RECORD 2 ]-----+ -| ab | xxxx | -|+ : xxxxxx | -|+c : xxxxxxxx | -| : xxxxxxxxxx | -| : xxxxxxxxxxx | -| ; x | -| : xxxxxxxxxxx | -| ; xxx | -| : xxxxxxxxxxx | -| ; xxxxx | -| : xxxxxxxxxxx | -| ; xxxxxxx | -| : xxxxxxxxxxx | -| ; xxxxxxxxx | -| a | yyyyyyyyyyy | -|+bc ; yyyyy | -| : yyyyyyyyyyy | -| ; yyy | -| : yyyyyyyyyyy | -| ; y | -| : yyyyyyyyyy | -| : yyyyyyyy | -| : yyyyyy | -| : yyyy | -| : yy | -| : | -+----+-------------+ - -deallocate q; --- test single-line header and data -prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as "0123456789" from generate_series(1,10) as n; -\pset linestyle ascii -\pset expanded off -\pset columns 40 -\pset border 0 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; - 0123456789abcdef 0123456789 --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(10 rows) - -\pset format wrapped -execute q; - 0123456789abcdef 0123456789 --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(10 rows) - -\pset border 1 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; - 0123456789abcdef | 0123456789 -----------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx | yyyyyyyyyyyyyy - xxxxxxxx | yyyyyyyyyyyy - xxxxxxxxxx | yyyyyyyyyy - xxxxxxxxxxxx | yyyyyyyy - xxxxxxxxxxxxxx | yyyyyy - xxxxxxxxxxxxxxxx | yyyy - xxxxxxxxxxxxxxxxxx | yy - xxxxxxxxxxxxxxxxxxxx | -(10 rows) - -\pset format wrapped -execute q; - 0123456789abcdef | 0123456789 ----------------------+------------------ - xx | yyyyyyyyyyyyyyyy. - |.yy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx | yyyyyyyyyyyyyy - xxxxxxxx | yyyyyyyyyyyy - xxxxxxxxxx | yyyyyyyyyy - xxxxxxxxxxxx | yyyyyyyy - xxxxxxxxxxxxxx | yyyyyy - xxxxxxxxxxxxxxxx | yyyy - xxxxxxxxxxxxxxxxxx | yy - xxxxxxxxxxxxxxxxxxx.| -.x | -(10 rows) - -\pset border 2 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; -+----------------------+--------------------+ -| 0123456789abcdef | 0123456789 | -+----------------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx | yyyyyyyyyyyyyyyy | -| xxxxxx | yyyyyyyyyyyyyy | -| xxxxxxxx | yyyyyyyyyyyy | -| xxxxxxxxxx | yyyyyyyyyy | -| xxxxxxxxxxxx | yyyyyyyy | -| xxxxxxxxxxxxxx | yyyyyy | -| xxxxxxxxxxxxxxxx | yyyy | -| xxxxxxxxxxxxxxxxxx | yy | -| xxxxxxxxxxxxxxxxxxxx | | -+----------------------+--------------------+ -(10 rows) - -\pset format wrapped -execute q; -+--------------------+-----------------+ -| 0123456789abcdef | 0123456789 | -+--------------------+-----------------+ -| xx | yyyyyyyyyyyyyyy.| -| |.yyy | -| xxxx | yyyyyyyyyyyyyyy.| -| |.y | -| xxxxxx | yyyyyyyyyyyyyy | -| xxxxxxxx | yyyyyyyyyyyy | -| xxxxxxxxxx | yyyyyyyyyy | -| xxxxxxxxxxxx | yyyyyyyy | -| xxxxxxxxxxxxxx | yyyyyy | -| xxxxxxxxxxxxxxxx | yyyy | -| xxxxxxxxxxxxxxxxxx | yy | -| xxxxxxxxxxxxxxxxxx.| | -|.xx | | -+--------------------+-----------------+ -(10 rows) - -\pset expanded on -\pset columns 30 -\pset border 0 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyyyyyyyyyyyyyyyyy -* Record 2 -0123456789abcdef xxxx -0123456789 yyyyyyyyyyyyyyyy -* Record 3 -0123456789abcdef xxxxxx -0123456789 yyyyyyyyyyyyyy -* Record 4 -0123456789abcdef xxxxxxxx -0123456789 yyyyyyyyyyyy -* Record 5 -0123456789abcdef xxxxxxxxxx -0123456789 yyyyyyyyyy -* Record 6 -0123456789abcdef xxxxxxxxxxxx -0123456789 yyyyyyyy -* Record 7 -0123456789abcdef xxxxxxxxxxxxxx -0123456789 yyyyyy -* Record 8 -0123456789abcdef xxxxxxxxxxxxxxxx -0123456789 yyyy -* Record 9 -0123456789abcdef xxxxxxxxxxxxxxxxxx -0123456789 yy -* Record 10 -0123456789abcdef xxxxxxxxxxxxxxxxxxxx -0123456789 - -\pset format wrapped -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyyyyyyyyyyy. - .yyyyyy -* Record 2 -0123456789abcdef xxxx -0123456789 yyyyyyyyyyyy. - .yyyy -* Record 3 -0123456789abcdef xxxxxx -0123456789 yyyyyyyyyyyy. - .yy -* Record 4 -0123456789abcdef xxxxxxxx -0123456789 yyyyyyyyyyyy -* Record 5 -0123456789abcdef xxxxxxxxxx -0123456789 yyyyyyyyyy -* Record 6 -0123456789abcdef xxxxxxxxxxxx -0123456789 yyyyyyyy -* Record 7 -0123456789abcdef xxxxxxxxxxxx. - .xx -0123456789 yyyyyy -* Record 8 -0123456789abcdef xxxxxxxxxxxx. - .xxxx -0123456789 yyyy -* Record 9 -0123456789abcdef xxxxxxxxxxxx. - .xxxxxx -0123456789 yy -* Record 10 -0123456789abcdef xxxxxxxxxxxx. - .xxxxxxxx -0123456789 - -\pset border 1 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; --[ RECORD 1 ]----+--------------------- -0123456789abcdef | xx -0123456789 | yyyyyyyyyyyyyyyyyy --[ RECORD 2 ]----+--------------------- -0123456789abcdef | xxxx -0123456789 | yyyyyyyyyyyyyyyy --[ RECORD 3 ]----+--------------------- -0123456789abcdef | xxxxxx -0123456789 | yyyyyyyyyyyyyy --[ RECORD 4 ]----+--------------------- -0123456789abcdef | xxxxxxxx -0123456789 | yyyyyyyyyyyy --[ RECORD 5 ]----+--------------------- -0123456789abcdef | xxxxxxxxxx -0123456789 | yyyyyyyyyy --[ RECORD 6 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxx -0123456789 | yyyyyyyy --[ RECORD 7 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxx -0123456789 | yyyyyy --[ RECORD 8 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxx -0123456789 | yyyy --[ RECORD 9 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxx -0123456789 | yy --[ RECORD 10 ]---+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxxxx -0123456789 | - -\pset format wrapped -execute q; --[ RECORD 1 ]----+----------- -0123456789abcdef | xx -0123456789 | yyyyyyyyyy. - |.yyyyyyyy --[ RECORD 2 ]----+----------- -0123456789abcdef | xxxx -0123456789 | yyyyyyyyyy. - |.yyyyyy --[ RECORD 3 ]----+----------- -0123456789abcdef | xxxxxx -0123456789 | yyyyyyyyyy. - |.yyyy --[ RECORD 4 ]----+----------- -0123456789abcdef | xxxxxxxx -0123456789 | yyyyyyyyyy. - |.yy --[ RECORD 5 ]----+----------- -0123456789abcdef | xxxxxxxxxx -0123456789 | yyyyyyyyyy --[ RECORD 6 ]----+----------- -0123456789abcdef | xxxxxxxxxx. - |.xx -0123456789 | yyyyyyyy --[ RECORD 7 ]----+----------- -0123456789abcdef | xxxxxxxxxx. - |.xxxx -0123456789 | yyyyyy --[ RECORD 8 ]----+----------- -0123456789abcdef | xxxxxxxxxx. - |.xxxxxx -0123456789 | yyyy --[ RECORD 9 ]----+----------- -0123456789abcdef | xxxxxxxxxx. - |.xxxxxxxx -0123456789 | yy --[ RECORD 10 ]---+----------- -0123456789abcdef | xxxxxxxxxx. - |.xxxxxxxxxx -0123456789 | - -\pset border 2 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -+-[ RECORD 1 ]-----+----------------------+ -| 0123456789abcdef | xx | -| 0123456789 | yyyyyyyyyyyyyyyyyy | -+-[ RECORD 2 ]-----+----------------------+ -| 0123456789abcdef | xxxx | -| 0123456789 | yyyyyyyyyyyyyyyy | -+-[ RECORD 3 ]-----+----------------------+ -| 0123456789abcdef | xxxxxx | -| 0123456789 | yyyyyyyyyyyyyy | -+-[ RECORD 4 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxx | -| 0123456789 | yyyyyyyyyyyy | -+-[ RECORD 5 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxx | -| 0123456789 | yyyyyyyyyy | -+-[ RECORD 6 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxx | -| 0123456789 | yyyyyyyy | -+-[ RECORD 7 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxx | -| 0123456789 | yyyyyy | -+-[ RECORD 8 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxx | -| 0123456789 | yyyy | -+-[ RECORD 9 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxx | -| 0123456789 | yy | -+-[ RECORD 10 ]----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxxxx | -| 0123456789 | | -+------------------+----------------------+ - -\pset format wrapped -execute q; -+-[ RECORD 1 ]-----+---------+ -| 0123456789abcdef | xx | -| 0123456789 | yyyyyyy.| -| |.yyyyyyy.| -| |.yyyy | -+-[ RECORD 2 ]-----+---------+ -| 0123456789abcdef | xxxx | -| 0123456789 | yyyyyyy.| -| |.yyyyyyy.| -| |.yy | -+-[ RECORD 3 ]-----+---------+ -| 0123456789abcdef | xxxxxx | -| 0123456789 | yyyyyyy.| -| |.yyyyyyy | -+-[ RECORD 4 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.x | -| 0123456789 | yyyyyyy.| -| |.yyyyy | -+-[ RECORD 5 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxx | -| 0123456789 | yyyyyyy.| -| |.yyy | -+-[ RECORD 6 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxxxx | -| 0123456789 | yyyyyyy.| -| |.y | -+-[ RECORD 7 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxxxxxx | -| 0123456789 | yyyyyy | -+-[ RECORD 8 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxxxxxx.| -| |.xx | -| 0123456789 | yyyy | -+-[ RECORD 9 ]-----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxxxxxx.| -| |.xxxx | -| 0123456789 | yy | -+-[ RECORD 10 ]----+---------+ -| 0123456789abcdef | xxxxxxx.| -| |.xxxxxxx.| -| |.xxxxxx | -| 0123456789 | | -+------------------+---------+ - -\pset expanded on -\pset columns 20 -\pset border 0 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyyyyyyyyyyyyyyyyy -* Record 2 -0123456789abcdef xxxx -0123456789 yyyyyyyyyyyyyyyy -* Record 3 -0123456789abcdef xxxxxx -0123456789 yyyyyyyyyyyyyy -* Record 4 -0123456789abcdef xxxxxxxx -0123456789 yyyyyyyyyyyy -* Record 5 -0123456789abcdef xxxxxxxxxx -0123456789 yyyyyyyyyy -* Record 6 -0123456789abcdef xxxxxxxxxxxx -0123456789 yyyyyyyy -* Record 7 -0123456789abcdef xxxxxxxxxxxxxx -0123456789 yyyyyy -* Record 8 -0123456789abcdef xxxxxxxxxxxxxxxx -0123456789 yyyy -* Record 9 -0123456789abcdef xxxxxxxxxxxxxxxxxx -0123456789 yy -* Record 10 -0123456789abcdef xxxxxxxxxxxxxxxxxxxx -0123456789 - -\pset format wrapped -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyy. - .yyy. - .yyy. - .yyy. - .yyy. - .yyy -* Record 2 -0123456789abcdef xxx. - .x -0123456789 yyy. - .yyy. - .yyy. - .yyy. - .yyy. - .y -* Record 3 -0123456789abcdef xxx. - .xxx -0123456789 yyy. - .yyy. - .yyy. - .yyy. - .yy -* Record 4 -0123456789abcdef xxx. - .xxx. - .xx -0123456789 yyy. - .yyy. - .yyy. - .yyy -* Record 5 -0123456789abcdef xxx. - .xxx. - .xxx. - .x -0123456789 yyy. - .yyy. - .yyy. - .y -* Record 6 -0123456789abcdef xxx. - .xxx. - .xxx. - .xxx -0123456789 yyy. - .yyy. - .yy -* Record 7 -0123456789abcdef xxx. - .xxx. - .xxx. - .xxx. - .xx -0123456789 yyy. - .yyy -* Record 8 -0123456789abcdef xxx. - .xxx. - .xxx. - .xxx. - .xxx. - .x -0123456789 yyy. - .y -* Record 9 -0123456789abcdef xxx. - .xxx. - .xxx. - .xxx. - .xxx. - .xxx -0123456789 yy -* Record 10 -0123456789abcdef xxx. - .xxx. - .xxx. - .xxx. - .xxx. - .xxx. - .xx -0123456789 - -\pset border 1 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; --[ RECORD 1 ]----+--------------------- -0123456789abcdef | xx -0123456789 | yyyyyyyyyyyyyyyyyy --[ RECORD 2 ]----+--------------------- -0123456789abcdef | xxxx -0123456789 | yyyyyyyyyyyyyyyy --[ RECORD 3 ]----+--------------------- -0123456789abcdef | xxxxxx -0123456789 | yyyyyyyyyyyyyy --[ RECORD 4 ]----+--------------------- -0123456789abcdef | xxxxxxxx -0123456789 | yyyyyyyyyyyy --[ RECORD 5 ]----+--------------------- -0123456789abcdef | xxxxxxxxxx -0123456789 | yyyyyyyyyy --[ RECORD 6 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxx -0123456789 | yyyyyyyy --[ RECORD 7 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxx -0123456789 | yyyyyy --[ RECORD 8 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxx -0123456789 | yyyy --[ RECORD 9 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxx -0123456789 | yy --[ RECORD 10 ]---+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxxxx -0123456789 | - -\pset format wrapped -execute q; --[ RECORD 1 ]----+---- -0123456789abcdef | xx -0123456789 | yyy. - |.yyy. - |.yyy. - |.yyy. - |.yyy. - |.yyy --[ RECORD 2 ]----+---- -0123456789abcdef | xxx. - |.x -0123456789 | yyy. - |.yyy. - |.yyy. - |.yyy. - |.yyy. - |.y --[ RECORD 3 ]----+---- -0123456789abcdef | xxx. - |.xxx -0123456789 | yyy. - |.yyy. - |.yyy. - |.yyy. - |.yy --[ RECORD 4 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xx -0123456789 | yyy. - |.yyy. - |.yyy. - |.yyy --[ RECORD 5 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.x -0123456789 | yyy. - |.yyy. - |.yyy. - |.y --[ RECORD 6 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.xxx -0123456789 | yyy. - |.yyy. - |.yy --[ RECORD 7 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.xxx. - |.xx -0123456789 | yyy. - |.yyy --[ RECORD 8 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.xxx. - |.xxx. - |.x -0123456789 | yyy. - |.y --[ RECORD 9 ]----+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.xxx. - |.xxx. - |.xxx -0123456789 | yy --[ RECORD 10 ]---+---- -0123456789abcdef | xxx. - |.xxx. - |.xxx. - |.xxx. - |.xxx. - |.xxx. - |.xx -0123456789 | - -\pset border 2 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -+-[ RECORD 1 ]-----+----------------------+ -| 0123456789abcdef | xx | -| 0123456789 | yyyyyyyyyyyyyyyyyy | -+-[ RECORD 2 ]-----+----------------------+ -| 0123456789abcdef | xxxx | -| 0123456789 | yyyyyyyyyyyyyyyy | -+-[ RECORD 3 ]-----+----------------------+ -| 0123456789abcdef | xxxxxx | -| 0123456789 | yyyyyyyyyyyyyy | -+-[ RECORD 4 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxx | -| 0123456789 | yyyyyyyyyyyy | -+-[ RECORD 5 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxx | -| 0123456789 | yyyyyyyyyy | -+-[ RECORD 6 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxx | -| 0123456789 | yyyyyyyy | -+-[ RECORD 7 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxx | -| 0123456789 | yyyyyy | -+-[ RECORD 8 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxx | -| 0123456789 | yyyy | -+-[ RECORD 9 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxx | -| 0123456789 | yy | -+-[ RECORD 10 ]----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxxxx | -| 0123456789 | | -+------------------+----------------------+ - -\pset format wrapped -execute q; -+-[ RECORD 1 ]-----+-----+ -| 0123456789abcdef | xx | -| 0123456789 | yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy | -+-[ RECORD 2 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.x | -| 0123456789 | yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy.| -| |.y | -+-[ RECORD 3 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx | -| 0123456789 | yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy.| -| |.yy | -+-[ RECORD 4 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xx | -| 0123456789 | yyy.| -| |.yyy.| -| |.yyy.| -| |.yyy | -+-[ RECORD 5 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.x | -| 0123456789 | yyy.| -| |.yyy.| -| |.yyy.| -| |.y | -+-[ RECORD 6 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx | -| 0123456789 | yyy.| -| |.yyy.| -| |.yy | -+-[ RECORD 7 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xx | -| 0123456789 | yyy.| -| |.yyy | -+-[ RECORD 8 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.x | -| 0123456789 | yyy.| -| |.y | -+-[ RECORD 9 ]-----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx | -| 0123456789 | yy | -+-[ RECORD 10 ]----+-----+ -| 0123456789abcdef | xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xxx.| -| |.xx | -| 0123456789 | | -+------------------+-----+ - -\pset linestyle old-ascii -\pset expanded off -\pset columns 40 -\pset border 0 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; - 0123456789abcdef 0123456789 --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(10 rows) - -\pset format wrapped -execute q; - 0123456789abcdef 0123456789 --------------------- ------------------ -xx yyyyyyyyyyyyyyyyyy -xxxx yyyyyyyyyyyyyyyy -xxxxxx yyyyyyyyyyyyyy -xxxxxxxx yyyyyyyyyyyy -xxxxxxxxxx yyyyyyyyyy -xxxxxxxxxxxx yyyyyyyy -xxxxxxxxxxxxxx yyyyyy -xxxxxxxxxxxxxxxx yyyy -xxxxxxxxxxxxxxxxxx yy -xxxxxxxxxxxxxxxxxxxx -(10 rows) - -\pset border 1 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; - 0123456789abcdef | 0123456789 -----------------------+-------------------- - xx | yyyyyyyyyyyyyyyyyy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx | yyyyyyyyyyyyyy - xxxxxxxx | yyyyyyyyyyyy - xxxxxxxxxx | yyyyyyyyyy - xxxxxxxxxxxx | yyyyyyyy - xxxxxxxxxxxxxx | yyyyyy - xxxxxxxxxxxxxxxx | yyyy - xxxxxxxxxxxxxxxxxx | yy - xxxxxxxxxxxxxxxxxxxx | -(10 rows) - -\pset format wrapped -execute q; - 0123456789abcdef | 0123456789 ----------------------+------------------ - xx | yyyyyyyyyyyyyyyy - ; yy - xxxx | yyyyyyyyyyyyyyyy - xxxxxx | yyyyyyyyyyyyyy - xxxxxxxx | yyyyyyyyyyyy - xxxxxxxxxx | yyyyyyyyyy - xxxxxxxxxxxx | yyyyyyyy - xxxxxxxxxxxxxx | yyyyyy - xxxxxxxxxxxxxxxx | yyyy - xxxxxxxxxxxxxxxxxx | yy - xxxxxxxxxxxxxxxxxxx | - x -(10 rows) - -\pset border 2 -\pset format unaligned -execute q; -0123456789abcdef|0123456789 -xx|yyyyyyyyyyyyyyyyyy -xxxx|yyyyyyyyyyyyyyyy -xxxxxx|yyyyyyyyyyyyyy -xxxxxxxx|yyyyyyyyyyyy -xxxxxxxxxx|yyyyyyyyyy -xxxxxxxxxxxx|yyyyyyyy -xxxxxxxxxxxxxx|yyyyyy -xxxxxxxxxxxxxxxx|yyyy -xxxxxxxxxxxxxxxxxx|yy -xxxxxxxxxxxxxxxxxxxx| -(10 rows) -\pset format aligned -execute q; -+----------------------+--------------------+ -| 0123456789abcdef | 0123456789 | -+----------------------+--------------------+ -| xx | yyyyyyyyyyyyyyyyyy | -| xxxx | yyyyyyyyyyyyyyyy | -| xxxxxx | yyyyyyyyyyyyyy | -| xxxxxxxx | yyyyyyyyyyyy | -| xxxxxxxxxx | yyyyyyyyyy | -| xxxxxxxxxxxx | yyyyyyyy | -| xxxxxxxxxxxxxx | yyyyyy | -| xxxxxxxxxxxxxxxx | yyyy | -| xxxxxxxxxxxxxxxxxx | yy | -| xxxxxxxxxxxxxxxxxxxx | | -+----------------------+--------------------+ -(10 rows) - -\pset format wrapped -execute q; -+--------------------+-----------------+ -| 0123456789abcdef | 0123456789 | -+--------------------+-----------------+ -| xx | yyyyyyyyyyyyyyy | -| ; yyy | -| xxxx | yyyyyyyyyyyyyyy | -| ; y | -| xxxxxx | yyyyyyyyyyyyyy | -| xxxxxxxx | yyyyyyyyyyyy | -| xxxxxxxxxx | yyyyyyyyyy | -| xxxxxxxxxxxx | yyyyyyyy | -| xxxxxxxxxxxxxx | yyyyyy | -| xxxxxxxxxxxxxxxx | yyyy | -| xxxxxxxxxxxxxxxxxx | yy | -| xxxxxxxxxxxxxxxxxx | | -| xx | -+--------------------+-----------------+ -(10 rows) - -\pset expanded on -\pset border 0 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyyyyyyyyyyyyyyyyy -* Record 2 -0123456789abcdef xxxx -0123456789 yyyyyyyyyyyyyyyy -* Record 3 -0123456789abcdef xxxxxx -0123456789 yyyyyyyyyyyyyy -* Record 4 -0123456789abcdef xxxxxxxx -0123456789 yyyyyyyyyyyy -* Record 5 -0123456789abcdef xxxxxxxxxx -0123456789 yyyyyyyyyy -* Record 6 -0123456789abcdef xxxxxxxxxxxx -0123456789 yyyyyyyy -* Record 7 -0123456789abcdef xxxxxxxxxxxxxx -0123456789 yyyyyy -* Record 8 -0123456789abcdef xxxxxxxxxxxxxxxx -0123456789 yyyy -* Record 9 -0123456789abcdef xxxxxxxxxxxxxxxxxx -0123456789 yy -* Record 10 -0123456789abcdef xxxxxxxxxxxxxxxxxxxx -0123456789 - -\pset format wrapped -execute q; -* Record 1 -0123456789abcdef xx -0123456789 yyyyyyyyyyyyyyyyyy -* Record 2 -0123456789abcdef xxxx -0123456789 yyyyyyyyyyyyyyyy -* Record 3 -0123456789abcdef xxxxxx -0123456789 yyyyyyyyyyyyyy -* Record 4 -0123456789abcdef xxxxxxxx -0123456789 yyyyyyyyyyyy -* Record 5 -0123456789abcdef xxxxxxxxxx -0123456789 yyyyyyyyyy -* Record 6 -0123456789abcdef xxxxxxxxxxxx -0123456789 yyyyyyyy -* Record 7 -0123456789abcdef xxxxxxxxxxxxxx -0123456789 yyyyyy -* Record 8 -0123456789abcdef xxxxxxxxxxxxxxxx -0123456789 yyyy -* Record 9 -0123456789abcdef xxxxxxxxxxxxxxxxxx -0123456789 yy -* Record 10 -0123456789abcdef xxxxxxxxxxxxxxxxxxxx -0123456789 - -\pset border 1 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; --[ RECORD 1 ]----+--------------------- -0123456789abcdef | xx -0123456789 | yyyyyyyyyyyyyyyyyy --[ RECORD 2 ]----+--------------------- -0123456789abcdef | xxxx -0123456789 | yyyyyyyyyyyyyyyy --[ RECORD 3 ]----+--------------------- -0123456789abcdef | xxxxxx -0123456789 | yyyyyyyyyyyyyy --[ RECORD 4 ]----+--------------------- -0123456789abcdef | xxxxxxxx -0123456789 | yyyyyyyyyyyy --[ RECORD 5 ]----+--------------------- -0123456789abcdef | xxxxxxxxxx -0123456789 | yyyyyyyyyy --[ RECORD 6 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxx -0123456789 | yyyyyyyy --[ RECORD 7 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxx -0123456789 | yyyyyy --[ RECORD 8 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxx -0123456789 | yyyy --[ RECORD 9 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxx -0123456789 | yy --[ RECORD 10 ]---+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxxxx -0123456789 | - -\pset format wrapped -execute q; --[ RECORD 1 ]----+--------------------- -0123456789abcdef | xx -0123456789 | yyyyyyyyyyyyyyyyyy --[ RECORD 2 ]----+--------------------- -0123456789abcdef | xxxx -0123456789 | yyyyyyyyyyyyyyyy --[ RECORD 3 ]----+--------------------- -0123456789abcdef | xxxxxx -0123456789 | yyyyyyyyyyyyyy --[ RECORD 4 ]----+--------------------- -0123456789abcdef | xxxxxxxx -0123456789 | yyyyyyyyyyyy --[ RECORD 5 ]----+--------------------- -0123456789abcdef | xxxxxxxxxx -0123456789 | yyyyyyyyyy --[ RECORD 6 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxx -0123456789 | yyyyyyyy --[ RECORD 7 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxx -0123456789 | yyyyyy --[ RECORD 8 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxx -0123456789 | yyyy --[ RECORD 9 ]----+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxx -0123456789 | yy --[ RECORD 10 ]---+--------------------- -0123456789abcdef | xxxxxxxxxxxxxxxxxxxx -0123456789 | - -\pset border 2 -\pset format unaligned -execute q; -0123456789abcdef|xx -0123456789|yyyyyyyyyyyyyyyyyy - -0123456789abcdef|xxxx -0123456789|yyyyyyyyyyyyyyyy - -0123456789abcdef|xxxxxx -0123456789|yyyyyyyyyyyyyy - -0123456789abcdef|xxxxxxxx -0123456789|yyyyyyyyyyyy - -0123456789abcdef|xxxxxxxxxx -0123456789|yyyyyyyyyy - -0123456789abcdef|xxxxxxxxxxxx -0123456789|yyyyyyyy - -0123456789abcdef|xxxxxxxxxxxxxx -0123456789|yyyyyy - -0123456789abcdef|xxxxxxxxxxxxxxxx -0123456789|yyyy - -0123456789abcdef|xxxxxxxxxxxxxxxxxx -0123456789|yy - -0123456789abcdef|xxxxxxxxxxxxxxxxxxxx -0123456789| -\pset format aligned -execute q; -+-[ RECORD 1 ]-----+----------------------+ -| 0123456789abcdef | xx | -| 0123456789 | yyyyyyyyyyyyyyyyyy | -+-[ RECORD 2 ]-----+----------------------+ -| 0123456789abcdef | xxxx | -| 0123456789 | yyyyyyyyyyyyyyyy | -+-[ RECORD 3 ]-----+----------------------+ -| 0123456789abcdef | xxxxxx | -| 0123456789 | yyyyyyyyyyyyyy | -+-[ RECORD 4 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxx | -| 0123456789 | yyyyyyyyyyyy | -+-[ RECORD 5 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxx | -| 0123456789 | yyyyyyyyyy | -+-[ RECORD 6 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxx | -| 0123456789 | yyyyyyyy | -+-[ RECORD 7 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxx | -| 0123456789 | yyyyyy | -+-[ RECORD 8 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxx | -| 0123456789 | yyyy | -+-[ RECORD 9 ]-----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxx | -| 0123456789 | yy | -+-[ RECORD 10 ]----+----------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxxxxx | -| 0123456789 | | -+------------------+----------------------+ - -\pset format wrapped -execute q; -+-[ RECORD 1 ]-----+-------------------+ -| 0123456789abcdef | xx | -| 0123456789 | yyyyyyyyyyyyyyyyy | -| ; y | -+-[ RECORD 2 ]-----+-------------------+ -| 0123456789abcdef | xxxx | -| 0123456789 | yyyyyyyyyyyyyyyy | -+-[ RECORD 3 ]-----+-------------------+ -| 0123456789abcdef | xxxxxx | -| 0123456789 | yyyyyyyyyyyyyy | -+-[ RECORD 4 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxx | -| 0123456789 | yyyyyyyyyyyy | -+-[ RECORD 5 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxxxx | -| 0123456789 | yyyyyyyyyy | -+-[ RECORD 6 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxxxxxx | -| 0123456789 | yyyyyyyy | -+-[ RECORD 7 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxx | -| 0123456789 | yyyyyy | -+-[ RECORD 8 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxx | -| 0123456789 | yyyy | -+-[ RECORD 9 ]-----+-------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxx | -| ; x | -| 0123456789 | yy | -+-[ RECORD 10 ]----+-------------------+ -| 0123456789abcdef | xxxxxxxxxxxxxxxxx | -| ; xxx | -| 0123456789 | | -+------------------+-------------------+ - -deallocate q; -\pset linestyle ascii -\pset border 1 --- support table for output-format tests (useful to create a footer) -create table psql_serial_tab (id serial); --- test header/footer/tuples_only behavior in aligned/unaligned/wrapped cases -\pset format aligned -\pset expanded off -\d psql_serial_tab_id_seq - Sequence "public.psql_serial_tab_id_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Owned by: public.psql_serial_tab.id - -\pset tuples_only true -\df exp - pg_catalog | exp | double precision | double precision | func - pg_catalog | exp | numeric | numeric | func - -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -Sequence "public.psql_serial_tab_id_seq" --[ RECORD 1 ]--------- -Type | integer -Start | 1 -Minimum | 1 -Maximum | 2147483647 -Increment | 1 -Cycles? | no -Cache | 1 - -Owned by: public.psql_serial_tab.id - -\pset tuples_only true -\df exp -Schema | pg_catalog -Name | exp -Result data type | double precision -Argument data types | double precision -Type | func ---------------------+----------------- -Schema | pg_catalog -Name | exp -Result data type | numeric -Argument data types | numeric -Type | func - -\pset tuples_only false --- empty table is a special case for this format -select 1 where false; -(0 rows) - -\pset format unaligned -\pset expanded off -\d psql_serial_tab_id_seq -Sequence "public.psql_serial_tab_id_seq" -Type|Start|Minimum|Maximum|Increment|Cycles?|Cache -integer|1|1|2147483647|1|no|1 -Owned by: public.psql_serial_tab.id -\pset tuples_only true -\df exp -pg_catalog|exp|double precision|double precision|func -pg_catalog|exp|numeric|numeric|func -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -Sequence "public.psql_serial_tab_id_seq" - -Type|integer -Start|1 -Minimum|1 -Maximum|2147483647 -Increment|1 -Cycles?|no -Cache|1 - -Owned by: public.psql_serial_tab.id -\pset tuples_only true -\df exp -Schema|pg_catalog -Name|exp -Result data type|double precision -Argument data types|double precision -Type|func - -Schema|pg_catalog -Name|exp -Result data type|numeric -Argument data types|numeric -Type|func -\pset tuples_only false -\pset format wrapped -\pset expanded off -\d psql_serial_tab_id_seq - Sequence "public.psql_serial_tab_id_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Owned by: public.psql_serial_tab.id - -\pset tuples_only true -\df exp - pg_catalog | exp | double precision | double precision | func - pg_catalog | exp | numeric | numeric | func - -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -Sequence "public.psql_serial_tab_id_seq" --[ RECORD 1 ]--------- -Type | integer -Start | 1 -Minimum | 1 -Maximum | 2147483647 -Increment | 1 -Cycles? | no -Cache | 1 - -Owned by: public.psql_serial_tab.id - -\pset tuples_only true -\df exp -Schema | pg_catalog -Name | exp -Result data type | double precision -Argument data types | double precision -Type | func ---------------------+----------------- -Schema | pg_catalog -Name | exp -Result data type | numeric -Argument data types | numeric -Type | func - -\pset tuples_only false --- check conditional am display -\pset expanded off -CREATE SCHEMA tableam_display; -CREATE ROLE regress_display_role; -ALTER SCHEMA tableam_display OWNER TO regress_display_role; -SET search_path TO tableam_display; -CREATE ACCESS METHOD heap_psql TYPE TABLE HANDLER heap_tableam_handler; -SET ROLE TO regress_display_role; --- Use only relations with a physical size of zero. -CREATE TABLE tbl_heap_psql(f1 int, f2 char(100)) using heap_psql; -CREATE TABLE tbl_heap(f1 int, f2 char(100)) using heap; -CREATE VIEW view_heap_psql AS SELECT f1 from tbl_heap_psql; -CREATE MATERIALIZED VIEW mat_view_heap_psql USING heap_psql AS SELECT f1 from tbl_heap_psql; -\d+ tbl_heap_psql - Table "tableam_display.tbl_heap_psql" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+----------------+-----------+----------+---------+----------+--------------+------------- - f1 | integer | | | | plain | | - f2 | character(100) | | | | extended | | - -\d+ tbl_heap - Table "tableam_display.tbl_heap" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+----------------+-----------+----------+---------+----------+--------------+------------- - f1 | integer | | | | plain | | - f2 | character(100) | | | | extended | | - -\set HIDE_TABLEAM off -\d+ tbl_heap_psql - Table "tableam_display.tbl_heap_psql" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+----------------+-----------+----------+---------+----------+--------------+------------- - f1 | integer | | | | plain | | - f2 | character(100) | | | | extended | | -Access method: heap_psql - -\d+ tbl_heap - Table "tableam_display.tbl_heap" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+----------------+-----------+----------+---------+----------+--------------+------------- - f1 | integer | | | | plain | | - f2 | character(100) | | | | extended | | -Access method: heap - --- AM is displayed for tables, indexes and materialized views. -\d+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | 0 bytes | - tableam_display | tbl_heap | table | regress_display_role | permanent | heap | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | 0 bytes | - tableam_display | view_heap_psql | view | regress_display_role | permanent | | 0 bytes | -(4 rows) - -\dt+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+---------------+-------+----------------------+-------------+---------------+---------+------------- - tableam_display | tbl_heap | table | regress_display_role | permanent | heap | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | 0 bytes | -(2 rows) - -\dm+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | 0 bytes | -(1 row) - --- But not for views and sequences. -\dv+ - List of relations - Schema | Name | Type | Owner | Persistence | Size | Description ------------------+----------------+------+----------------------+-------------+---------+------------- - tableam_display | view_heap_psql | view | regress_display_role | permanent | 0 bytes | -(1 row) - -\set HIDE_TABLEAM on -\d+ - List of relations - Schema | Name | Type | Owner | Persistence | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | 0 bytes | - tableam_display | tbl_heap | table | regress_display_role | permanent | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | 0 bytes | - tableam_display | view_heap_psql | view | regress_display_role | permanent | 0 bytes | -(4 rows) - -RESET ROLE; -RESET search_path; -DROP SCHEMA tableam_display CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table tableam_display.tbl_heap_psql -drop cascades to table tableam_display.tbl_heap -drop cascades to view tableam_display.view_heap_psql -drop cascades to materialized view tableam_display.mat_view_heap_psql -DROP ACCESS METHOD heap_psql; -DROP ROLE regress_display_role; --- test numericlocale (as best we can without control of psql's locale) -\pset format aligned -\pset expanded off -\pset numericlocale true -select n, -n as m, n * 111 as x, '1e90'::float8 as f -from generate_series(0,3) n; - n | m | x | f ----+----+-----+------- - 0 | 0 | 0 | 1e+90 - 1 | -1 | 111 | 1e+90 - 2 | -2 | 222 | 1e+90 - 3 | -3 | 333 | 1e+90 -(4 rows) - -\pset numericlocale false --- test asciidoc output format -\pset format asciidoc -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq - -.Sequence "public.psql_serial_tab_id_seq" -[options="header",cols="l,>l,>l,>l,l",frame="none"] -|==== -^l|Type ^l|Start ^l|Minimum ^l|Maximum ^l|Increment ^l|Cycles? ^l|Cache -|integer |1 |1 |2147483647 |1 |no |1 -|==== - -.... -Owned by: public.psql_serial_tab.id -.... -\pset tuples_only true -\df exp - -[cols="l|1 -l|1 -l|2147483647 -l|1 -l|1 -|==== - -.... -Owned by: public.psql_serial_tab.id -.... -\pset tuples_only true -\df exp - -[cols="h,l",frame="none"] -|==== -2+| -l|1 -2+^|Record 2 -l|2 -|==== -\pset border 1 -execute q; - -[cols="h,l",frame="none"] -|==== -2+^|Record 1 -l|1 -2+^|Record 2 -l|2 -|==== -\pset border 2 -execute q; - -[cols="h,l",frame="all",grid="all"] -|==== -2+^|Record 1 -l|1 -2+^|Record 2 -l|2 -|==== -deallocate q; --- test csv output format -\pset format csv -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq -Type,Start,Minimum,Maximum,Increment,Cycles?,Cache -integer,1,1,2147483647,1,no,1 -\pset tuples_only true -\df exp -pg_catalog,exp,double precision,double precision,func -pg_catalog,exp,numeric,numeric,func -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -Type,integer -Start,1 -Minimum,1 -Maximum,2147483647 -Increment,1 -Cycles?,no -Cache,1 -\pset tuples_only true -\df exp -Schema,pg_catalog -Name,exp -Result data type,double precision -Argument data types,double precision -Type,func -Schema,pg_catalog -Name,exp -Result data type,numeric -Argument data types,numeric -Type,func -\pset tuples_only false -prepare q as - select 'some"text' as "a""title", E' \n' as "junk", - ' ' as "empty", n as int - from generate_series(1,2) as n; -\pset expanded off -execute q; -"a""title",junk,empty,int -"some""text"," -", ,1 -"some""text"," -", ,2 -\pset expanded on -execute q; -"a""title","some""text" -junk," -" -empty, -int,1 -"a""title","some""text" -junk," -" -empty, -int,2 -deallocate q; --- special cases -\pset expanded off -select 'comma,comma' as comma, 'semi;semi' as semi; -comma,semi -"comma,comma",semi;semi -\pset csv_fieldsep ';' -select 'comma,comma' as comma, 'semi;semi' as semi; -comma;semi -comma,comma;"semi;semi" -select '\.' as data; -data -"\." -\pset csv_fieldsep '.' -select '\' as d1, '' as d2; -"d1"."d2" -"\"."" --- illegal csv separators -\pset csv_fieldsep '' -\pset: csv_fieldsep must be a single one-byte character -\pset csv_fieldsep '\0' -\pset: csv_fieldsep must be a single one-byte character -\pset csv_fieldsep '\n' -\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return -\pset csv_fieldsep '\r' -\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return -\pset csv_fieldsep '"' -\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return -\pset csv_fieldsep ',,' -\pset: csv_fieldsep must be a single one-byte character -\pset csv_fieldsep ',' --- test html output format -\pset format html -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq - - - - - - - - - - - - - - - - - - - - -
Sequence "public.psql_serial_tab_id_seq"
TypeStartMinimumMaximumIncrementCycles?Cache
integer1121474836471no1
-

Owned by: public.psql_serial_tab.id
-

-\pset tuples_only true -\df exp - - - - - - - - - - - - - - - -
pg_catalogexpdouble precisiondouble precisionfunc
pg_catalogexpnumericnumericfunc
- -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sequence "public.psql_serial_tab_id_seq"
Record 1
Typeinteger
Start1
Minimum1
Maximum2147483647
Increment1
Cycles?no
Cache1
-

Owned by: public.psql_serial_tab.id
-

-\pset tuples_only true -\df exp - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Schemapg_catalog
Nameexp
Result data typedouble precision
Argument data typesdouble precision
Typefunc
 
Schemapg_catalog
Nameexp
Result data typenumeric
Argument data typesnumeric
Typefunc
- -\pset tuples_only false -prepare q as - select 'some"text' as "a&title", E' \n' as "junk", - ' ' as "empty", n as int - from generate_series(1,2) as n; -\pset expanded off -\pset border 0 -execute q; - - - - - - - - - - - - - - - - - - - -
a&titlejunkemptyint
some"text  <foo>
-<bar>
  1
some"text  <foo>
-<bar>
  2
-

(2 rows)
-

-\pset border 1 -execute q; - - - - - - - - - - - - - - - - - - - -
a&titlejunkemptyint
some"text  <foo>
-<bar>
  1
some"text  <foo>
-<bar>
  2
-

(2 rows)
-

-\pset tableattr foobar -execute q; - - - - - - - - - - - - - - - - - - - -
a&titlejunkemptyint
some"text  <foo>
-<bar>
  1
some"text  <foo>
-<bar>
  2
-

(2 rows)
-

-\pset tableattr -\pset expanded on -\pset border 0 -execute q; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Record 1
a&titlesome"text
junk  <foo>
-<bar>
empty 
int1
Record 2
a&titlesome"text
junk  <foo>
-<bar>
empty 
int2
- -\pset border 1 -execute q; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Record 1
a&titlesome"text
junk  <foo>
-<bar>
empty 
int1
Record 2
a&titlesome"text
junk  <foo>
-<bar>
empty 
int2
- -\pset tableattr foobar -execute q; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Record 1
a&titlesome"text
junk  <foo>
-<bar>
empty 
int1
Record 2
a&titlesome"text
junk  <foo>
-<bar>
empty 
int2
- -\pset tableattr -deallocate q; --- test latex output format -\pset format latex -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq -\begin{center} -Sequence "public.psql\_serial\_tab\_id\_seq" -\end{center} - -\begin{tabular}{l | r | r | r | r | l | r} -\textit{Type} & \textit{Start} & \textit{Minimum} & \textit{Maximum} & \textit{Increment} & \textit{Cycles?} & \textit{Cache} \\ -\hline -integer & 1 & 1 & 2147483647 & 1 & no & 1 \\ -\end{tabular} - -\noindent Owned by: public.psql\_serial\_tab.id \\ - -\pset tuples_only true -\df exp -\begin{tabular}{l | l | l | l | l} -pg\_catalog & exp & double precision & double precision & func \\ -pg\_catalog & exp & numeric & numeric & func \\ -\end{tabular} - -\noindent -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -\begin{center} -Sequence "public.psql\_serial\_tab\_id\_seq" -\end{center} - -\begin{tabular}{c|l} -\multicolumn{2}{c}{\textit{Record 1}} \\ -\hline -Type & integer \\ -Start & 1 \\ -Minimum & 1 \\ -Maximum & 2147483647 \\ -Increment & 1 \\ -Cycles? & no \\ -Cache & 1 \\ -\end{tabular} - -\noindent Owned by: public.psql\_serial\_tab.id \\ - -\pset tuples_only true -\df exp -\begin{tabular}{c|l} -\hline -Schema & pg\_catalog \\ -Name & exp \\ -Result data type & double precision \\ -Argument data types & double precision \\ -Type & func \\ -\hline -Schema & pg\_catalog \\ -Name & exp \\ -Result data type & numeric \\ -Argument data types & numeric \\ -Type & func \\ -\end{tabular} - -\noindent -\pset tuples_only false -prepare q as - select 'some\more_text' as "a$title", E' #%&^~|\n{bar}' as "junk", - ' ' as "empty", n as int - from generate_series(1,2) as n; -\pset expanded off -\pset border 0 -execute q; -\begin{tabular}{lllr} -\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ -\hline -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\ -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\ -\end{tabular} - -\noindent (2 rows) \\ - -\pset border 1 -execute q; -\begin{tabular}{l | l | l | r} -\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ -\hline -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\ -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\ -\end{tabular} - -\noindent (2 rows) \\ - -\pset border 2 -execute q; -\begin{tabular}{| l | l | l | r |} -\hline -\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ -\hline -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\ -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\ -\hline -\end{tabular} - -\noindent (2 rows) \\ - -\pset border 3 -execute q; -\begin{tabular}{| l | l | l | r |} -\hline -\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ -\hline -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\ -\hline -some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\ -\hline -\end{tabular} - -\noindent (2 rows) \\ - -\pset expanded on -\pset border 0 -execute q; -\begin{tabular}{cl} -\multicolumn{2}{c}{\textit{Record 1}} \\ -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\multicolumn{2}{c}{\textit{Record 2}} \\ -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\end{tabular} - -\noindent -\pset border 1 -execute q; -\begin{tabular}{c|l} -\multicolumn{2}{c}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\multicolumn{2}{c}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\end{tabular} - -\noindent -\pset border 2 -execute q; -\begin{tabular}{|c|l|} -\hline -\multicolumn{2}{|c|}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\hline -\multicolumn{2}{|c|}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\hline -\end{tabular} - -\noindent -\pset border 3 -execute q; -\begin{tabular}{|c|l|} -\hline -\multicolumn{2}{|c|}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\hline -\multicolumn{2}{|c|}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\hline -\end{tabular} - -\noindent -deallocate q; --- test latex-longtable output format -\pset format latex-longtable -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq -\begin{longtable}{l | r | r | r | r | l | r} -\small\textbf{\textit{Type}} & \small\textbf{\textit{Start}} & \small\textbf{\textit{Minimum}} & \small\textbf{\textit{Maximum}} & \small\textbf{\textit{Increment}} & \small\textbf{\textit{Cycles?}} & \small\textbf{\textit{Cache}} \\ -\midrule -\endfirsthead -\small\textbf{\textit{Type}} & \small\textbf{\textit{Start}} & \small\textbf{\textit{Minimum}} & \small\textbf{\textit{Maximum}} & \small\textbf{\textit{Increment}} & \small\textbf{\textit{Cycles?}} & \small\textbf{\textit{Cache}} \\ -\midrule -\endhead -\caption[Sequence "public.psql\_serial\_tab\_id\_seq" (Continued)]{Sequence "public.psql\_serial\_tab\_id\_seq"} -\endfoot -\caption[Sequence "public.psql\_serial\_tab\_id\_seq"]{Sequence "public.psql\_serial\_tab\_id\_seq"} -\endlastfoot -\raggedright{integer} -& -\raggedright{1} -& -\raggedright{1} -& -\raggedright{2147483647} -& -\raggedright{1} -& -\raggedright{no} -& -\raggedright{1} \tabularnewline -\end{longtable} -\pset tuples_only true -\df exp -\begin{longtable}{l | l | l | l | l} -\raggedright{pg\_catalog} -& -\raggedright{exp} -& -\raggedright{double precision} -& -\raggedright{double precision} -& -\raggedright{func} \tabularnewline -\raggedright{pg\_catalog} -& -\raggedright{exp} -& -\raggedright{numeric} -& -\raggedright{numeric} -& -\raggedright{func} \tabularnewline -\end{longtable} -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -\begin{center} -Sequence "public.psql\_serial\_tab\_id\_seq" -\end{center} - -\begin{tabular}{c|l} -\multicolumn{2}{c}{\textit{Record 1}} \\ -\hline -Type & integer \\ -Start & 1 \\ -Minimum & 1 \\ -Maximum & 2147483647 \\ -Increment & 1 \\ -Cycles? & no \\ -Cache & 1 \\ -\end{tabular} - -\noindent Owned by: public.psql\_serial\_tab.id \\ - -\pset tuples_only true -\df exp -\begin{tabular}{c|l} -\hline -Schema & pg\_catalog \\ -Name & exp \\ -Result data type & double precision \\ -Argument data types & double precision \\ -Type & func \\ -\hline -Schema & pg\_catalog \\ -Name & exp \\ -Result data type & numeric \\ -Argument data types & numeric \\ -Type & func \\ -\end{tabular} - -\noindent -\pset tuples_only false -prepare q as - select 'some\more_text' as "a$title", E' #%&^~|\n{bar}' as "junk", - ' ' as "empty", n as int - from generate_series(1,2) as n; -\pset expanded off -\pset border 0 -execute q; -\begin{longtable}{lllr} -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endfirsthead -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endhead -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{1} \tabularnewline -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{2} \tabularnewline -\end{longtable} -\pset border 1 -execute q; -\begin{longtable}{l | l | l | r} -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endfirsthead -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endhead -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{1} \tabularnewline -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{2} \tabularnewline -\end{longtable} -\pset border 2 -execute q; -\begin{longtable}{| l | l | l | r |} -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endfirsthead -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endhead -\bottomrule -\endfoot -\bottomrule -\endlastfoot -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{1} \tabularnewline -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{2} \tabularnewline -\end{longtable} -\pset border 3 -execute q; -\begin{longtable}{| l | l | l | r |} -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endfirsthead -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\endhead -\bottomrule -\endfoot -\bottomrule -\endlastfoot -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{1} \tabularnewline - \hline -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{2} \tabularnewline - \hline -\end{longtable} -\pset tableattr lr -execute q; -\begin{longtable}{| p{lr\textwidth} | p{lr\textwidth} | p{lr\textwidth} | r |} -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\midrule -\endfirsthead -\toprule -\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ -\endhead -\bottomrule -\endfoot -\bottomrule -\endlastfoot -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{1} \tabularnewline - \hline -\raggedright{some\textbackslash{}more\_text} -& -\raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}} -& -\raggedright{ } -& -\raggedright{2} \tabularnewline - \hline -\end{longtable} -\pset tableattr -\pset expanded on -\pset border 0 -execute q; -\begin{tabular}{cl} -\multicolumn{2}{c}{\textit{Record 1}} \\ -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\multicolumn{2}{c}{\textit{Record 2}} \\ -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\end{tabular} - -\noindent -\pset border 1 -execute q; -\begin{tabular}{c|l} -\multicolumn{2}{c}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\multicolumn{2}{c}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\end{tabular} - -\noindent -\pset border 2 -execute q; -\begin{tabular}{|c|l|} -\hline -\multicolumn{2}{|c|}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\hline -\multicolumn{2}{|c|}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\hline -\end{tabular} - -\noindent -\pset border 3 -execute q; -\begin{tabular}{|c|l|} -\hline -\multicolumn{2}{|c|}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\hline -\multicolumn{2}{|c|}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\hline -\end{tabular} - -\noindent -\pset tableattr lr -execute q; -\begin{tabular}{|c|l|} -\hline -\multicolumn{2}{|c|}{\textit{Record 1}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 1 \\ -\hline -\multicolumn{2}{|c|}{\textit{Record 2}} \\ -\hline -a\$title & some\textbackslash{}more\_text \\ -junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\ -empty & \\ -int & 2 \\ -\hline -\end{tabular} - -\noindent -\pset tableattr -deallocate q; --- test troff-ms output format -\pset format troff-ms -\pset border 1 -\pset expanded off -\d psql_serial_tab_id_seq -.LP -.DS C -Sequence "public.psql_serial_tab_id_seq" -.DE -.LP -.TS -center; -l | r | r | r | r | l | r. -\fIType\fP \fIStart\fP \fIMinimum\fP \fIMaximum\fP \fIIncrement\fP \fICycles?\fP \fICache\fP -_ -integer 1 1 2147483647 1 no 1 -.TE -.DS L -Owned by: public.psql_serial_tab.id -.DE -\pset tuples_only true -\df exp -.LP -.TS -center; -l | l | l | l | l. -pg_catalog exp double precision double precision func -pg_catalog exp numeric numeric func -.TE -.DS L -.DE -\pset tuples_only false -\pset expanded on -\d psql_serial_tab_id_seq -.LP -.DS C -Sequence "public.psql_serial_tab_id_seq" -.DE -.LP -.TS -center; -c s. -\fIRecord 1\fP -_ -.T& -c | l. -Type integer -Start 1 -Minimum 1 -Maximum 2147483647 -Increment 1 -Cycles? no -Cache 1 -.TE -.DS L -Owned by: public.psql_serial_tab.id -.DE -\pset tuples_only true -\df exp -.LP -.TS -center; -c l; -_ -Schema pg_catalog -Name exp -Result data type double precision -Argument data types double precision -Type func -_ -Schema pg_catalog -Name exp -Result data type numeric -Argument data types numeric -Type func -.TE -.DS L -.DE -\pset tuples_only false -prepare q as - select 'some\text' as "a\title", E' \n' as "junk", - ' ' as "empty", n as int - from generate_series(1,2) as n; -\pset expanded off -\pset border 0 -execute q; -.LP -.TS -center; -lllr. -\fIa\(rstitle\fP \fIjunk\fP \fIempty\fP \fIint\fP -_ -some\(rstext - 1 -some\(rstext - 2 -.TE -.DS L -(2 rows) -.DE -\pset border 1 -execute q; -.LP -.TS -center; -l | l | l | r. -\fIa\(rstitle\fP \fIjunk\fP \fIempty\fP \fIint\fP -_ -some\(rstext - 1 -some\(rstext - 2 -.TE -.DS L -(2 rows) -.DE -\pset border 2 -execute q; -.LP -.TS -center box; -l | l | l | r. -\fIa\(rstitle\fP \fIjunk\fP \fIempty\fP \fIint\fP -_ -some\(rstext - 1 -some\(rstext - 2 -.TE -.DS L -(2 rows) -.DE -\pset expanded on -\pset border 0 -execute q; -.LP -.TS -center; -c s. -\fIRecord 1\fP -.T& -c l. -a\(rstitle some\(rstext -junk - -empty -int 1 -.T& -c s. -\fIRecord 2\fP -.T& -c l. -a\(rstitle some\(rstext -junk - -empty -int 2 -.TE -.DS L -.DE -\pset border 1 -execute q; -.LP -.TS -center; -c s. -\fIRecord 1\fP -_ -.T& -c | l. -a\(rstitle some\(rstext -junk - -empty -int 1 -.T& -c s. -\fIRecord 2\fP -_ -.T& -c | l. -a\(rstitle some\(rstext -junk - -empty -int 2 -.TE -.DS L -.DE -\pset border 2 -execute q; -.LP -.TS -center box; -c s. -\fIRecord 1\fP -_ -.T& -c l. -a\(rstitle some\(rstext -junk - -empty -int 1 -_ -.T& -c s. -\fIRecord 2\fP -_ -.T& -c l. -a\(rstitle some\(rstext -junk - -empty -int 2 -.TE -.DS L -.DE -deallocate q; --- check ambiguous format requests -\pset format a -\pset: ambiguous abbreviation "a" matches both "aligned" and "asciidoc" -\pset format l --- clean up after output format tests -drop table psql_serial_tab; -\pset format aligned -\pset expanded off -\pset border 1 --- \echo and allied features -\echo this is a test -this is a test -\echo -n without newline -without newline\echo with -n newline -with -n newline -\echo '-n' with newline --n with newline -\set foo bar -\echo foo = :foo -foo = bar -\qecho this is a test -this is a test -\qecho foo = :foo -foo = bar -\warn this is a test -this is a test -\warn foo = :foo -foo = bar --- tests for \if ... \endif -\if true - select 'okay'; - ?column? ----------- - okay -(1 row) - - select 'still okay'; - ?column? ------------- - still okay -(1 row) - -\else - not okay; - still not okay -\endif --- at this point query buffer should still have last valid line -\g - ?column? ------------- - still okay -(1 row) - --- \if should work okay on part of a query -select - \if true - 42 - \else - (bogus - \endif - forty_two; - forty_two ------------ - 42 -(1 row) - -select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; - forty_two ------------ - 42 -(1 row) - --- test a large nested if using a variety of true-equivalents -\if true - \if 1 - \if yes - \if on - \echo 'all true' -all true - \else - \echo 'should not print #1-1' - \endif - \else - \echo 'should not print #1-2' - \endif - \else - \echo 'should not print #1-3' - \endif -\else - \echo 'should not print #1-4' -\endif --- test a variety of false-equivalents in an if/elif/else structure -\if false - \echo 'should not print #2-1' -\elif 0 - \echo 'should not print #2-2' -\elif no - \echo 'should not print #2-3' -\elif off - \echo 'should not print #2-4' -\else - \echo 'all false' -all false -\endif --- test true-false elif after initial true branch -\if true - \echo 'should print #2-5' -should print #2-5 -\elif true - \echo 'should not print #2-6' -\elif false - \echo 'should not print #2-7' -\else - \echo 'should not print #2-8' -\endif --- test simple true-then-else -\if true - \echo 'first thing true' -first thing true -\else - \echo 'should not print #3-1' -\endif --- test simple false-true-else -\if false - \echo 'should not print #4-1' -\elif true - \echo 'second thing true' -second thing true -\else - \echo 'should not print #5-1' -\endif --- invalid boolean expressions are false -\if invalid boolean expression -unrecognized value "invalid boolean expression" for "\if expression": Boolean expected - \echo 'will not print #6-1' -\else - \echo 'will print anyway #6-2' -will print anyway #6-2 -\endif --- test un-matched endif -\endif -\endif: no matching \if --- test un-matched else -\else -\else: no matching \if --- test un-matched elif -\elif -\elif: no matching \if --- test double-else error -\if true -\else -\else -\else: cannot occur after \else -\endif --- test elif out-of-order -\if false -\else -\elif -\elif: cannot occur after \else -\endif --- test if-endif matching in a false branch -\if false - \if false - \echo 'should not print #7-1' - \else - \echo 'should not print #7-2' - \endif - \echo 'should not print #7-3' -\else - \echo 'should print #7-4' -should print #7-4 -\endif --- show that vars and backticks are not expanded when ignoring extra args -\set foo bar -\echo :foo :'foo' :"foo" -bar 'bar' "bar" -\pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" -\pset: extra argument "nosuchcommand" ignored -\pset: extra argument ":foo" ignored -\pset: extra argument ":'foo'" ignored -\pset: extra argument ":"foo"" ignored --- show that vars and backticks are not expanded and commands are ignored --- when in a false if-branch -\set try_to_quit '\\q' -\if false - :try_to_quit - \echo `nosuchcommand` :foo :'foo' :"foo" - \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" - \a - \C arg1 - \c arg1 arg2 arg3 arg4 - \cd arg1 - \conninfo - \copy arg1 arg2 arg3 arg4 arg5 arg6 - \copyright - SELECT 1 as one, 2, 3 \crosstabview - \dt arg1 - \e arg1 arg2 - \ef whole_line - \ev whole_line - \echo arg1 arg2 arg3 arg4 arg5 - \echo arg1 - \encoding arg1 - \errverbose - \f arg1 - \g arg1 - \gx arg1 - \gexec - SELECT 1 AS one \gset - \h - \? - \html - \i arg1 - \ir arg1 - \l arg1 - \lo arg1 arg2 -invalid command \lo - \lo_list - \o arg1 - \p - \password arg1 - \prompt arg1 arg2 - \pset arg1 arg2 - \q - \reset - \s arg1 - \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 - \setenv arg1 arg2 - \sf whole_line - \sv whole_line - \t arg1 - \T arg1 - \timing arg1 - \unset arg1 - \w arg1 - \watch arg1 - \x arg1 - -- \else here is eaten as part of OT_FILEPIPE argument - \w |/no/such/file \else - -- \endif here is eaten as part of whole-line argument - \! whole_line \endif - \z -\else - \echo 'should print #8-1' -should print #8-1 -\endif --- :{?...} defined variable test -\set i 1 -\if :{?i} - \echo '#9-1 ok, variable i is defined' -#9-1 ok, variable i is defined -\else - \echo 'should not print #9-2' -\endif -\if :{?no_such_variable} - \echo 'should not print #10-1' -\else - \echo '#10-2 ok, variable no_such_variable is not defined' -#10-2 ok, variable no_such_variable is not defined -\endif -SELECT :{?i} AS i_is_defined; - i_is_defined --------------- - t -(1 row) - -SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined; - no_such_var_is_not_defined ----------------------------- - t -(1 row) - --- SHOW_CONTEXT -\set SHOW_CONTEXT never -do $$ -begin - raise notice 'foo'; - raise exception 'bar'; -end $$; -NOTICE: foo -ERROR: bar -\set SHOW_CONTEXT errors -do $$ -begin - raise notice 'foo'; - raise exception 'bar'; -end $$; -NOTICE: foo -ERROR: bar -CONTEXT: PL/pgSQL function inline_code_block line 4 at RAISE -\set SHOW_CONTEXT always -do $$ -begin - raise notice 'foo'; - raise exception 'bar'; -end $$; -NOTICE: foo -CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE -ERROR: bar -CONTEXT: PL/pgSQL function inline_code_block line 4 at RAISE --- test printing and clearing the query buffer -SELECT 1; - ?column? ----------- - 1 -(1 row) - -\p -SELECT 1; -SELECT 2 \r -\p -SELECT 1; -SELECT 3 \p -SELECT 3 -UNION SELECT 4 \p -SELECT 3 -UNION SELECT 4 -UNION SELECT 5 -ORDER BY 1; - ?column? ----------- - 3 - 4 - 5 -(3 rows) - -\r -\p -SELECT 3 -UNION SELECT 4 -UNION SELECT 5 -ORDER BY 1; --- tests for special result variables --- working query, 2 rows selected -SELECT 1 AS stuff UNION SELECT 2; - stuff -------- - 1 - 2 -(2 rows) - -\echo 'error:' :ERROR -error: false -\echo 'error code:' :SQLSTATE -error code: 00000 -\echo 'number of rows:' :ROW_COUNT -number of rows: 2 --- syntax error -SELECT 1 UNION; -ERROR: syntax error at or near ";" -LINE 1: SELECT 1 UNION; - ^ -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 42601 -\echo 'number of rows:' :ROW_COUNT -number of rows: 0 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: syntax error at or near ";" -\echo 'last error code:' :LAST_ERROR_SQLSTATE -last error code: 42601 --- empty query -; -\echo 'error:' :ERROR -error: false -\echo 'error code:' :SQLSTATE -error code: 00000 -\echo 'number of rows:' :ROW_COUNT -number of rows: 0 --- must have kept previous values -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: syntax error at or near ";" -\echo 'last error code:' :LAST_ERROR_SQLSTATE -last error code: 42601 --- other query error -DROP TABLE this_table_does_not_exist; -ERROR: table "this_table_does_not_exist" does not exist -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 42P01 -\echo 'number of rows:' :ROW_COUNT -number of rows: 0 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: table "this_table_does_not_exist" does not exist -\echo 'last error code:' :LAST_ERROR_SQLSTATE -last error code: 42P01 --- nondefault verbosity error settings (except verbose, which is too unstable) -\set VERBOSITY terse -SELECT 1 UNION; -ERROR: syntax error at or near ";" at character 15 -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 42601 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: syntax error at or near ";" -\set VERBOSITY sqlstate -SELECT 1/0; -ERROR: 22012 -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 22012 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: division by zero -\set VERBOSITY default --- working \gdesc -SELECT 3 AS three, 4 AS four \gdesc - Column | Type ---------+--------- - three | integer - four | integer -(2 rows) - -\echo 'error:' :ERROR -error: false -\echo 'error code:' :SQLSTATE -error code: 00000 -\echo 'number of rows:' :ROW_COUNT -number of rows: 2 --- \gdesc with an error -SELECT 4 AS \gdesc -ERROR: syntax error at end of input -LINE 1: SELECT 4 AS - ^ -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 42601 -\echo 'number of rows:' :ROW_COUNT -number of rows: 0 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: syntax error at end of input -\echo 'last error code:' :LAST_ERROR_SQLSTATE -last error code: 42601 --- check row count for a cursor-fetched query -\set FETCH_COUNT 10 -select unique2 from tenk1 order by unique2 limit 19; - unique2 ---------- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 -(19 rows) - -\echo 'error:' :ERROR -error: false -\echo 'error code:' :SQLSTATE -error code: 00000 -\echo 'number of rows:' :ROW_COUNT -number of rows: 19 --- cursor-fetched query with an error after the first group -select 1/(15-unique2) from tenk1 order by unique2 limit 19; - ?column? ----------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -ERROR: division by zero -\echo 'error:' :ERROR -error: true -\echo 'error code:' :SQLSTATE -error code: 22012 -\echo 'number of rows:' :ROW_COUNT -number of rows: 0 -\echo 'last error message:' :LAST_ERROR_MESSAGE -last error message: division by zero -\echo 'last error code:' :LAST_ERROR_SQLSTATE -last error code: 22012 -\unset FETCH_COUNT -create schema testpart; -create role regress_partitioning_role; -alter schema testpart owner to regress_partitioning_role; -set role to regress_partitioning_role; --- run test inside own schema and hide other partitions -set search_path to testpart; -create table testtable_apple(logdate date); -create table testtable_orange(logdate date); -create index testtable_apple_index on testtable_apple(logdate); -create index testtable_orange_index on testtable_orange(logdate); -create table testpart_apple(logdate date) partition by range(logdate); -create table testpart_orange(logdate date) partition by range(logdate); -create index testpart_apple_index on testpart_apple(logdate); -create index testpart_orange_index on testpart_orange(logdate); --- only partition related object should be displayed -\dP test*apple* - List of partitioned relations - Schema | Name | Owner | Type | Parent name | Table -----------+----------------------+---------------------------+-------------------+-------------+---------------- - testpart | testpart_apple | regress_partitioning_role | partitioned table | | - testpart | testpart_apple_index | regress_partitioning_role | partitioned index | | testpart_apple -(2 rows) - -\dPt test*apple* - List of partitioned tables - Schema | Name | Owner | Parent name -----------+----------------+---------------------------+------------- - testpart | testpart_apple | regress_partitioning_role | -(1 row) - -\dPi test*apple* - List of partitioned indexes - Schema | Name | Owner | Parent name | Table -----------+----------------------+---------------------------+-------------+---------------- - testpart | testpart_apple_index | regress_partitioning_role | | testpart_apple -(1 row) - -drop table testtable_apple; -drop table testtable_orange; -drop table testpart_apple; -drop table testpart_orange; -create table parent_tab (id int) partition by range (id); -create index parent_index on parent_tab (id); -create table child_0_10 partition of parent_tab - for values from (0) to (10); -create table child_10_20 partition of parent_tab - for values from (10) to (20); -create table child_20_30 partition of parent_tab - for values from (20) to (30); -insert into parent_tab values (generate_series(0,29)); -create table child_30_40 partition of parent_tab -for values from (30) to (40) - partition by range(id); -create table child_30_35 partition of child_30_40 - for values from (30) to (35); -create table child_35_40 partition of child_30_40 - for values from (35) to (40); -insert into parent_tab values (generate_series(30,39)); -\dPt - List of partitioned tables - Schema | Name | Owner -----------+------------+--------------------------- - testpart | parent_tab | regress_partitioning_role -(1 row) - -\dPi - List of partitioned indexes - Schema | Name | Owner | Table -----------+--------------+---------------------------+------------ - testpart | parent_index | regress_partitioning_role | parent_tab -(1 row) - -\dP testpart.* - List of partitioned relations - Schema | Name | Owner | Type | Parent name | Table -----------+--------------------+---------------------------+-------------------+--------------+------------- - testpart | parent_tab | regress_partitioning_role | partitioned table | | - testpart | child_30_40 | regress_partitioning_role | partitioned table | parent_tab | - testpart | parent_index | regress_partitioning_role | partitioned index | | parent_tab - testpart | child_30_40_id_idx | regress_partitioning_role | partitioned index | parent_index | child_30_40 -(4 rows) - -\dP - List of partitioned relations - Schema | Name | Owner | Type | Table -----------+--------------+---------------------------+-------------------+------------ - testpart | parent_tab | regress_partitioning_role | partitioned table | - testpart | parent_index | regress_partitioning_role | partitioned index | parent_tab -(2 rows) - -\dPtn - List of partitioned tables - Schema | Name | Owner | Parent name -----------+-------------+---------------------------+------------- - testpart | parent_tab | regress_partitioning_role | - testpart | child_30_40 | regress_partitioning_role | parent_tab -(2 rows) - -\dPin - List of partitioned indexes - Schema | Name | Owner | Parent name | Table -----------+--------------------+---------------------------+--------------+------------- - testpart | parent_index | regress_partitioning_role | | parent_tab - testpart | child_30_40_id_idx | regress_partitioning_role | parent_index | child_30_40 -(2 rows) - -\dPn - List of partitioned relations - Schema | Name | Owner | Type | Parent name | Table -----------+--------------------+---------------------------+-------------------+--------------+------------- - testpart | parent_tab | regress_partitioning_role | partitioned table | | - testpart | child_30_40 | regress_partitioning_role | partitioned table | parent_tab | - testpart | parent_index | regress_partitioning_role | partitioned index | | parent_tab - testpart | child_30_40_id_idx | regress_partitioning_role | partitioned index | parent_index | child_30_40 -(4 rows) - -\dPn testpart.* - List of partitioned relations - Schema | Name | Owner | Type | Parent name | Table -----------+--------------------+---------------------------+-------------------+--------------+------------- - testpart | parent_tab | regress_partitioning_role | partitioned table | | - testpart | child_30_40 | regress_partitioning_role | partitioned table | parent_tab | - testpart | parent_index | regress_partitioning_role | partitioned index | | parent_tab - testpart | child_30_40_id_idx | regress_partitioning_role | partitioned index | parent_index | child_30_40 -(4 rows) - -drop table parent_tab cascade; -drop schema testpart; -set search_path to default; -set role to default; -drop role regress_partitioning_role; --- \d on toast table (use pg_statistic's toast table, which has a known name) -\d pg_toast.pg_toast_2619 -TOAST table "pg_toast.pg_toast_2619" - Column | Type -------------+--------- - chunk_id | oid - chunk_seq | integer - chunk_data | bytea -Owning table: "pg_catalog.pg_statistic" -Indexes: - "pg_toast_2619_index" PRIMARY KEY, btree (chunk_id, chunk_seq) - --- check printing info about access methods -\dA -List of access methods - Name | Type ---------+------- - brin | Index - btree | Index - gin | Index - gist | Index - hash | Index - heap | Table - heap2 | Table - spgist | Index -(8 rows) - -\dA * -List of access methods - Name | Type ---------+------- - brin | Index - btree | Index - gin | Index - gist | Index - hash | Index - heap | Table - heap2 | Table - spgist | Index -(8 rows) - -\dA h* -List of access methods - Name | Type --------+------- - hash | Index - heap | Table - heap2 | Table -(3 rows) - -\dA foo -List of access methods - Name | Type -------+------ -(0 rows) - -\dA foo bar -List of access methods - Name | Type -------+------ -(0 rows) - -\dA: extra argument "bar" ignored -\dA+ - List of access methods - Name | Type | Handler | Description ---------+-------+----------------------+---------------------------------------- - brin | Index | brinhandler | block range index (BRIN) access method - btree | Index | bthandler | b-tree index access method - gin | Index | ginhandler | GIN index access method - gist | Index | gisthandler | GiST index access method - hash | Index | hashhandler | hash index access method - heap | Table | heap_tableam_handler | heap table access method - heap2 | Table | heap_tableam_handler | - spgist | Index | spghandler | SP-GiST index access method -(8 rows) - -\dA+ * - List of access methods - Name | Type | Handler | Description ---------+-------+----------------------+---------------------------------------- - brin | Index | brinhandler | block range index (BRIN) access method - btree | Index | bthandler | b-tree index access method - gin | Index | ginhandler | GIN index access method - gist | Index | gisthandler | GiST index access method - hash | Index | hashhandler | hash index access method - heap | Table | heap_tableam_handler | heap table access method - heap2 | Table | heap_tableam_handler | - spgist | Index | spghandler | SP-GiST index access method -(8 rows) - -\dA+ h* - List of access methods - Name | Type | Handler | Description --------+-------+----------------------+-------------------------- - hash | Index | hashhandler | hash index access method - heap | Table | heap_tableam_handler | heap table access method - heap2 | Table | heap_tableam_handler | -(3 rows) - -\dA+ foo - List of access methods - Name | Type | Handler | Description -------+------+---------+------------- -(0 rows) - -\dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_multi_ops | no - brin | oid | | oid_minmax_ops | yes -(3 rows) - -\dAf spgist - List of operator families - AM | Operator family | Applicable types ---------+-----------------+------------------ - spgist | box_ops | box - spgist | kd_point_ops | point - spgist | network_ops | inet - spgist | poly_ops | polygon - spgist | quad_point_ops | point - spgist | range_ops | anyrange - spgist | text_ops | text -(7 rows) - -\dAf btree int4 - List of operator families - AM | Operator family | Applicable types --------+-----------------+--------------------------- - btree | integer_ops | smallint, integer, bigint -(1 row) - -\dAo+ btree float_ops - List of operators of operator families - AM | Operator family | Operator | Strategy | Purpose | Sort opfamily --------+-----------------+---------------------------------------+----------+---------+--------------- - btree | float_ops | <(double precision,double precision) | 1 | search | - btree | float_ops | <=(double precision,double precision) | 2 | search | - btree | float_ops | =(double precision,double precision) | 3 | search | - btree | float_ops | >=(double precision,double precision) | 4 | search | - btree | float_ops | >(double precision,double precision) | 5 | search | - btree | float_ops | <(real,real) | 1 | search | - btree | float_ops | <=(real,real) | 2 | search | - btree | float_ops | =(real,real) | 3 | search | - btree | float_ops | >=(real,real) | 4 | search | - btree | float_ops | >(real,real) | 5 | search | - btree | float_ops | <(double precision,real) | 1 | search | - btree | float_ops | <=(double precision,real) | 2 | search | - btree | float_ops | =(double precision,real) | 3 | search | - btree | float_ops | >=(double precision,real) | 4 | search | - btree | float_ops | >(double precision,real) | 5 | search | - btree | float_ops | <(real,double precision) | 1 | search | - btree | float_ops | <=(real,double precision) | 2 | search | - btree | float_ops | =(real,double precision) | 3 | search | - btree | float_ops | >=(real,double precision) | 4 | search | - btree | float_ops | >(real,double precision) | 5 | search | -(20 rows) - -\dAo * pg_catalog.jsonb_path_ops - List of operators of operator families - AM | Operator family | Operator | Strategy | Purpose ------+-----------------+--------------------+----------+--------- - gin | jsonb_path_ops | @>(jsonb,jsonb) | 7 | search - gin | jsonb_path_ops | @?(jsonb,jsonpath) | 15 | search - gin | jsonb_path_ops | @@(jsonb,jsonpath) | 16 | search -(3 rows) - -\dAp+ btree float_ops - List of support functions of operator families - AM | Operator family | Registered left type | Registered right type | Number | Function --------+-----------------+----------------------+-----------------------+--------+------------------------------------------------------------------------------ - btree | float_ops | double precision | double precision | 1 | btfloat8cmp(double precision,double precision) - btree | float_ops | double precision | double precision | 2 | btfloat8sortsupport(internal) - btree | float_ops | double precision | double precision | 3 | in_range(double precision,double precision,double precision,boolean,boolean) - btree | float_ops | real | real | 1 | btfloat4cmp(real,real) - btree | float_ops | real | real | 2 | btfloat4sortsupport(internal) - btree | float_ops | double precision | real | 1 | btfloat84cmp(double precision,real) - btree | float_ops | real | double precision | 1 | btfloat48cmp(real,double precision) - btree | float_ops | real | double precision | 3 | in_range(real,real,double precision,boolean,boolean) -(8 rows) - -\dAp * pg_catalog.uuid_ops - List of support functions of operator families - AM | Operator family | Registered left type | Registered right type | Number | Function --------+-----------------+----------------------+-----------------------+--------+-------------------- - btree | uuid_ops | uuid | uuid | 1 | uuid_cmp - btree | uuid_ops | uuid | uuid | 2 | uuid_sortsupport - btree | uuid_ops | uuid | uuid | 4 | btequalimage - hash | uuid_ops | uuid | uuid | 1 | uuid_hash - hash | uuid_ops | uuid | uuid | 2 | uuid_hash_extended -(5 rows) - --- check \df, \do with argument specifications -\df *sqrt - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+--------------+------------------+---------------------+------ - pg_catalog | dsqrt | double precision | double precision | func - pg_catalog | numeric_sqrt | numeric | numeric | func - pg_catalog | sqrt | double precision | double precision | func - pg_catalog | sqrt | numeric | numeric | func -(4 rows) - -\df *sqrt num* - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+--------------+------------------+---------------------+------ - pg_catalog | numeric_sqrt | numeric | numeric | func - pg_catalog | sqrt | numeric | numeric | func -(2 rows) - -\df int*pl - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+-------------+------------------+---------------------+------ - pg_catalog | int24pl | integer | smallint, integer | func - pg_catalog | int28pl | bigint | smallint, bigint | func - pg_catalog | int2pl | smallint | smallint, smallint | func - pg_catalog | int42pl | integer | integer, smallint | func - pg_catalog | int48pl | bigint | integer, bigint | func - pg_catalog | int4pl | integer | integer, integer | func - pg_catalog | int82pl | bigint | bigint, smallint | func - pg_catalog | int84pl | bigint | bigint, integer | func - pg_catalog | int8pl | bigint | bigint, bigint | func - pg_catalog | interval_pl | interval | interval, interval | func -(10 rows) - -\df int*pl int4 - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+---------+------------------+---------------------+------ - pg_catalog | int42pl | integer | integer, smallint | func - pg_catalog | int48pl | bigint | integer, bigint | func - pg_catalog | int4pl | integer | integer, integer | func -(3 rows) - -\df int*pl * pg_catalog.int8 - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+---------+------------------+---------------------+------ - pg_catalog | int28pl | bigint | smallint, bigint | func - pg_catalog | int48pl | bigint | integer, bigint | func - pg_catalog | int8pl | bigint | bigint, bigint | func -(3 rows) - -\df acl* aclitem[] - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+-------------+------------------+----------------------------------------------------------------------------------------------------+------ - pg_catalog | aclcontains | boolean | aclitem[], aclitem | func - pg_catalog | aclexplode | SETOF record | acl aclitem[], OUT grantor oid, OUT grantee oid, OUT privilege_type text, OUT is_grantable boolean | func - pg_catalog | aclinsert | aclitem[] | aclitem[], aclitem | func - pg_catalog | aclremove | aclitem[] | aclitem[], aclitem | func -(4 rows) - -\df has_database_privilege oid text - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+------------------------+------------------+---------------------+------ - pg_catalog | has_database_privilege | boolean | oid, text | func - pg_catalog | has_database_privilege | boolean | oid, text, text | func -(2 rows) - -\df has_database_privilege oid text - - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+------------------------+------------------+---------------------+------ - pg_catalog | has_database_privilege | boolean | oid, text | func -(1 row) - -\dfa bit* small* - List of functions - Schema | Name | Result data type | Argument data types | Type -------------+---------+------------------+---------------------+------ - pg_catalog | bit_and | smallint | smallint | agg - pg_catalog | bit_or | smallint | smallint | agg - pg_catalog | bit_xor | smallint | smallint | agg -(3 rows) - -\do - pg_catalog.int4 - List of operators - Schema | Name | Left arg type | Right arg type | Result type | Description -------------+------+---------------+----------------+-------------+------------- - pg_catalog | - | | integer | integer | negate -(1 row) - -\do && anyarray * - List of operators - Schema | Name | Left arg type | Right arg type | Result type | Description -------------+------+---------------+----------------+-------------+------------- - pg_catalog | && | anyarray | anyarray | boolean | overlaps -(1 row) - --- AUTOCOMMIT -CREATE TABLE ac_test (a int); -\set AUTOCOMMIT off -INSERT INTO ac_test VALUES (1); -COMMIT; -SELECT * FROM ac_test; - a ---- - 1 -(1 row) - -COMMIT; -INSERT INTO ac_test VALUES (2); -ROLLBACK; -SELECT * FROM ac_test; - a ---- - 1 -(1 row) - -COMMIT; -BEGIN; -INSERT INTO ac_test VALUES (3); -COMMIT; -SELECT * FROM ac_test; - a ---- - 1 - 3 -(2 rows) - -COMMIT; -BEGIN; -INSERT INTO ac_test VALUES (4); -ROLLBACK; -SELECT * FROM ac_test; - a ---- - 1 - 3 -(2 rows) - -COMMIT; -\set AUTOCOMMIT on -DROP TABLE ac_test; -SELECT * FROM ac_test; -- should be gone now -ERROR: relation "ac_test" does not exist -LINE 1: SELECT * FROM ac_test; - ^ --- ON_ERROR_ROLLBACK -\set ON_ERROR_ROLLBACK on -CREATE TABLE oer_test (a int); -BEGIN; -INSERT INTO oer_test VALUES (1); -INSERT INTO oer_test VALUES ('foo'); -ERROR: invalid input syntax for type integer: "foo" -LINE 1: INSERT INTO oer_test VALUES ('foo'); - ^ -INSERT INTO oer_test VALUES (3); -COMMIT; -SELECT * FROM oer_test; - a ---- - 1 - 3 -(2 rows) - -BEGIN; -INSERT INTO oer_test VALUES (4); -ROLLBACK; -SELECT * FROM oer_test; - a ---- - 1 - 3 -(2 rows) - -BEGIN; -INSERT INTO oer_test VALUES (5); -COMMIT AND CHAIN; -INSERT INTO oer_test VALUES (6); -COMMIT; -SELECT * FROM oer_test; - a ---- - 1 - 3 - 5 - 6 -(4 rows) - -DROP TABLE oer_test; -\set ON_ERROR_ROLLBACK off --- ECHO errors -\set ECHO errors -ERROR: relation "notexists" does not exist -LINE 1: SELECT * FROM notexists; - ^ -STATEMENT: SELECT * FROM notexists; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/psql_crosstab.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/psql_crosstab.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/psql_crosstab.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/psql_crosstab.out 2021-10-30 22:04:16 -0600 @@ -1,216 +1,2 @@ --- --- \crosstabview --- -CREATE TABLE ctv_data (v, h, c, i, d) AS -VALUES - ('v1','h2','foo', 3, '2015-04-01'::date), - ('v2','h1','bar', 3, '2015-01-02'), - ('v1','h0','baz', NULL, '2015-07-12'), - ('v0','h4','qux', 4, '2015-07-15'), - ('v0','h4','dbl', -3, '2014-12-15'), - ('v0',NULL,'qux', 5, '2014-07-15'), - ('v1','h2','quux',7, '2015-04-04'); --- make plans more stable -ANALYZE ctv_data; --- running \crosstabview after query uses query in buffer -SELECT v, EXTRACT(year FROM d), count(*) - FROM ctv_data - GROUP BY 1, 2 - ORDER BY 1, 2; - v | extract | count -----+---------+------- - v0 | 2014 | 2 - v0 | 2015 | 1 - v1 | 2015 | 3 - v2 | 2015 | 1 -(4 rows) - --- basic usage with 3 columns - \crosstabview - v | 2014 | 2015 -----+------+------ - v0 | 2 | 1 - v1 | | 3 - v2 | | 1 -(3 rows) - --- ordered months in horizontal header, quoted column name -SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num, - count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1 - \crosstabview v "month name" 4 num - v | Jan | Apr | Jul | Dec -----+-----+-----+-----+----- - v0 | | | 2 | 1 - v1 | | 2 | 1 | - v2 | 1 | | | -(3 rows) - --- ordered months in vertical header, ordered years in horizontal header -SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS """month"" name", - EXTRACT(month FROM d) AS month, - format('sum=%s avg=%s', sum(i), avg(i)::numeric(2,1)) - FROM ctv_data - GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d) -ORDER BY month -\crosstabview """month"" name" year format year - "month" name | 2014 | 2015 ---------------+-----------------+---------------- - Jan | | sum=3 avg=3.0 - Apr | | sum=10 avg=5.0 - Jul | sum=5 avg=5.0 | sum=4 avg=4.0 - Dec | sum=-3 avg=-3.0 | -(4 rows) - --- combine contents vertically into the same cell (V/H duplicates) -SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3 - \crosstabview 1 2 3 - v | h4 | | h0 | h2 | h1 -----+-----+-----+-----+------+----- - v0 | qux+| qux | | | - | dbl | | | | - v1 | | | baz | foo +| - | | | | quux | - v2 | | | | | bar -(3 rows) - --- horizontal ASC order from window function -SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r -FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 - \crosstabview v h c r - v | h0 | h1 | h2 | h4 | -----+-----+-----+------+-----+----- - v0 | | | | qux+| qux - | | | | dbl | - v1 | baz | | foo +| | - | | | quux | | - v2 | | bar | | | -(3 rows) - --- horizontal DESC order from window function -SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r -FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 - \crosstabview v h c r - v | | h4 | h2 | h1 | h0 -----+-----+-----+------+-----+----- - v0 | qux | qux+| | | - | | dbl | | | - v1 | | | foo +| | baz - | | | quux | | - v2 | | | | bar | -(3 rows) - --- horizontal ASC order from window function, NULLs pushed rightmost -SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r -FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 - \crosstabview v h c r - v | h0 | h1 | h2 | h4 | -----+-----+-----+------+-----+----- - v0 | | | | qux+| qux - | | | | dbl | - v1 | baz | | foo +| | - | | | quux | | - v2 | | bar | | | -(3 rows) - --- only null, no column name, 2 columns: error -SELECT null,null \crosstabview -\crosstabview: query must return at least three columns --- only null, no column name, 3 columns: works -SELECT null,null,null \crosstabview - ?column? | -----------+-- - | -(1 row) - --- null display -\pset null '#null#' -SELECT v,h, string_agg(i::text, E'\n') AS i FROM ctv_data -GROUP BY v, h ORDER BY h,v - \crosstabview v h i - v | h0 | h1 | h2 | h4 | #null# -----+--------+----+----+----+-------- - v1 | #null# | | 3 +| | - | | | 7 | | - v2 | | 3 | | | - v0 | | | | 4 +| 5 - | | | | -3 | -(3 rows) - -\pset null '' --- refer to columns by position -SELECT v,h,string_agg(i::text, E'\n'), string_agg(c, E'\n') -FROM ctv_data GROUP BY v, h ORDER BY h,v - \crosstabview 2 1 4 - h | v1 | v2 | v0 -----+------+-----+----- - h0 | baz | | - h1 | | bar | - h2 | foo +| | - | quux | | - h4 | | | qux+ - | | | dbl - | | | qux -(5 rows) - --- refer to columns by positions and names mixed -SELECT v,h, string_agg(i::text, E'\n') AS i, string_agg(c, E'\n') AS c -FROM ctv_data GROUP BY v, h ORDER BY h,v - \crosstabview 1 "h" 4 - v | h0 | h1 | h2 | h4 | -----+-----+-----+------+-----+----- - v1 | baz | | foo +| | - | | | quux | | - v2 | | bar | | | - v0 | | | | qux+| qux - | | | | dbl | -(3 rows) - --- refer to columns by quoted names, check downcasing of unquoted name -SELECT 1 as "22", 2 as b, 3 as "Foo" - \crosstabview "22" B "Foo" - 22 | 2 -----+--- - 1 | 3 -(1 row) - --- error: bad column name -SELECT v,h,c,i FROM ctv_data - \crosstabview v h j -\crosstabview: column name not found: "j" --- error: need to quote name -SELECT 1 as "22", 2 as b, 3 as "Foo" - \crosstabview 1 2 Foo -\crosstabview: column name not found: "foo" --- error: need to not quote name -SELECT 1 as "22", 2 as b, 3 as "Foo" - \crosstabview 1 "B" "Foo" -\crosstabview: column name not found: "B" --- error: bad column number -SELECT v,h,i,c FROM ctv_data - \crosstabview 2 1 5 -\crosstabview: column number 5 is out of range 1..4 --- error: same H and V columns -SELECT v,h,i,c FROM ctv_data - \crosstabview 2 h 4 -\crosstabview: vertical and horizontal headers must be different columns --- error: too many columns -SELECT a,a,1 FROM generate_series(1,3000) AS a - \crosstabview -\crosstabview: maximum number of columns (1600) exceeded --- error: only one column -SELECT 1 \crosstabview -\crosstabview: query must return at least three columns -DROP TABLE ctv_data; --- check error reporting (bug #14476) -CREATE TABLE ctv_data (x int, y int, v text); -INSERT INTO ctv_data SELECT 1, x, '*' || x FROM generate_series(1,10) x; -SELECT * FROM ctv_data \crosstabview - x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ----+----+----+----+----+----+----+----+----+----+----- - 1 | *1 | *2 | *3 | *4 | *5 | *6 | *7 | *8 | *9 | *10 -(1 row) - -INSERT INTO ctv_data VALUES (1, 10, '*'); -- duplicate data to cause error -SELECT * FROM ctv_data \crosstabview -\crosstabview: query result contains multiple data values for row "1", column "10" -DROP TABLE ctv_data; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/amutils.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/amutils.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/amutils.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/amutils.out 2021-10-30 22:04:16 -0600 @@ -1,254 +1,2 @@ --- --- Test index AM property-reporting functions --- -select prop, - pg_indexam_has_property(a.oid, prop) as "AM", - pg_index_has_property('onek_hundred'::regclass, prop) as "Index", - pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as "Column" - from pg_am a, - unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', - 'orderable', 'distance_orderable', 'returnable', - 'search_array', 'search_nulls', - 'clusterable', 'index_scan', 'bitmap_scan', - 'backward_scan', - 'can_order', 'can_unique', 'can_multi_col', - 'can_exclude', 'can_include', - 'bogus']::text[]) - with ordinality as u(prop,ord) - where a.amname = 'btree' - order by ord; - prop | AM | Index | Column ---------------------+----+-------+-------- - asc | | | t - desc | | | f - nulls_first | | | f - nulls_last | | | t - orderable | | | t - distance_orderable | | | f - returnable | | | t - search_array | | | t - search_nulls | | | t - clusterable | | t | - index_scan | | t | - bitmap_scan | | t | - backward_scan | | t | - can_order | t | | - can_unique | t | | - can_multi_col | t | | - can_exclude | t | | - can_include | t | | - bogus | | | -(19 rows) - -select prop, - pg_indexam_has_property(a.oid, prop) as "AM", - pg_index_has_property('gcircleind'::regclass, prop) as "Index", - pg_index_column_has_property('gcircleind'::regclass, 1, prop) as "Column" - from pg_am a, - unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', - 'orderable', 'distance_orderable', 'returnable', - 'search_array', 'search_nulls', - 'clusterable', 'index_scan', 'bitmap_scan', - 'backward_scan', - 'can_order', 'can_unique', 'can_multi_col', - 'can_exclude', 'can_include', - 'bogus']::text[]) - with ordinality as u(prop,ord) - where a.amname = 'gist' - order by ord; - prop | AM | Index | Column ---------------------+----+-------+-------- - asc | | | f - desc | | | f - nulls_first | | | f - nulls_last | | | f - orderable | | | f - distance_orderable | | | t - returnable | | | f - search_array | | | f - search_nulls | | | t - clusterable | | t | - index_scan | | t | - bitmap_scan | | t | - backward_scan | | f | - can_order | f | | - can_unique | f | | - can_multi_col | t | | - can_exclude | t | | - can_include | t | | - bogus | | | -(19 rows) - -select prop, - pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as btree, - pg_index_column_has_property('hash_i4_index'::regclass, 1, prop) as hash, - pg_index_column_has_property('gcircleind'::regclass, 1, prop) as gist, - pg_index_column_has_property('sp_radix_ind'::regclass, 1, prop) as spgist_radix, - pg_index_column_has_property('sp_quad_ind'::regclass, 1, prop) as spgist_quad, - pg_index_column_has_property('botharrayidx'::regclass, 1, prop) as gin, - pg_index_column_has_property('brinidx'::regclass, 1, prop) as brin - from unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', - 'orderable', 'distance_orderable', 'returnable', - 'search_array', 'search_nulls', - 'bogus']::text[]) - with ordinality as u(prop,ord) - order by ord; - prop | btree | hash | gist | spgist_radix | spgist_quad | gin | brin ---------------------+-------+------+------+--------------+-------------+-----+------ - asc | t | f | f | f | f | f | f - desc | f | f | f | f | f | f | f - nulls_first | f | f | f | f | f | f | f - nulls_last | t | f | f | f | f | f | f - orderable | t | f | f | f | f | f | f - distance_orderable | f | f | t | f | t | f | f - returnable | t | f | f | t | t | f | f - search_array | t | f | f | f | f | f | f - search_nulls | t | f | t | t | t | f | t - bogus | | | | | | | -(10 rows) - -select prop, - pg_index_has_property('onek_hundred'::regclass, prop) as btree, - pg_index_has_property('hash_i4_index'::regclass, prop) as hash, - pg_index_has_property('gcircleind'::regclass, prop) as gist, - pg_index_has_property('sp_radix_ind'::regclass, prop) as spgist, - pg_index_has_property('botharrayidx'::regclass, prop) as gin, - pg_index_has_property('brinidx'::regclass, prop) as brin - from unnest(array['clusterable', 'index_scan', 'bitmap_scan', - 'backward_scan', - 'bogus']::text[]) - with ordinality as u(prop,ord) - order by ord; - prop | btree | hash | gist | spgist | gin | brin ----------------+-------+------+------+--------+-----+------ - clusterable | t | f | t | f | f | f - index_scan | t | t | t | t | f | f - bitmap_scan | t | t | t | t | t | t - backward_scan | t | t | f | f | f | f - bogus | | | | | | -(5 rows) - -select amname, prop, pg_indexam_has_property(a.oid, prop) as p - from pg_am a, - unnest(array['can_order', 'can_unique', 'can_multi_col', - 'can_exclude', 'can_include', 'bogus']::text[]) - with ordinality as u(prop,ord) - where amtype = 'i' - order by amname, ord; - amname | prop | p ---------+---------------+--- - brin | can_order | f - brin | can_unique | f - brin | can_multi_col | t - brin | can_exclude | f - brin | can_include | f - brin | bogus | - btree | can_order | t - btree | can_unique | t - btree | can_multi_col | t - btree | can_exclude | t - btree | can_include | t - btree | bogus | - gin | can_order | f - gin | can_unique | f - gin | can_multi_col | t - gin | can_exclude | f - gin | can_include | f - gin | bogus | - gist | can_order | f - gist | can_unique | f - gist | can_multi_col | t - gist | can_exclude | t - gist | can_include | t - gist | bogus | - hash | can_order | f - hash | can_unique | f - hash | can_multi_col | f - hash | can_exclude | t - hash | can_include | f - hash | bogus | - spgist | can_order | f - spgist | can_unique | f - spgist | can_multi_col | f - spgist | can_exclude | t - spgist | can_include | t - spgist | bogus | -(36 rows) - --- --- additional checks for pg_index_column_has_property --- -CREATE TEMP TABLE foo (f1 int, f2 int, f3 int, f4 int); -CREATE INDEX fooindex ON foo (f1 desc, f2 asc, f3 nulls first, f4 nulls last); -select col, prop, pg_index_column_has_property(o, col, prop) - from (values ('fooindex'::regclass)) v1(o), - (values (1,'orderable'),(2,'asc'),(3,'desc'), - (4,'nulls_first'),(5,'nulls_last'), - (6, 'bogus')) v2(idx,prop), - generate_series(1,4) col - order by col, idx; - col | prop | pg_index_column_has_property ------+-------------+------------------------------ - 1 | orderable | t - 1 | asc | f - 1 | desc | t - 1 | nulls_first | t - 1 | nulls_last | f - 1 | bogus | - 2 | orderable | t - 2 | asc | t - 2 | desc | f - 2 | nulls_first | f - 2 | nulls_last | t - 2 | bogus | - 3 | orderable | t - 3 | asc | t - 3 | desc | f - 3 | nulls_first | t - 3 | nulls_last | f - 3 | bogus | - 4 | orderable | t - 4 | asc | t - 4 | desc | f - 4 | nulls_first | f - 4 | nulls_last | t - 4 | bogus | -(24 rows) - -CREATE INDEX foocover ON foo (f1) INCLUDE (f2,f3); -select col, prop, pg_index_column_has_property(o, col, prop) - from (values ('foocover'::regclass)) v1(o), - (values (1,'orderable'),(2,'asc'),(3,'desc'), - (4,'nulls_first'),(5,'nulls_last'), - (6,'distance_orderable'),(7,'returnable'), - (8, 'bogus')) v2(idx,prop), - generate_series(1,3) col - order by col, idx; - col | prop | pg_index_column_has_property ------+--------------------+------------------------------ - 1 | orderable | t - 1 | asc | t - 1 | desc | f - 1 | nulls_first | f - 1 | nulls_last | t - 1 | distance_orderable | f - 1 | returnable | t - 1 | bogus | - 2 | orderable | f - 2 | asc | - 2 | desc | - 2 | nulls_first | - 2 | nulls_last | - 2 | distance_orderable | f - 2 | returnable | t - 2 | bogus | - 3 | orderable | f - 3 | asc | - 3 | desc | - 3 | nulls_first | - 3 | nulls_last | - 3 | distance_orderable | f - 3 | returnable | t - 3 | bogus | -(24 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/stats_ext.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stats_ext.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/stats_ext.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stats_ext.out 2021-10-30 22:04:16 -0600 @@ -1,3173 +1,2 @@ --- Generic extended statistics support --- --- Note: tables for which we check estimated row counts should be created --- with autovacuum_enabled = off, so that we don't have unstable results --- from auto-analyze happening when we didn't expect it. --- --- check the number of estimated/actual rows in the top node -create function check_estimated_rows(text) returns table (estimated int, actual int) -language plpgsql as -$$ -declare - ln text; - tmp text[]; - first_row bool := true; -begin - for ln in - execute format('explain analyze %s', $1) - loop - if first_row then - first_row := false; - tmp := regexp_match(ln, 'rows=(\d*) .* rows=(\d*)'); - return query select tmp[1]::int, tmp[2]::int; - end if; - end loop; -end; -$$; --- Verify failures -CREATE TABLE ext_stats_test (x text, y int, z int); -CREATE STATISTICS tst; -ERROR: syntax error at or near ";" -LINE 1: CREATE STATISTICS tst; - ^ -CREATE STATISTICS tst ON a, b; -ERROR: syntax error at or near ";" -LINE 1: CREATE STATISTICS tst ON a, b; - ^ -CREATE STATISTICS tst FROM sometab; -ERROR: syntax error at or near "FROM" -LINE 1: CREATE STATISTICS tst FROM sometab; - ^ -CREATE STATISTICS tst ON a, b FROM nonexistent; -ERROR: relation "nonexistent" does not exist -CREATE STATISTICS tst ON a, b FROM ext_stats_test; -ERROR: column "a" does not exist -CREATE STATISTICS tst ON x, x, y FROM ext_stats_test; -ERROR: duplicate column name in statistics definition -CREATE STATISTICS tst ON x, x, y, x, x, y, x, x, y FROM ext_stats_test; -ERROR: cannot have more than 8 columns in statistics -CREATE STATISTICS tst ON x, x, y, x, x, (x || 'x'), (y + 1), (x || 'x'), (x || 'x'), (y + 1) FROM ext_stats_test; -ERROR: cannot have more than 8 columns in statistics -CREATE STATISTICS tst ON (x || 'x'), (x || 'x'), (y + 1), (x || 'x'), (x || 'x'), (y + 1), (x || 'x'), (x || 'x'), (y + 1) FROM ext_stats_test; -ERROR: cannot have more than 8 columns in statistics -CREATE STATISTICS tst ON (x || 'x'), (x || 'x'), y FROM ext_stats_test; -ERROR: duplicate expression in statistics definition -CREATE STATISTICS tst (unrecognized) ON x, y FROM ext_stats_test; -ERROR: unrecognized statistics kind "unrecognized" --- incorrect expressions -CREATE STATISTICS tst ON (y) FROM ext_stats_test; -- single column reference -ERROR: extended statistics require at least 2 columns -CREATE STATISTICS tst ON y + z FROM ext_stats_test; -- missing parentheses -ERROR: syntax error at or near "+" -LINE 1: CREATE STATISTICS tst ON y + z FROM ext_stats_test; - ^ -CREATE STATISTICS tst ON (x, y) FROM ext_stats_test; -- tuple expression -ERROR: syntax error at or near "," -LINE 1: CREATE STATISTICS tst ON (x, y) FROM ext_stats_test; - ^ -DROP TABLE ext_stats_test; --- Ensure stats are dropped sanely, and test IF NOT EXISTS while at it -CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); -CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; -COMMENT ON STATISTICS ab1_a_b_stats IS 'new comment'; -CREATE ROLE regress_stats_ext; -SET SESSION AUTHORIZATION regress_stats_ext; -COMMENT ON STATISTICS ab1_a_b_stats IS 'changed comment'; -ERROR: must be owner of statistics object ab1_a_b_stats -DROP STATISTICS ab1_a_b_stats; -ERROR: must be owner of statistics object ab1_a_b_stats -ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new; -ERROR: must be owner of statistics object ab1_a_b_stats -RESET SESSION AUTHORIZATION; -DROP ROLE regress_stats_ext; -CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; -NOTICE: statistics object "ab1_a_b_stats" already exists, skipping -DROP STATISTICS ab1_a_b_stats; -CREATE SCHEMA regress_schema_2; -CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1; --- Let's also verify the pg_get_statisticsobjdef output looks sane. -SELECT pg_get_statisticsobjdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; - pg_get_statisticsobjdef -------------------------------------------------------------------- - CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1 -(1 row) - -DROP STATISTICS regress_schema_2.ab1_a_b_stats; --- Ensure statistics are dropped when columns are -CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1; -CREATE STATISTICS ab1_a_b_c_stats ON a, b, c FROM ab1; -CREATE STATISTICS ab1_b_a_stats ON b, a FROM ab1; -ALTER TABLE ab1 DROP COLUMN a; -\d ab1 - Table "public.ab1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | integer | | | - c | integer | | | -Statistics objects: - "public.ab1_b_c_stats" ON b, c FROM ab1 - --- Ensure statistics are dropped when table is -SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; - stxname ---------------- - ab1_b_c_stats -(1 row) - -DROP TABLE ab1; -SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; - stxname ---------- -(0 rows) - --- Ensure things work sanely with SET STATISTICS 0 -CREATE TABLE ab1 (a INTEGER, b INTEGER); -ALTER TABLE ab1 ALTER a SET STATISTICS 0; -INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; -CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; -ANALYZE ab1; -WARNING: statistics object "public.ab1_a_b_stats" could not be computed for relation "public.ab1" -ALTER TABLE ab1 ALTER a SET STATISTICS -1; --- setting statistics target 0 skips the statistics, without printing any message, so check catalog -ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0; -\d ab1 - Table "public.ab1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Statistics objects: - "public.ab1_a_b_stats" ON a, b FROM ab1; STATISTICS 0 - -ANALYZE ab1; -SELECT stxname, stxdndistinct, stxddependencies, stxdmcv - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxname = 'ab1_a_b_stats' - AND d.stxoid = s.oid; - stxname | stxdndistinct | stxddependencies | stxdmcv ----------------+---------------+------------------+--------- - ab1_a_b_stats | | | -(1 row) - -ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; -\d+ ab1 - Table "public.ab1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - a | integer | | | | plain | | - b | integer | | | | plain | | -Statistics objects: - "public.ab1_a_b_stats" ON a, b FROM ab1 - --- partial analyze doesn't build stats either -ANALYZE ab1 (a); -WARNING: statistics object "public.ab1_a_b_stats" could not be computed for relation "public.ab1" -ANALYZE ab1; -DROP TABLE ab1; -ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0; -ERROR: statistics object "ab1_a_b_stats" does not exist -ALTER STATISTICS IF EXISTS ab1_a_b_stats SET STATISTICS 0; -NOTICE: statistics object "ab1_a_b_stats" does not exist, skipping --- Ensure we can build statistics for tables with inheritance. -CREATE TABLE ab1 (a INTEGER, b INTEGER); -CREATE TABLE ab1c () INHERITS (ab1); -INSERT INTO ab1 VALUES (1,1); -CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; -ANALYZE ab1; -DROP TABLE ab1 CASCADE; -NOTICE: drop cascades to table ab1c --- basic test for statistics on expressions -CREATE TABLE ab1 (a INTEGER, b INTEGER, c TIMESTAMP, d TIMESTAMPTZ); --- expression stats may be built on a single expression column -CREATE STATISTICS ab1_exprstat_1 ON (a+b) FROM ab1; --- with a single expression, we only enable expression statistics -CREATE STATISTICS ab1_exprstat_2 ON (a+b) FROM ab1; -SELECT stxkind FROM pg_statistic_ext WHERE stxname = 'ab1_exprstat_2'; - stxkind ---------- - {e} -(1 row) - --- adding anything to the expression builds all statistics kinds -CREATE STATISTICS ab1_exprstat_3 ON (a+b), a FROM ab1; -SELECT stxkind FROM pg_statistic_ext WHERE stxname = 'ab1_exprstat_3'; - stxkind ------------ - {d,f,m,e} -(1 row) - --- date_trunc on timestamptz is not immutable, but that should not matter -CREATE STATISTICS ab1_exprstat_4 ON date_trunc('day', d) FROM ab1; --- date_trunc on timestamp is immutable -CREATE STATISTICS ab1_exprstat_5 ON date_trunc('day', c) FROM ab1; --- insert some data and run analyze, to test that these cases build properly -INSERT INTO ab1 -SELECT - generate_series(1,10), - generate_series(1,10), - generate_series('2020-10-01'::timestamp, '2020-10-10'::timestamp, interval '1 day'), - generate_series('2020-10-01'::timestamptz, '2020-10-10'::timestamptz, interval '1 day'); -ANALYZE ab1; -DROP TABLE ab1; --- Verify supported object types for extended statistics -CREATE schema tststats; -CREATE TABLE tststats.t (a int, b int, c text); -CREATE INDEX ti ON tststats.t (a, b); -CREATE SEQUENCE tststats.s; -CREATE VIEW tststats.v AS SELECT * FROM tststats.t; -CREATE MATERIALIZED VIEW tststats.mv AS SELECT * FROM tststats.t; -CREATE TYPE tststats.ty AS (a int, b int, c text); -CREATE FOREIGN DATA WRAPPER extstats_dummy_fdw; -CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw; -CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv; -CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); -CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); -CREATE STATISTICS tststats.s1 ON a, b FROM tststats.t; -CREATE STATISTICS tststats.s2 ON a, b FROM tststats.ti; -ERROR: cannot define statistics for relation "ti" -DETAIL: This operation is not supported for indexes. -CREATE STATISTICS tststats.s3 ON a, b FROM tststats.s; -ERROR: cannot define statistics for relation "s" -DETAIL: This operation is not supported for sequences. -CREATE STATISTICS tststats.s4 ON a, b FROM tststats.v; -ERROR: cannot define statistics for relation "v" -DETAIL: This operation is not supported for views. -CREATE STATISTICS tststats.s5 ON a, b FROM tststats.mv; -CREATE STATISTICS tststats.s6 ON a, b FROM tststats.ty; -ERROR: cannot define statistics for relation "ty" -DETAIL: This operation is not supported for composite types. -CREATE STATISTICS tststats.s7 ON a, b FROM tststats.f; -CREATE STATISTICS tststats.s8 ON a, b FROM tststats.pt; -CREATE STATISTICS tststats.s9 ON a, b FROM tststats.pt1; -DO $$ -DECLARE - relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; -BEGIN - EXECUTE 'CREATE STATISTICS tststats.s10 ON a, b FROM ' || relname; -EXCEPTION WHEN wrong_object_type THEN - RAISE NOTICE 'stats on toast table not created'; -END; -$$; -NOTICE: stats on toast table not created -DROP SCHEMA tststats CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table tststats.t -drop cascades to sequence tststats.s -drop cascades to view tststats.v -drop cascades to materialized view tststats.mv -drop cascades to type tststats.ty -drop cascades to foreign table tststats.f -drop cascades to table tststats.pt -DROP FOREIGN DATA WRAPPER extstats_dummy_fdw CASCADE; -NOTICE: drop cascades to server extstats_dummy_srv --- n-distinct tests -CREATE TABLE ndistinct ( - filler1 TEXT, - filler2 NUMERIC, - a INT, - b INT, - filler3 DATE, - c INT, - d INT -) -WITH (autovacuum_enabled = off); --- over-estimates when using only per-column statistics -INSERT INTO ndistinct (a, b, c, filler1) - SELECT i/100, i/100, i/100, cash_words((i/100)::money) - FROM generate_series(1,1000) s(i); -ANALYZE ndistinct; --- Group Aggregate, due to over-estimate of the number of groups -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); - estimated | actual ------------+-------- - 200 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); - estimated | actual ------------+-------- - 200 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (a+1)'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 11 -(1 row) - --- correct command -CREATE STATISTICS s10 ON a, b, c FROM ndistinct; -ANALYZE ndistinct; -SELECT s.stxkind, d.stxdndistinct - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxrelid = 'ndistinct'::regclass - AND d.stxoid = s.oid; - stxkind | stxdndistinct ----------+----------------------------------------------------- - {d,f,m} | {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11} -(1 row) - --- minor improvement, make sure the ctid does not break the matching -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - --- Hash Aggregate, thanks to estimates improved by the statistic -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - --- partial improvement (match on attributes) -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (a+1)'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - --- expressions - no improvement -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 11 | 11 -(1 row) - --- last two plans keep using Group Aggregate, because 'd' is not covered --- by the statistic and while it's NULL-only we assume 200 values for it -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); - estimated | actual ------------+-------- - 200 | 11 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); - estimated | actual ------------+-------- - 200 | 11 -(1 row) - -TRUNCATE TABLE ndistinct; --- under-estimates when using only per-column statistics -INSERT INTO ndistinct (a, b, c, filler1) - SELECT mod(i,13), mod(i,17), mod(i,19), - cash_words(mod(i,23)::int::money) - FROM generate_series(1,1000) s(i); -ANALYZE ndistinct; -SELECT s.stxkind, d.stxdndistinct - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxrelid = 'ndistinct'::regclass - AND d.stxoid = s.oid; - stxkind | stxdndistinct ----------+---------------------------------------------------------- - {d,f,m} | {"3, 4": 221, "3, 6": 247, "4, 6": 323, "3, 4, 6": 1000} -(1 row) - --- correct estimates -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); - estimated | actual ------------+-------- - 323 | 323 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); - estimated | actual ------------+-------- - 200 | 13 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (a+1)'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -DROP STATISTICS s10; -SELECT s.stxkind, d.stxdndistinct - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxrelid = 'ndistinct'::regclass - AND d.stxoid = s.oid; - stxkind | stxdndistinct ----------+--------------- -(0 rows) - --- dropping the statistics results in under-estimates -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); - estimated | actual ------------+-------- - 100 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); - estimated | actual ------------+-------- - 200 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); - estimated | actual ------------+-------- - 200 | 323 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); - estimated | actual ------------+-------- - 200 | 13 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (a+1)'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 100 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - --- ndistinct estimates with statistics on expressions -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 100 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -CREATE STATISTICS s10 (ndistinct) ON (a+1), (b+100), (2*c) FROM ndistinct; -ANALYZE ndistinct; -SELECT s.stxkind, d.stxdndistinct - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxrelid = 'ndistinct'::regclass - AND d.stxoid = s.oid; - stxkind | stxdndistinct ----------+------------------------------------------------------------------- - {d,e} | {"-1, -2": 221, "-1, -3": 247, "-2, -3": 323, "-1, -2, -3": 1000} -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100)'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a+1), (b+100), (2*c)'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (a+1), (b+100)'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -DROP STATISTICS s10; --- a mix of attributes and expressions -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 100 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (2*c)'); - estimated | actual ------------+-------- - 100 | 247 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (2*c)'); - estimated | actual ------------+-------- - 100 | 1000 -(1 row) - -CREATE STATISTICS s10 (ndistinct) ON a, b, (2*c) FROM ndistinct; -ANALYZE ndistinct; -SELECT s.stxkind, d.stxdndistinct - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxrelid = 'ndistinct'::regclass - AND d.stxoid = s.oid; - stxkind | stxdndistinct ----------+------------------------------------------------------------- - {d,e} | {"3, 4": 221, "3, -1": 247, "4, -1": 323, "3, 4, -1": 1000} -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 221 | 221 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (2*c)'); - estimated | actual ------------+-------- - 247 | 247 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (2*c)'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -DROP STATISTICS s10; --- combination of multiple ndistinct statistics, with/without expressions -TRUNCATE ndistinct; --- two mostly independent groups of columns -INSERT INTO ndistinct (a, b, c, d) - SELECT mod(i,3), mod(i,9), mod(i,5), mod(i,20) - FROM generate_series(1,1000) s(i); -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 27 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 27 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 27 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 27 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 100 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 100 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - --- basic statistics on both attributes (no expressions) -CREATE STATISTICS s11 (ndistinct) ON a, b FROM ndistinct; -CREATE STATISTICS s12 (ndistinct) ON c, d FROM ndistinct; -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - --- replace the second statistics by statistics on expressions -DROP STATISTICS s12; -CREATE STATISTICS s12 (ndistinct) ON (c * 10), (d - 1) FROM ndistinct; -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - --- replace the second statistics by statistics on both attributes and expressions -DROP STATISTICS s12; -CREATE STATISTICS s12 (ndistinct) ON c, d, (c * 10), (d - 1) FROM ndistinct; -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - --- replace the other statistics by statistics on both attributes and expressions -DROP STATISTICS s11; -CREATE STATISTICS s11 (ndistinct) ON a, b, (a*5), (b+1) FROM ndistinct; -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - --- replace statistics by somewhat overlapping ones (this expected to get worse estimate --- because the first statistics shall be applied to 3 columns, and the second one can't --- be really applied) -DROP STATISTICS s11; -DROP STATISTICS s12; -CREATE STATISTICS s11 (ndistinct) ON a, b, (a*5), (b+1) FROM ndistinct; -CREATE STATISTICS s12 (ndistinct) ON a, (b+1), (c * 10) FROM ndistinct; -ANALYZE ndistinct; -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), b'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1)'); - estimated | actual ------------+-------- - 9 | 9 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY (a*5), (b+1), c'); - estimated | actual ------------+-------- - 45 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, (c*10)'); - estimated | actual ------------+-------- - 100 | 45 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, (b+1), c, (d - 1)'); - estimated | actual ------------+-------- - 100 | 180 -(1 row) - -DROP STATISTICS s11; -DROP STATISTICS s12; --- functional dependencies tests -CREATE TABLE functional_dependencies ( - filler1 TEXT, - filler2 NUMERIC, - a INT, - b TEXT, - filler3 DATE, - c INT, - d TEXT -) -WITH (autovacuum_enabled = off); -CREATE INDEX fdeps_ab_idx ON functional_dependencies (a, b); -CREATE INDEX fdeps_abc_idx ON functional_dependencies (a, b, c); --- random data (no functional dependencies) -INSERT INTO functional_dependencies (a, b, c, filler1) - SELECT mod(i, 5), mod(i, 7), mod(i, 11), i FROM generate_series(1,1000) s(i); -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 29 | 29 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 3 | 3 -(1 row) - --- create statistics -CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 29 | 29 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 3 | 3 -(1 row) - --- a => b, a => c, b => c -TRUNCATE functional_dependencies; -DROP STATISTICS func_deps_stat; --- now do the same thing, but with expressions -INSERT INTO functional_dependencies (a, b, c, filler1) - SELECT i, i, i, i FROM generate_series(1,5000) s(i); -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE mod(a, 11) = 1 AND mod(b::int, 13) = 1'); - estimated | actual ------------+-------- - 1 | 35 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE mod(a, 11) = 1 AND mod(b::int, 13) = 1 AND mod(c, 7) = 1'); - estimated | actual ------------+-------- - 1 | 5 -(1 row) - --- create statistics -CREATE STATISTICS func_deps_stat (dependencies) ON (mod(a,11)), (mod(b::int, 13)), (mod(c, 7)) FROM functional_dependencies; -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE mod(a, 11) = 1 AND mod(b::int, 13) = 1'); - estimated | actual ------------+-------- - 35 | 35 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE mod(a, 11) = 1 AND mod(b::int, 13) = 1 AND mod(c, 7) = 1'); - estimated | actual ------------+-------- - 5 | 5 -(1 row) - --- a => b, a => c, b => c -TRUNCATE functional_dependencies; -DROP STATISTICS func_deps_stat; -INSERT INTO functional_dependencies (a, b, c, filler1) - SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i); -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - --- IN -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ''1'''); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 4 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b = ''1'''); - estimated | actual ------------+-------- - 4 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 26, 51, 76) AND b IN (''1'', ''26'') AND c = 1'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 26, 51, 76) AND b IN (''1'', ''26'') AND c IN (1)'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 26, 27, 51, 52, 76, 77) AND b IN (''1'', ''2'', ''26'', ''27'') AND c IN (1, 2)'); - estimated | actual ------------+-------- - 3 | 400 -(1 row) - --- OR clauses referencing the same attribute -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND b = ''1'''); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND (b = ''1'' OR b = ''2'')'); - estimated | actual ------------+-------- - 4 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 2 OR a = 51 OR a = 52) AND (b = ''1'' OR b = ''2'')'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - --- OR clauses referencing different attributes -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR b = ''1'') AND b = ''1'''); - estimated | actual ------------+-------- - 3 | 100 -(1 row) - --- ANY -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 51]) AND b = ''1'''); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 51]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 4 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 26, 51, 76]) AND b = ANY (ARRAY[''1'', ''26'']) AND c = 1'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 26, 51, 76]) AND b = ANY (ARRAY[''1'', ''26'']) AND c = ANY (ARRAY[1])'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 2, 26, 27, 51, 52, 76, 77]) AND b = ANY (ARRAY[''1'', ''2'', ''26'', ''27'']) AND c = ANY (ARRAY[1, 2])'); - estimated | actual ------------+-------- - 3 | 400 -(1 row) - --- ANY with inequalities should not benefit from functional dependencies -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a < ANY (ARRAY[1, 51]) AND b > ''1'''); - estimated | actual ------------+-------- - 2472 | 2400 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a >= ANY (ARRAY[1, 51]) AND b <= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1441 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a <= ANY (ARRAY[1, 2, 51, 52]) AND b >= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 3909 | 2550 -(1 row) - --- ALL (should not benefit from functional dependencies) -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ALL (ARRAY[''1''])'); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - --- create statistics -CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; -ANALYZE functional_dependencies; --- print the detected dependencies -SELECT dependencies FROM pg_stats_ext WHERE statistics_name = 'func_deps_stat'; - dependencies ------------------------------------------------------------------------------------------------------------- - {"3 => 4": 1.000000, "3 => 6": 1.000000, "4 => 6": 1.000000, "3, 4 => 6": 1.000000, "3, 6 => 4": 1.000000} -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - --- IN -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 26, 51, 76) AND b IN (''1'', ''26'') AND c = 1'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 26, 51, 76) AND b IN (''1'', ''26'') AND c IN (1)'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 26, 27, 51, 52, 76, 77) AND b IN (''1'', ''2'', ''26'', ''27'') AND c IN (1, 2)'); - estimated | actual ------------+-------- - 400 | 400 -(1 row) - --- OR clauses referencing the same attribute -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND b = ''1'''); - estimated | actual ------------+-------- - 99 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND (b = ''1'' OR b = ''2'')'); - estimated | actual ------------+-------- - 99 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 2 OR a = 51 OR a = 52) AND (b = ''1'' OR b = ''2'')'); - estimated | actual ------------+-------- - 197 | 200 -(1 row) - --- OR clauses referencing different attributes are incompatible -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR b = ''1'') AND b = ''1'''); - estimated | actual ------------+-------- - 3 | 100 -(1 row) - --- ANY -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 51]) AND b = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 51]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 26, 51, 76]) AND b = ANY (ARRAY[''1'', ''26'']) AND c = 1'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 26, 51, 76]) AND b = ANY (ARRAY[''1'', ''26'']) AND c = ANY (ARRAY[1])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = ANY (ARRAY[1, 2, 26, 27, 51, 52, 76, 77]) AND b = ANY (ARRAY[''1'', ''2'', ''26'', ''27'']) AND c = ANY (ARRAY[1, 2])'); - estimated | actual ------------+-------- - 400 | 400 -(1 row) - --- ANY with inequalities should not benefit from functional dependencies -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a < ANY (ARRAY[1, 51]) AND b > ''1'''); - estimated | actual ------------+-------- - 2472 | 2400 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a >= ANY (ARRAY[1, 51]) AND b <= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1441 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a <= ANY (ARRAY[1, 2, 51, 52]) AND b >= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 3909 | 2550 -(1 row) - --- ALL (should not benefit from functional dependencies) -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ALL (ARRAY[''1''])'); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 51) AND b = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a IN (1, 2, 51, 52) AND b = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - --- changing the type of column c causes all its stats to be dropped, reverting --- to default estimates without any statistics, i.e. 0.5% selectivity for each --- condition -ALTER TABLE functional_dependencies ALTER COLUMN c TYPE numeric; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -ANALYZE functional_dependencies; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -DROP STATISTICS func_deps_stat; --- now try functional dependencies with expressions -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = 2 AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = 2 AND upper(b) = ''1'' AND (c + 1) = 2'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - --- IN -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 52, 102, 152) AND upper(b) IN (''1'', ''26'') AND (c + 1) = 2'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 52, 102, 152) AND upper(b) IN (''1'', ''26'') AND (c + 1) IN (2)'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 52, 54, 102, 104, 152, 154) AND upper(b) IN (''1'', ''2'', ''26'', ''27'') AND (c + 1) IN (2, 3)'); - estimated | actual ------------+-------- - 1 | 400 -(1 row) - --- OR clauses referencing the same attribute -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 4 OR (a * 2) = 102 OR (a * 2) = 104) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - --- OR clauses referencing different attributes -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR upper(b) = ''1'') AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - --- ANY -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 102]) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 102]) AND upper(b) = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 4, 102, 104]) AND upper(b) = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 52, 102, 152]) AND upper(b) = ANY (ARRAY[''1'', ''26'']) AND (c + 1) = 2'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 52, 102, 152]) AND upper(b) = ANY (ARRAY[''1'', ''26'']) AND (c + 1) = ANY (ARRAY[2])'); - estimated | actual ------------+-------- - 1 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 4, 52, 54, 102, 104, 152, 154]) AND upper(b) = ANY (ARRAY[''1'', ''2'', ''26'', ''27'']) AND (c + 1) = ANY (ARRAY[2, 3])'); - estimated | actual ------------+-------- - 1 | 400 -(1 row) - --- ANY with inequalities should not benefit from functional dependencies --- the estimates however improve thanks to having expression statistics -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) < ANY (ARRAY[2, 102]) AND upper(b) > ''1'''); - estimated | actual ------------+-------- - 926 | 2400 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) >= ANY (ARRAY[2, 102]) AND upper(b) <= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1543 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) <= ANY (ARRAY[2, 4, 102, 104]) AND upper(b) >= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 2229 | 2550 -(1 row) - --- ALL (should not benefit from functional dependencies) -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ALL (ARRAY[''1''])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - --- create statistics on expressions -CREATE STATISTICS func_deps_stat (dependencies) ON (a * 2), upper(b), (c + 1) FROM functional_dependencies; -ANALYZE functional_dependencies; --- print the detected dependencies -SELECT dependencies FROM pg_stats_ext WHERE statistics_name = 'func_deps_stat'; - dependencies ------------------------------------------------------------------------------------------------------------------------- - {"-1 => -2": 1.000000, "-1 => -3": 1.000000, "-2 => -3": 1.000000, "-1, -2 => -3": 1.000000, "-1, -3 => -2": 1.000000} -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = 2 AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = 2 AND upper(b) = ''1'' AND (c + 1) = 2'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - --- IN -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) IN (''1'', ''2'')'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 52, 102, 152) AND upper(b) IN (''1'', ''26'') AND (c + 1) = 2'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 52, 102, 152) AND upper(b) IN (''1'', ''26'') AND (c + 1) IN (2)'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 52, 54, 102, 104, 152, 154) AND upper(b) IN (''1'', ''2'', ''26'', ''27'') AND (c + 1) IN (2, 3)'); - estimated | actual ------------+-------- - 400 | 400 -(1 row) - --- OR clauses referencing the same attribute -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 99 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); - estimated | actual ------------+-------- - 99 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 4 OR (a * 2) = 102 OR (a * 2) = 104) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); - estimated | actual ------------+-------- - 197 | 200 -(1 row) - --- OR clauses referencing different attributes -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR upper(b) = ''1'') AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 3 | 100 -(1 row) - --- ANY -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 102]) AND upper(b) = ''1'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 102]) AND upper(b) = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 4, 102, 104]) AND upper(b) = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 52, 102, 152]) AND upper(b) = ANY (ARRAY[''1'', ''26'']) AND (c + 1) = 2'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 52, 102, 152]) AND upper(b) = ANY (ARRAY[''1'', ''26'']) AND (c + 1) = ANY (ARRAY[2])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) = ANY (ARRAY[2, 4, 52, 54, 102, 104, 152, 154]) AND upper(b) = ANY (ARRAY[''1'', ''2'', ''26'', ''27'']) AND (c + 1) = ANY (ARRAY[2, 3])'); - estimated | actual ------------+-------- - 400 | 400 -(1 row) - --- ANY with inequalities should not benefit from functional dependencies --- the estimates however improve thanks to having expression statistics -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) < ANY (ARRAY[2, 102]) AND upper(b) > ''1'''); - estimated | actual ------------+-------- - 2472 | 2400 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) >= ANY (ARRAY[2, 102]) AND upper(b) <= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1441 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) <= ANY (ARRAY[2, 4, 102, 104]) AND upper(b) >= ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 3909 | 2550 -(1 row) - --- ALL (should not benefit from functional dependencies) -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ALL (ARRAY[''1''])'); - estimated | actual ------------+-------- - 2 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 102) AND upper(b) = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a * 2) IN (2, 4, 102, 104) AND upper(b) = ALL (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - --- check the ability to use multiple functional dependencies -CREATE TABLE functional_dependencies_multi ( - a INTEGER, - b INTEGER, - c INTEGER, - d INTEGER -) -WITH (autovacuum_enabled = off); -INSERT INTO functional_dependencies_multi (a, b, c, d) - SELECT - mod(i,7), - mod(i,7), - mod(i,11), - mod(i,11) - FROM generate_series(1,5000) s(i); -ANALYZE functional_dependencies_multi; --- estimates without any functional dependencies -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0'); - estimated | actual ------------+-------- - 102 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND 0 = b'); - estimated | actual ------------+-------- - 102 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE c = 0 AND d = 0'); - estimated | actual ------------+-------- - 41 | 454 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0 AND c = 0 AND d = 0'); - estimated | actual ------------+-------- - 1 | 64 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND b = 0 AND 0 = c AND d = 0'); - estimated | actual ------------+-------- - 1 | 64 -(1 row) - --- create separate functional dependencies -CREATE STATISTICS functional_dependencies_multi_1 (dependencies) ON a, b FROM functional_dependencies_multi; -CREATE STATISTICS functional_dependencies_multi_2 (dependencies) ON c, d FROM functional_dependencies_multi; -ANALYZE functional_dependencies_multi; -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0'); - estimated | actual ------------+-------- - 714 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND 0 = b'); - estimated | actual ------------+-------- - 714 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE c = 0 AND d = 0'); - estimated | actual ------------+-------- - 454 | 454 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0 AND c = 0 AND d = 0'); - estimated | actual ------------+-------- - 65 | 64 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND b = 0 AND 0 = c AND d = 0'); - estimated | actual ------------+-------- - 65 | 64 -(1 row) - -DROP TABLE functional_dependencies_multi; --- MCV lists -CREATE TABLE mcv_lists ( - filler1 TEXT, - filler2 NUMERIC, - a INT, - b VARCHAR, - filler3 DATE, - c INT, - d TEXT -) -WITH (autovacuum_enabled = off); --- random data (no MCV list) -INSERT INTO mcv_lists (a, b, c, filler1) - SELECT mod(i,37), mod(i,41), mod(i,43), mod(i,47) FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 3 | 4 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 1 | 1 -(1 row) - --- create statistics -CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 3 | 4 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 1 | 1 -(1 row) - -TRUNCATE mcv_lists; -DROP STATISTICS mcv_lists_stats; --- random data (no MCV list), but with expression -INSERT INTO mcv_lists (a, b, c, filler1) - SELECT i, i, i, i FROM generate_series(1,1000) s(i); -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,7) = 1 AND mod(b::int,11) = 1'); - estimated | actual ------------+-------- - 1 | 13 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,7) = 1 AND mod(b::int,11) = 1 AND mod(c,13) = 1'); - estimated | actual ------------+-------- - 1 | 1 -(1 row) - --- create statistics -CREATE STATISTICS mcv_lists_stats (mcv) ON (mod(a,7)), (mod(b::int,11)), (mod(c,13)) FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,7) = 1 AND mod(b::int,11) = 1'); - estimated | actual ------------+-------- - 13 | 13 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,7) = 1 AND mod(b::int,11) = 1 AND mod(c,13) = 1'); - estimated | actual ------------+-------- - 1 | 1 -(1 row) - --- 100 distinct combinations, all in the MCV list -TRUNCATE mcv_lists; -DROP STATISTICS mcv_lists_stats; -INSERT INTO mcv_lists (a, b, c, filler1) - SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 = a AND ''1'' = b'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 > a AND ''1'' > b'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 0 >= a AND ''0'' >= b'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND ''1'' > b AND 5 > c'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 4 >= a AND ''0'' >= b AND 4 >= c'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''1'' OR c = 1'); - estimated | actual ------------+-------- - 343 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''1'' OR c = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 343 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (1, 2, 51, 52) AND b IN ( ''1'', ''2'')'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (1, 2, 51, 52, NULL) AND b IN ( ''1'', ''2'', NULL)'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = ANY (ARRAY[1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = ANY (ARRAY[NULL, 1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2'', NULL])'); - estimated | actual ------------+-------- - 8 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= ANY (ARRAY[1, 2, 3]) AND b IN (''1'', ''2'', ''3'')'); - estimated | actual ------------+-------- - 26 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= ANY (ARRAY[1, NULL, 2, 3]) AND b IN (''1'', ''2'', NULL, ''3'')'); - estimated | actual ------------+-------- - 26 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND c > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 10 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND c > ANY (ARRAY[1, 2, 3, NULL])'); - estimated | actual ------------+-------- - 10 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND b IN (''1'', ''2'', ''3'') AND c > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND b IN (''1'', ''2'', NULL, ''3'') AND c > ANY (ARRAY[1, 2, NULL, 3])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - --- create statistics -CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 = a AND ''1'' = b'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 > a AND ''1'' > b'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 0 >= a AND ''0'' >= b'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND ''1'' > b AND 5 > c'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 4 >= a AND ''0'' >= b AND 4 >= c'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''1'' OR c = 1'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''1'' OR c = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''1'' OR c = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (1, 2, 51, 52) AND b IN ( ''1'', ''2'')'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (1, 2, 51, 52, NULL) AND b IN ( ''1'', ''2'', NULL)'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = ANY (ARRAY[1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2''])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = ANY (ARRAY[NULL, 1, 2, 51, 52]) AND b = ANY (ARRAY[''1'', ''2'', NULL])'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= ANY (ARRAY[1, 2, 3]) AND b IN (''1'', ''2'', ''3'')'); - estimated | actual ------------+-------- - 150 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= ANY (ARRAY[1, NULL, 2, 3]) AND b IN (''1'', ''2'', NULL, ''3'')'); - estimated | actual ------------+-------- - 150 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND c > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND c > ANY (ARRAY[1, 2, 3, NULL])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND b IN (''1'', ''2'', ''3'') AND c > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY[4, 5]) AND b IN (''1'', ''2'', NULL, ''3'') AND c > ANY (ARRAY[1, 2, NULL, 3])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - --- check change of unrelated column type does not reset the MCV statistics -ALTER TABLE mcv_lists ALTER COLUMN d TYPE VARCHAR(64); -SELECT d.stxdmcv IS NOT NULL - FROM pg_statistic_ext s, pg_statistic_ext_data d - WHERE s.stxname = 'mcv_lists_stats' - AND d.stxoid = s.oid; - ?column? ----------- - t -(1 row) - --- check change of column type resets the MCV statistics -ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - --- 100 distinct combinations, all in the MCV list, but with expressions -TRUNCATE mcv_lists; -DROP STATISTICS mcv_lists_stats; -INSERT INTO mcv_lists (a, b, c, filler1) - SELECT i, i, i, i FROM generate_series(1,1000) s(i); -ANALYZE mcv_lists; --- without any stats on the expressions, we have to use default selectivities, which --- is why the estimates here are different from the pre-computed case above -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 = mod(a,20) AND 1 = mod(b::int,10)'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < 1 AND mod(b::int,10) < 1'); - estimated | actual ------------+-------- - 111 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 > mod(a,20) AND 1 > mod(b::int,10)'); - estimated | actual ------------+-------- - 111 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1 AND mod(c,5) = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 OR mod(b::int,10) = 1 OR mod(c,25) = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 15 | 120 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) IN (1, 2, 51, 52, NULL) AND mod(b::int,10) IN ( 1, 2, NULL)'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = ANY (ARRAY[1, 2, 51, 52]) AND mod(b::int,10) = ANY (ARRAY[1, 2])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) <= ANY (ARRAY[1, NULL, 2, 3]) AND mod(b::int,10) IN (1, 2, NULL, 3)'); - estimated | actual ------------+-------- - 11 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < ALL (ARRAY[4, 5]) AND mod(b::int,10) IN (1, 2, 3) AND mod(c,5) > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - --- create statistics with expressions only (we create three separate stats, in order not to build more complex extended stats) -CREATE STATISTICS mcv_lists_stats_1 ON (mod(a,20)) FROM mcv_lists; -CREATE STATISTICS mcv_lists_stats_2 ON (mod(b::int,10)) FROM mcv_lists; -CREATE STATISTICS mcv_lists_stats_3 ON (mod(c,5)) FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1'); - estimated | actual ------------+-------- - 5 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 = mod(a,20) AND 1 = mod(b::int,10)'); - estimated | actual ------------+-------- - 5 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < 1 AND mod(b::int,10) < 1'); - estimated | actual ------------+-------- - 5 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 > mod(a,20) AND 1 > mod(b::int,10)'); - estimated | actual ------------+-------- - 5 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1 AND mod(c,5) = 1'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 OR mod(b::int,10) = 1 OR mod(c,25) = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 149 | 120 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) IN (1, 2, 51, 52, NULL) AND mod(b::int,10) IN ( 1, 2, NULL)'); - estimated | actual ------------+-------- - 20 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = ANY (ARRAY[1, 2, 51, 52]) AND mod(b::int,10) = ANY (ARRAY[1, 2])'); - estimated | actual ------------+-------- - 20 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) <= ANY (ARRAY[1, NULL, 2, 3]) AND mod(b::int,10) IN (1, 2, NULL, 3)'); - estimated | actual ------------+-------- - 116 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < ALL (ARRAY[4, 5]) AND mod(b::int,10) IN (1, 2, 3) AND mod(c,5) > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 12 | 100 -(1 row) - -DROP STATISTICS mcv_lists_stats_1; -DROP STATISTICS mcv_lists_stats_2; -DROP STATISTICS mcv_lists_stats_3; --- create statistics with both MCV and expressions -CREATE STATISTICS mcv_lists_stats (mcv) ON (mod(a,20)), (mod(b::int,10)), (mod(c,5)) FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 = mod(a,20) AND 1 = mod(b::int,10)'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < 1 AND mod(b::int,10) < 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE 1 > mod(a,20) AND 1 > mod(b::int,10)'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 AND mod(b::int,10) = 1 AND mod(c,5) = 1'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 OR mod(b::int,10) = 1 OR mod(c,25) = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 105 | 120 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) IN (1, 2, 51, 52, NULL) AND mod(b::int,10) IN ( 1, 2, NULL)'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = ANY (ARRAY[1, 2, 51, 52]) AND mod(b::int,10) = ANY (ARRAY[1, 2])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) <= ANY (ARRAY[1, NULL, 2, 3]) AND mod(b::int,10) IN (1, 2, NULL, 3)'); - estimated | actual ------------+-------- - 150 | 150 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) < ALL (ARRAY[4, 5]) AND mod(b::int,10) IN (1, 2, 3) AND mod(c,5) > ANY (ARRAY[1, 2, 3])'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - --- we can't use the statistic for OR clauses that are not fully covered (missing 'd' attribute) -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE mod(a,20) = 1 OR mod(b::int,10) = 1 OR mod(c,5) = 1 OR d IS NOT NULL'); - estimated | actual ------------+-------- - 200 | 200 -(1 row) - --- 100 distinct combinations with NULL values, all in the MCV list -TRUNCATE mcv_lists; -DROP STATISTICS mcv_lists_stats; -INSERT INTO mcv_lists (a, b, c, filler1) - SELECT - (CASE WHEN mod(i,100) = 1 THEN NULL ELSE mod(i,100) END), - (CASE WHEN mod(i,50) = 1 THEN NULL ELSE mod(i,50) END), - (CASE WHEN mod(i,25) = 1 THEN NULL ELSE mod(i,25) END), - i - FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NOT NULL'); - estimated | actual ------------+-------- - 49 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NOT NULL AND b IS NULL AND c IS NOT NULL'); - estimated | actual ------------+-------- - 95 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (0, 1) AND b IN (''0'', ''1'')'); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - --- create statistics -CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NOT NULL'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NOT NULL AND b IS NULL AND c IS NOT NULL'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (0, 1) AND b IN (''0'', ''1'')'); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - --- test pg_mcv_list_items with a very simple (single item) MCV list -TRUNCATE mcv_lists; -INSERT INTO mcv_lists (a, b, c) SELECT 1, 2, 3 FROM generate_series(1,1000) s(i); -ANALYZE mcv_lists; -SELECT m.* - FROM pg_statistic_ext s, pg_statistic_ext_data d, - pg_mcv_list_items(d.stxdmcv) m - WHERE s.stxname = 'mcv_lists_stats' - AND d.stxoid = s.oid; - index | values | nulls | frequency | base_frequency --------+---------+---------+-----------+---------------- - 0 | {1,2,3} | {f,f,f} | 1 | 1 -(1 row) - --- 2 distinct combinations with NULL values, all in the MCV list -TRUNCATE mcv_lists; -DROP STATISTICS mcv_lists_stats; -INSERT INTO mcv_lists (a, b, c, d) - SELECT - NULL, -- always NULL - (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END), - (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 0 END), - (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END) - FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); - estimated | actual ------------+-------- - 3750 | 2500 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''x'' OR d = ''x'''); - estimated | actual ------------+-------- - 3750 | 2500 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND (b = ''x'' OR d = ''x'')'); - estimated | actual ------------+-------- - 3750 | 2500 -(1 row) - --- create statistics -CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, d FROM mcv_lists; -ANALYZE mcv_lists; --- test pg_mcv_list_items with MCV list containing variable-length data and NULLs -SELECT m.* - FROM pg_statistic_ext s, pg_statistic_ext_data d, - pg_mcv_list_items(d.stxdmcv) m - WHERE s.stxname = 'mcv_lists_stats' - AND d.stxoid = s.oid; - index | values | nulls | frequency | base_frequency --------+------------------+---------+-----------+---------------- - 0 | {NULL,x,x} | {t,f,f} | 0.5 | 0.25 - 1 | {NULL,NULL,NULL} | {t,t,t} | 0.5 | 0.25 -(2 rows) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); - estimated | actual ------------+-------- - 2500 | 2500 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 OR b = ''x'' OR d = ''x'''); - estimated | actual ------------+-------- - 2500 | 2500 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND (b = ''x'' OR d = ''x'')'); - estimated | actual ------------+-------- - 2500 | 2500 -(1 row) - --- mcv with pass-by-ref fixlen types, e.g. uuid -CREATE TABLE mcv_lists_uuid ( - a UUID, - b UUID, - c UUID -) -WITH (autovacuum_enabled = off); -INSERT INTO mcv_lists_uuid (a, b, c) - SELECT - md5(mod(i,100)::text)::uuid, - md5(mod(i,50)::text)::uuid, - md5(mod(i,25)::text)::uuid - FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists_uuid; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND c = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'''); - estimated | actual ------------+-------- - 1 | 50 -(1 row) - -CREATE STATISTICS mcv_lists_uuid_stats (mcv) ON a, b, c - FROM mcv_lists_uuid; -ANALYZE mcv_lists_uuid; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND c = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'''); - estimated | actual ------------+-------- - 50 | 50 -(1 row) - -DROP TABLE mcv_lists_uuid; --- mcv with arrays -CREATE TABLE mcv_lists_arrays ( - a TEXT[], - b NUMERIC[], - c INT[] -) -WITH (autovacuum_enabled = off); -INSERT INTO mcv_lists_arrays (a, b, c) - SELECT - ARRAY[md5((i/100)::text), md5((i/100-1)::text), md5((i/100+1)::text)], - ARRAY[(i/100-1)::numeric/1000, (i/100)::numeric/1000, (i/100+1)::numeric/1000], - ARRAY[(i/100-1), i/100, (i/100+1)] - FROM generate_series(1,5000) s(i); -CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c - FROM mcv_lists_arrays; -ANALYZE mcv_lists_arrays; --- mcv with bool -CREATE TABLE mcv_lists_bool ( - a BOOL, - b BOOL, - c BOOL -) -WITH (autovacuum_enabled = off); -INSERT INTO mcv_lists_bool (a, b, c) - SELECT - (mod(i,2) = 0), (mod(i,4) = 0), (mod(i,8) = 0) - FROM generate_series(1,10000) s(i); -ANALYZE mcv_lists_bool; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); - estimated | actual ------------+-------- - 156 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); - estimated | actual ------------+-------- - 156 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); - estimated | actual ------------+-------- - 469 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); - estimated | actual ------------+-------- - 1094 | 0 -(1 row) - -CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c - FROM mcv_lists_bool; -ANALYZE mcv_lists_bool; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); - estimated | actual ------------+-------- - 1250 | 1250 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - --- mcv covering just a small fraction of data -CREATE TABLE mcv_lists_partial ( - a INT, - b INT, - c INT -); --- 10 frequent groups, each with 100 elements -INSERT INTO mcv_lists_partial (a, b, c) - SELECT - mod(i,10), - mod(i,10), - mod(i,10) - FROM generate_series(0,999) s(i); --- 100 groups that will make it to the MCV list (includes the 10 frequent ones) -INSERT INTO mcv_lists_partial (a, b, c) - SELECT - i, - i, - i - FROM generate_series(0,99) s(i); --- 4000 groups in total, most of which won't make it (just a single item) -INSERT INTO mcv_lists_partial (a, b, c) - SELECT - i, - i, - i - FROM generate_series(0,3999) s(i); -ANALYZE mcv_lists_partial; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 AND b = 0 AND c = 0'); - estimated | actual ------------+-------- - 1 | 102 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 OR b = 0 OR c = 0'); - estimated | actual ------------+-------- - 300 | 102 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 10 AND b = 10 AND c = 10'); - estimated | actual ------------+-------- - 1 | 2 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 10 OR b = 10 OR c = 10'); - estimated | actual ------------+-------- - 6 | 2 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 AND b = 0 AND c = 10'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 OR b = 0 OR c = 10'); - estimated | actual ------------+-------- - 204 | 104 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0 AND c = 0) OR (a = 1 AND b = 1 AND c = 1) OR (a = 2 AND b = 2 AND c = 2)'); - estimated | actual ------------+-------- - 1 | 306 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0) OR (a = 0 AND c = 0) OR (b = 0 AND c = 0)'); - estimated | actual ------------+-------- - 6 | 102 -(1 row) - -CREATE STATISTICS mcv_lists_partial_stats (mcv) ON a, b, c - FROM mcv_lists_partial; -ANALYZE mcv_lists_partial; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 AND b = 0 AND c = 0'); - estimated | actual ------------+-------- - 102 | 102 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 OR b = 0 OR c = 0'); - estimated | actual ------------+-------- - 96 | 102 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 10 AND b = 10 AND c = 10'); - estimated | actual ------------+-------- - 2 | 2 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 10 OR b = 10 OR c = 10'); - estimated | actual ------------+-------- - 2 | 2 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 AND b = 0 AND c = 10'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0 OR b = 0 OR c = 10'); - estimated | actual ------------+-------- - 102 | 104 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0 AND c = 0) OR (a = 1 AND b = 1 AND c = 1) OR (a = 2 AND b = 2 AND c = 2)'); - estimated | actual ------------+-------- - 306 | 306 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0) OR (a = 0 AND c = 0) OR (b = 0 AND c = 0)'); - estimated | actual ------------+-------- - 108 | 102 -(1 row) - -DROP TABLE mcv_lists_partial; --- check the ability to use multiple MCV lists -CREATE TABLE mcv_lists_multi ( - a INTEGER, - b INTEGER, - c INTEGER, - d INTEGER -) -WITH (autovacuum_enabled = off); -INSERT INTO mcv_lists_multi (a, b, c, d) - SELECT - mod(i,5), - mod(i,5), - mod(i,7), - mod(i,7) - FROM generate_series(1,5000) s(i); -ANALYZE mcv_lists_multi; --- estimates without any mcv statistics -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0'); - estimated | actual ------------+-------- - 200 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE c = 0 AND d = 0'); - estimated | actual ------------+-------- - 102 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE b = 0 AND c = 0'); - estimated | actual ------------+-------- - 143 | 142 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE b = 0 OR c = 0'); - estimated | actual ------------+-------- - 1571 | 1572 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0 AND c = 0 AND d = 0'); - estimated | actual ------------+-------- - 4 | 142 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE (a = 0 AND b = 0) OR (c = 0 AND d = 0)'); - estimated | actual ------------+-------- - 298 | 1572 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 OR b = 0 OR c = 0 OR d = 0'); - estimated | actual ------------+-------- - 2649 | 1572 -(1 row) - --- create separate MCV statistics -CREATE STATISTICS mcv_lists_multi_1 (mcv) ON a, b FROM mcv_lists_multi; -CREATE STATISTICS mcv_lists_multi_2 (mcv) ON c, d FROM mcv_lists_multi; -ANALYZE mcv_lists_multi; -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0'); - estimated | actual ------------+-------- - 1000 | 1000 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE c = 0 AND d = 0'); - estimated | actual ------------+-------- - 714 | 714 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE b = 0 AND c = 0'); - estimated | actual ------------+-------- - 143 | 142 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE b = 0 OR c = 0'); - estimated | actual ------------+-------- - 1571 | 1572 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0 AND c = 0 AND d = 0'); - estimated | actual ------------+-------- - 143 | 142 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE (a = 0 AND b = 0) OR (c = 0 AND d = 0)'); - estimated | actual ------------+-------- - 1571 | 1572 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 OR b = 0 OR c = 0 OR d = 0'); - estimated | actual ------------+-------- - 1571 | 1572 -(1 row) - -DROP TABLE mcv_lists_multi; --- statistics on integer expressions -CREATE TABLE expr_stats (a int, b int, c int); -INSERT INTO expr_stats SELECT mod(i,10), mod(i,10), mod(i,10) FROM generate_series(1,1000) s(i); -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE (2*a) = 0 AND (3*b) = 0'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE (a+b) = 0 AND (a-b) = 0'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -CREATE STATISTICS expr_stats_1 (mcv) ON (a+b), (a-b), (2*a), (3*b) FROM expr_stats; -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE (2*a) = 0 AND (3*b) = 0'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE (a+b) = 0 AND (a-b) = 0'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -DROP STATISTICS expr_stats_1; -DROP TABLE expr_stats; --- statistics on a mix columns and expressions -CREATE TABLE expr_stats (a int, b int, c int); -INSERT INTO expr_stats SELECT mod(i,10), mod(i,10), mod(i,10) FROM generate_series(1,1000) s(i); -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND (2*a) = 0 AND (3*b) = 0'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 3 AND b = 3 AND (a-b) = 0'); - estimated | actual ------------+-------- - 1 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND b = 1 AND (a-b) = 0'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -CREATE STATISTICS expr_stats_1 (mcv) ON a, b, (2*a), (3*b), (a+b), (a-b) FROM expr_stats; -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND (2*a) = 0 AND (3*b) = 0'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 3 AND b = 3 AND (a-b) = 0'); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND b = 1 AND (a-b) = 0'); - estimated | actual ------------+-------- - 1 | 0 -(1 row) - -DROP TABLE expr_stats; --- statistics on expressions with different data types -CREATE TABLE expr_stats (a int, b name, c text); -INSERT INTO expr_stats SELECT mod(i,10), md5(mod(i,10)::text), md5(mod(i,10)::text) FROM generate_series(1,1000) s(i); -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND (b || c) <= ''z'' AND (c || b) >= ''0'''); - estimated | actual ------------+-------- - 11 | 100 -(1 row) - -CREATE STATISTICS expr_stats_1 (mcv) ON a, b, (b || c), (c || b) FROM expr_stats; -ANALYZE expr_stats; -SELECT * FROM check_estimated_rows('SELECT * FROM expr_stats WHERE a = 0 AND (b || c) <= ''z'' AND (c || b) >= ''0'''); - estimated | actual ------------+-------- - 100 | 100 -(1 row) - -DROP TABLE expr_stats; --- test handling of a mix of compatible and incompatible expressions -CREATE TABLE expr_stats_incompatible_test ( - c0 double precision, - c1 boolean NOT NULL -); -CREATE STATISTICS expr_stat_comp_1 ON c0, c1 FROM expr_stats_incompatible_test; -INSERT INTO expr_stats_incompatible_test VALUES (1234,false), (5678,true); -ANALYZE expr_stats_incompatible_test; -SELECT c0 FROM ONLY expr_stats_incompatible_test WHERE -( - upper('x') LIKE ('x'||('[0,1]'::int4range)) - AND - (c0 IN (0, 1) OR c1) -); - c0 ----- -(0 rows) - -DROP TABLE expr_stats_incompatible_test; --- Permission tests. Users should not be able to see specific data values in --- the extended statistics, if they lack permission to see those values in --- the underlying table. --- --- Currently this is only relevant for MCV stats. -CREATE SCHEMA tststats; -CREATE TABLE tststats.priv_test_tbl ( - a int, - b int -); -INSERT INTO tststats.priv_test_tbl - SELECT mod(i,5), mod(i,10) FROM generate_series(1,100) s(i); -CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b - FROM tststats.priv_test_tbl; -ANALYZE tststats.priv_test_tbl; --- Check printing info about extended statistics by \dX -create table stts_t1 (a int, b int); -create statistics stts_1 (ndistinct) on a, b from stts_t1; -create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1; -create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1; -create table stts_t2 (a int, b int, c int); -create statistics stts_4 on b, c from stts_t2; -create table stts_t3 (col1 int, col2 int, col3 int); -create statistics stts_hoge on col1, col2, col3 from stts_t3; -create schema stts_s1; -create schema stts_s2; -create statistics stts_s1.stts_foo on col1, col2 from stts_t3; -create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3; -insert into stts_t1 select i,i from generate_series(1,100) i; -analyze stts_t1; -set search_path to public, stts_s1, stts_s2, tststats; -\dX - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV -----------+------------------------+------------------------------------------------------------------+-----------+--------------+--------- - public | func_deps_stat | (a * 2), upper(b), (c + 1::numeric) FROM functional_dependencies | | defined | - public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays | | | defined - public | mcv_lists_bool_stats | a, b, c FROM mcv_lists_bool | | | defined - public | mcv_lists_stats | a, b, d FROM mcv_lists | | | defined - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - stts_s1 | stts_foo | col1, col2 FROM stts_t3 | defined | defined | defined - stts_s2 | stts_yama | col1, col3 FROM stts_t3 | | defined | defined - tststats | priv_test_stats | a, b FROM priv_test_tbl | | | defined -(12 rows) - -\dX stts_? - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+--------+-------------------+-----------+--------------+--------- - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined -(4 rows) - -\dX *stts_hoge - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+-----------+-------------------------------+-----------+--------------+--------- - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined -(1 row) - -\dX+ - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV -----------+------------------------+------------------------------------------------------------------+-----------+--------------+--------- - public | func_deps_stat | (a * 2), upper(b), (c + 1::numeric) FROM functional_dependencies | | defined | - public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays | | | defined - public | mcv_lists_bool_stats | a, b, c FROM mcv_lists_bool | | | defined - public | mcv_lists_stats | a, b, d FROM mcv_lists | | | defined - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - stts_s1 | stts_foo | col1, col2 FROM stts_t3 | defined | defined | defined - stts_s2 | stts_yama | col1, col3 FROM stts_t3 | | defined | defined - tststats | priv_test_stats | a, b FROM priv_test_tbl | | | defined -(12 rows) - -\dX+ stts_? - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+--------+-------------------+-----------+--------------+--------- - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined -(4 rows) - -\dX+ *stts_hoge - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+-----------+-------------------------------+-----------+--------------+--------- - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined -(1 row) - -\dX+ stts_s2.stts_yama - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ----------+-----------+-------------------------+-----------+--------------+--------- - stts_s2 | stts_yama | col1, col3 FROM stts_t3 | | defined | defined -(1 row) - -set search_path to public, stts_s1; -\dX - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ----------+------------------------+------------------------------------------------------------------+-----------+--------------+--------- - public | func_deps_stat | (a * 2), upper(b), (c + 1::numeric) FROM functional_dependencies | | defined | - public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays | | | defined - public | mcv_lists_bool_stats | a, b, c FROM mcv_lists_bool | | | defined - public | mcv_lists_stats | a, b, d FROM mcv_lists | | | defined - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - stts_s1 | stts_foo | col1, col2 FROM stts_t3 | defined | defined | defined -(10 rows) - -create role regress_stats_ext nosuperuser; -set role regress_stats_ext; -\dX - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+------------------------+------------------------------------------------------------------+-----------+--------------+--------- - public | func_deps_stat | (a * 2), upper(b), (c + 1::numeric) FROM functional_dependencies | | defined | - public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays | | | defined - public | mcv_lists_bool_stats | a, b, c FROM mcv_lists_bool | | | defined - public | mcv_lists_stats | a, b, d FROM mcv_lists | | | defined - public | stts_1 | a, b FROM stts_t1 | defined | | - public | stts_2 | a, b FROM stts_t1 | defined | defined | - public | stts_3 | a, b FROM stts_t1 | defined | defined | defined - public | stts_4 | b, c FROM stts_t2 | defined | defined | defined - public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined -(9 rows) - -reset role; -drop table stts_t1, stts_t2, stts_t3; -drop schema stts_s1, stts_s2 cascade; -drop user regress_stats_ext; -reset search_path; --- User with no access -CREATE USER regress_stats_user1; -GRANT USAGE ON SCHEMA tststats TO regress_stats_user1; -SET SESSION AUTHORIZATION regress_stats_user1; -SELECT * FROM tststats.priv_test_tbl; -- Permission denied -ERROR: permission denied for table priv_test_tbl --- Attempt to gain access using a leaky operator -CREATE FUNCTION op_leak(int, int) RETURNS bool - AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END' - LANGUAGE plpgsql; -CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int, - restrict = scalarltsel); -SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied -ERROR: permission denied for table priv_test_tbl -DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied -ERROR: permission denied for table priv_test_tbl --- Grant access via a security barrier view, but hide all data -RESET SESSION AUTHORIZATION; -CREATE VIEW tststats.priv_test_view WITH (security_barrier=true) - AS SELECT * FROM tststats.priv_test_tbl WHERE false; -GRANT SELECT, DELETE ON tststats.priv_test_view TO regress_stats_user1; --- Should now have access via the view, but see nothing and leak nothing -SET SESSION AUTHORIZATION regress_stats_user1; -SELECT * FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak - a | b ----+--- -(0 rows) - -DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak --- Grant table access, but hide all data with RLS -RESET SESSION AUTHORIZATION; -ALTER TABLE tststats.priv_test_tbl ENABLE ROW LEVEL SECURITY; -GRANT SELECT, DELETE ON tststats.priv_test_tbl TO regress_stats_user1; --- Should now have direct table access, but see nothing and leak nothing -SET SESSION AUTHORIZATION regress_stats_user1; -SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak - a | b ----+--- -(0 rows) - -DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak --- Tidy up -DROP OPERATOR <<< (int, int); -DROP FUNCTION op_leak(int, int); -RESET SESSION AUTHORIZATION; -DROP SCHEMA tststats CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table tststats.priv_test_tbl -drop cascades to view tststats.priv_test_view -DROP USER regress_stats_user1; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.linux.utf8_1.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.linux.utf8.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/collate.linux.utf8_1.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/collate.linux.utf8.out 2021-10-30 22:04:16 -0600 @@ -1,11 +1,2 @@ -/* - * This test is for Linux/glibc systems and assumes that a full set of - * locales is installed. It must be run in a database with UTF-8 encoding, - * because other encodings don't support all the characters used. - */ -SELECT getdatabaseencoding() <> 'UTF8' OR - (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE', 'tr_TR') AND collencoding = pg_char_to_encoding('UTF8')) <> 4 OR - version() !~ 'linux-gnu' - AS skip_test \gset -\if :skip_test -\quit +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_parallel.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_parallel.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/select_parallel.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/select_parallel.out 2021-10-30 22:04:16 -0600 @@ -1,1197 +1,2 @@ --- --- PARALLEL --- -create function sp_parallel_restricted(int) returns int as - $$begin return $1; end$$ language plpgsql parallel restricted; -begin; --- encourage use of parallel plans -set parallel_setup_cost=0; -set parallel_tuple_cost=0; -set min_parallel_table_scan_size=0; -set max_parallel_workers_per_gather=4; --- Parallel Append with partial-subplans -explain (costs off) - select round(avg(aa)), sum(aa) from a_star; - QUERY PLAN --------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 3 - -> Partial Aggregate - -> Parallel Append - -> Parallel Seq Scan on d_star a_star_4 - -> Parallel Seq Scan on f_star a_star_6 - -> Parallel Seq Scan on e_star a_star_5 - -> Parallel Seq Scan on b_star a_star_2 - -> Parallel Seq Scan on c_star a_star_3 - -> Parallel Seq Scan on a_star a_star_1 -(11 rows) - -select round(avg(aa)), sum(aa) from a_star a1; - round | sum --------+----- - 14 | 355 -(1 row) - --- Parallel Append with both partial and non-partial subplans -alter table c_star set (parallel_workers = 0); -alter table d_star set (parallel_workers = 0); -explain (costs off) - select round(avg(aa)), sum(aa) from a_star; - QUERY PLAN --------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 3 - -> Partial Aggregate - -> Parallel Append - -> Seq Scan on d_star a_star_4 - -> Seq Scan on c_star a_star_3 - -> Parallel Seq Scan on f_star a_star_6 - -> Parallel Seq Scan on e_star a_star_5 - -> Parallel Seq Scan on b_star a_star_2 - -> Parallel Seq Scan on a_star a_star_1 -(11 rows) - -select round(avg(aa)), sum(aa) from a_star a2; - round | sum --------+----- - 14 | 355 -(1 row) - --- Parallel Append with only non-partial subplans -alter table a_star set (parallel_workers = 0); -alter table b_star set (parallel_workers = 0); -alter table e_star set (parallel_workers = 0); -alter table f_star set (parallel_workers = 0); -explain (costs off) - select round(avg(aa)), sum(aa) from a_star; - QUERY PLAN ------------------------------------------------------ - Finalize Aggregate - -> Gather - Workers Planned: 3 - -> Partial Aggregate - -> Parallel Append - -> Seq Scan on d_star a_star_4 - -> Seq Scan on f_star a_star_6 - -> Seq Scan on e_star a_star_5 - -> Seq Scan on b_star a_star_2 - -> Seq Scan on c_star a_star_3 - -> Seq Scan on a_star a_star_1 -(11 rows) - -select round(avg(aa)), sum(aa) from a_star a3; - round | sum --------+----- - 14 | 355 -(1 row) - --- Disable Parallel Append -alter table a_star reset (parallel_workers); -alter table b_star reset (parallel_workers); -alter table c_star reset (parallel_workers); -alter table d_star reset (parallel_workers); -alter table e_star reset (parallel_workers); -alter table f_star reset (parallel_workers); -set enable_parallel_append to off; -explain (costs off) - select round(avg(aa)), sum(aa) from a_star; - QUERY PLAN --------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 1 - -> Partial Aggregate - -> Append - -> Parallel Seq Scan on a_star a_star_1 - -> Parallel Seq Scan on b_star a_star_2 - -> Parallel Seq Scan on c_star a_star_3 - -> Parallel Seq Scan on d_star a_star_4 - -> Parallel Seq Scan on e_star a_star_5 - -> Parallel Seq Scan on f_star a_star_6 -(11 rows) - -select round(avg(aa)), sum(aa) from a_star a4; - round | sum --------+----- - 14 | 355 -(1 row) - -reset enable_parallel_append; --- Parallel Append that runs serially -create function sp_test_func() returns setof text as -$$ select 'foo'::varchar union all select 'bar'::varchar $$ -language sql stable; -select sp_test_func() order by 1; - sp_test_func --------------- - bar - foo -(2 rows) - --- Parallel Append is not to be used when the subpath depends on the outer param -create table part_pa_test(a int, b int) partition by range(a); -create table part_pa_test_p1 partition of part_pa_test for values from (minvalue) to (0); -create table part_pa_test_p2 partition of part_pa_test for values from (0) to (maxvalue); -explain (costs off) - select (select max((select pa1.b from part_pa_test pa1 where pa1.a = pa2.a))) - from part_pa_test pa2; - QUERY PLAN --------------------------------------------------------------- - Aggregate - -> Gather - Workers Planned: 3 - -> Parallel Append - -> Parallel Seq Scan on part_pa_test_p1 pa2_1 - -> Parallel Seq Scan on part_pa_test_p2 pa2_2 - SubPlan 2 - -> Result - SubPlan 1 - -> Append - -> Seq Scan on part_pa_test_p1 pa1_1 - Filter: (a = pa2.a) - -> Seq Scan on part_pa_test_p2 pa1_2 - Filter: (a = pa2.a) -(14 rows) - -drop table part_pa_test; --- test with leader participation disabled -set parallel_leader_participation = off; -explain (costs off) - select count(*) from tenk1 where stringu1 = 'GRAAAA'; - QUERY PLAN ---------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Seq Scan on tenk1 - Filter: (stringu1 = 'GRAAAA'::name) -(6 rows) - -select count(*) from tenk1 where stringu1 = 'GRAAAA'; - count -------- - 15 -(1 row) - --- test with leader participation disabled, but no workers available (so --- the leader will have to run the plan despite the setting) -set max_parallel_workers = 0; -explain (costs off) - select count(*) from tenk1 where stringu1 = 'GRAAAA'; - QUERY PLAN ---------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Seq Scan on tenk1 - Filter: (stringu1 = 'GRAAAA'::name) -(6 rows) - -select count(*) from tenk1 where stringu1 = 'GRAAAA'; - count -------- - 15 -(1 row) - -reset max_parallel_workers; -reset parallel_leader_participation; --- test that parallel_restricted function doesn't run in worker -alter table tenk1 set (parallel_workers = 4); -explain (verbose, costs off) -select sp_parallel_restricted(unique1) from tenk1 - where stringu1 = 'GRAAAA' order by 1; - QUERY PLAN ---------------------------------------------------------- - Sort - Output: (sp_parallel_restricted(unique1)) - Sort Key: (sp_parallel_restricted(tenk1.unique1)) - -> Gather - Output: sp_parallel_restricted(unique1) - Workers Planned: 4 - -> Parallel Seq Scan on public.tenk1 - Output: unique1 - Filter: (tenk1.stringu1 = 'GRAAAA'::name) -(9 rows) - --- test parallel plan when group by expression is in target list. -explain (costs off) - select length(stringu1) from tenk1 group by length(stringu1); - QUERY PLAN ---------------------------------------------------- - Finalize HashAggregate - Group Key: (length((stringu1)::text)) - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: length((stringu1)::text) - -> Parallel Seq Scan on tenk1 -(7 rows) - -select length(stringu1) from tenk1 group by length(stringu1); - length --------- - 6 -(1 row) - -explain (costs off) - select stringu1, count(*) from tenk1 group by stringu1 order by stringu1; - QUERY PLAN ----------------------------------------------------- - Sort - Sort Key: stringu1 - -> Finalize HashAggregate - Group Key: stringu1 - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: stringu1 - -> Parallel Seq Scan on tenk1 -(9 rows) - --- test that parallel plan for aggregates is not selected when --- target list contains parallel restricted clause. -explain (costs off) - select sum(sp_parallel_restricted(unique1)) from tenk1 - group by(sp_parallel_restricted(unique1)); - QUERY PLAN -------------------------------------------------------------------- - HashAggregate - Group Key: sp_parallel_restricted(unique1) - -> Gather - Workers Planned: 4 - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 -(5 rows) - --- test prepared statement -prepare tenk1_count(integer) As select count((unique1)) from tenk1 where hundred > $1; -explain (costs off) execute tenk1_count(1); - QUERY PLAN ----------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Seq Scan on tenk1 - Filter: (hundred > 1) -(6 rows) - -execute tenk1_count(1); - count -------- - 9800 -(1 row) - -deallocate tenk1_count; --- test parallel plans for queries containing un-correlated subplans. -alter table tenk2 set (parallel_workers = 0); -explain (costs off) - select count(*) from tenk1 where (two, four) not in - (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 - -> Seq Scan on tenk2 - Filter: (thousand > 100) -(9 rows) - -select count(*) from tenk1 where (two, four) not in - (select hundred, thousand from tenk2 where thousand > 100); - count -------- - 10000 -(1 row) - --- this is not parallel-safe due to use of random() within SubLink's testexpr: -explain (costs off) - select * from tenk1 where (unique1 + random())::integer not in - (select ten from tenk2); - QUERY PLAN ------------------------------------- - Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 - -> Seq Scan on tenk2 -(4 rows) - -alter table tenk2 reset (parallel_workers); --- test parallel plan for a query containing initplan. -set enable_indexscan = off; -set enable_indexonlyscan = off; -set enable_bitmapscan = off; -alter table tenk2 set (parallel_workers = 2); -explain (costs off) - select count(*) from tenk1 - where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); - QUERY PLAN ------------------------------------------------------- - Aggregate - InitPlan 1 (returns $2) - -> Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Seq Scan on tenk2 - -> Gather - Workers Planned: 4 - Params Evaluated: $2 - -> Parallel Seq Scan on tenk1 - Filter: (unique1 = $2) -(12 rows) - -select count(*) from tenk1 - where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); - count -------- - 1 -(1 row) - -reset enable_indexscan; -reset enable_indexonlyscan; -reset enable_bitmapscan; -alter table tenk2 reset (parallel_workers); --- test parallel index scans. -set enable_seqscan to off; -set enable_bitmapscan to off; -explain (costs off) - select count((unique1)) from tenk1 where hundred > 1; - QUERY PLAN --------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Scan using tenk1_hundred on tenk1 - Index Cond: (hundred > 1) -(6 rows) - -select count((unique1)) from tenk1 where hundred > 1; - count -------- - 9800 -(1 row) - --- test parallel index-only scans. -explain (costs off) - select count(*) from tenk1 where thousand > 95; - QUERY PLAN --------------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: (thousand > 95) -(6 rows) - -select count(*) from tenk1 where thousand > 95; - count -------- - 9040 -(1 row) - --- test rescan cases too -set enable_material = false; -explain (costs off) -select * from - (select count(unique1) from tenk1 where hundred > 10) ss - right join (values (1),(2),(3)) v(x) on true; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop Left Join - -> Values Scan on "*VALUES*" - -> Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Scan using tenk1_hundred on tenk1 - Index Cond: (hundred > 10) -(8 rows) - -select * from - (select count(unique1) from tenk1 where hundred > 10) ss - right join (values (1),(2),(3)) v(x) on true; - count | x --------+--- - 8900 | 1 - 8900 | 2 - 8900 | 3 -(3 rows) - -explain (costs off) -select * from - (select count(*) from tenk1 where thousand > 99) ss - right join (values (1),(2),(3)) v(x) on true; - QUERY PLAN --------------------------------------------------------------------------------------- - Nested Loop Left Join - -> Values Scan on "*VALUES*" - -> Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: (thousand > 99) -(8 rows) - -select * from - (select count(*) from tenk1 where thousand > 99) ss - right join (values (1),(2),(3)) v(x) on true; - count | x --------+--- - 9000 | 1 - 9000 | 2 - 9000 | 3 -(3 rows) - --- test rescans for a Limit node with a parallel node beneath it. -reset enable_seqscan; -set enable_indexonlyscan to off; -set enable_indexscan to off; -alter table tenk1 set (parallel_workers = 0); -alter table tenk2 set (parallel_workers = 1); -explain (costs off) -select count(*) from tenk1 - left join (select tenk2.unique1 from tenk2 order by 1 limit 1000) ss - on tenk1.unique1 < ss.unique1 + 1 - where tenk1.unique1 < 2; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: (tenk1.unique1 < (tenk2.unique1 + 1)) - -> Seq Scan on tenk1 - Filter: (unique1 < 2) - -> Limit - -> Gather Merge - Workers Planned: 1 - -> Sort - Sort Key: tenk2.unique1 - -> Parallel Seq Scan on tenk2 -(11 rows) - -select count(*) from tenk1 - left join (select tenk2.unique1 from tenk2 order by 1 limit 1000) ss - on tenk1.unique1 < ss.unique1 + 1 - where tenk1.unique1 < 2; - count -------- - 1999 -(1 row) - ---reset the value of workers for each table as it was before this test. -alter table tenk1 set (parallel_workers = 4); -alter table tenk2 reset (parallel_workers); -reset enable_material; -reset enable_bitmapscan; -reset enable_indexonlyscan; -reset enable_indexscan; --- test parallel bitmap heap scan. -set enable_seqscan to off; -set enable_indexscan to off; -set enable_hashjoin to off; -set enable_mergejoin to off; -set enable_material to off; --- test prefetching, if the platform allows it -DO $$ -BEGIN - SET effective_io_concurrency = 50; -EXCEPTION WHEN invalid_parameter_value THEN -END $$; -set work_mem='64kB'; --set small work mem to force lossy pages -explain (costs off) - select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk2 - Filter: (thousand = 0) - -> Gather - Workers Planned: 4 - -> Parallel Bitmap Heap Scan on tenk1 - Recheck Cond: (hundred > 1) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred > 1) -(10 rows) - -select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0; - count -------- - 98000 -(1 row) - -create table bmscantest (a int, t text); -insert into bmscantest select r, 'fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1,100000) r; -create index i_bmtest ON bmscantest(a); -select count(*) from bmscantest where a>1; - count -------- - 99999 -(1 row) - --- test accumulation of stats for parallel nodes -reset enable_seqscan; -alter table tenk2 set (parallel_workers = 0); -explain (analyze, timing off, summary off, costs off) - select count(*) from tenk1, tenk2 where tenk1.hundred > 1 - and tenk2.thousand=0; - QUERY PLAN --------------------------------------------------------------------------- - Aggregate (actual rows=1 loops=1) - -> Nested Loop (actual rows=98000 loops=1) - -> Seq Scan on tenk2 (actual rows=10 loops=1) - Filter: (thousand = 0) - Rows Removed by Filter: 9990 - -> Gather (actual rows=9800 loops=10) - Workers Planned: 4 - Workers Launched: 4 - -> Parallel Seq Scan on tenk1 (actual rows=1960 loops=50) - Filter: (hundred > 1) - Rows Removed by Filter: 40 -(11 rows) - -alter table tenk2 reset (parallel_workers); -reset work_mem; -create function explain_parallel_sort_stats() returns setof text -language plpgsql as -$$ -declare ln text; -begin - for ln in - explain (analyze, timing off, summary off, costs off) - select * from - (select ten from tenk1 where ten < 100 order by ten) ss - right join (values (1),(2),(3)) v(x) on true - loop - ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); - return next ln; - end loop; -end; -$$; -select * from explain_parallel_sort_stats(); - explain_parallel_sort_stats --------------------------------------------------------------------------- - Nested Loop Left Join (actual rows=30000 loops=1) - -> Values Scan on "*VALUES*" (actual rows=3 loops=1) - -> Gather Merge (actual rows=10000 loops=3) - Workers Planned: 4 - Workers Launched: 4 - -> Sort (actual rows=2000 loops=15) - Sort Key: tenk1.ten - Sort Method: quicksort Memory: xxx - Worker 0: Sort Method: quicksort Memory: xxx - Worker 1: Sort Method: quicksort Memory: xxx - Worker 2: Sort Method: quicksort Memory: xxx - Worker 3: Sort Method: quicksort Memory: xxx - -> Parallel Seq Scan on tenk1 (actual rows=2000 loops=15) - Filter: (ten < 100) -(14 rows) - -reset enable_indexscan; -reset enable_hashjoin; -reset enable_mergejoin; -reset enable_material; -reset effective_io_concurrency; -drop table bmscantest; -drop function explain_parallel_sort_stats(); --- test parallel merge join path. -set enable_hashjoin to off; -set enable_nestloop to off; -explain (costs off) - select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; - QUERY PLAN -------------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Merge Join - Merge Cond: (tenk1.unique1 = tenk2.unique1) - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 - -> Index Only Scan using tenk2_unique1 on tenk2 -(8 rows) - -select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; - count -------- - 10000 -(1 row) - -reset enable_hashjoin; -reset enable_nestloop; --- test gather merge -set enable_hashagg = false; -explain (costs off) - select count(*) from tenk1 group by twenty; - QUERY PLAN ----------------------------------------------------- - Finalize GroupAggregate - Group Key: twenty - -> Gather Merge - Workers Planned: 4 - -> Partial GroupAggregate - Group Key: twenty - -> Sort - Sort Key: twenty - -> Parallel Seq Scan on tenk1 -(9 rows) - -select count(*) from tenk1 group by twenty; - count -------- - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 -(20 rows) - ---test expressions in targetlist are pushed down for gather merge -create function sp_simple_func(var1 integer) returns integer -as $$ -begin - return var1 + 10; -end; -$$ language plpgsql PARALLEL SAFE; -explain (costs off, verbose) - select ten, sp_simple_func(ten) from tenk1 where ten < 100 order by ten; - QUERY PLAN ------------------------------------------------------ - Gather Merge - Output: ten, (sp_simple_func(ten)) - Workers Planned: 4 - -> Result - Output: ten, sp_simple_func(ten) - -> Sort - Output: ten - Sort Key: tenk1.ten - -> Parallel Seq Scan on public.tenk1 - Output: ten - Filter: (tenk1.ten < 100) -(11 rows) - -drop function sp_simple_func(integer); --- test handling of SRFs in targetlist (bug in 10.0) -explain (costs off) - select count(*), generate_series(1,2) from tenk1 group by twenty; - QUERY PLAN ----------------------------------------------------------- - ProjectSet - -> Finalize GroupAggregate - Group Key: twenty - -> Gather Merge - Workers Planned: 4 - -> Partial GroupAggregate - Group Key: twenty - -> Sort - Sort Key: twenty - -> Parallel Seq Scan on tenk1 -(10 rows) - -select count(*), generate_series(1,2) from tenk1 group by twenty; - count | generate_series --------+----------------- - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 - 500 | 1 - 500 | 2 -(40 rows) - --- test gather merge with parallel leader participation disabled -set parallel_leader_participation = off; -explain (costs off) - select count(*) from tenk1 group by twenty; - QUERY PLAN ----------------------------------------------------- - Finalize GroupAggregate - Group Key: twenty - -> Gather Merge - Workers Planned: 4 - -> Partial GroupAggregate - Group Key: twenty - -> Sort - Sort Key: twenty - -> Parallel Seq Scan on tenk1 -(9 rows) - -select count(*) from tenk1 group by twenty; - count -------- - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 - 500 -(20 rows) - -reset parallel_leader_participation; ---test rescan behavior of gather merge -set enable_material = false; -explain (costs off) -select * from - (select string4, count(unique2) - from tenk1 group by string4 order by string4) ss - right join (values (1),(2),(3)) v(x) on true; - QUERY PLAN ----------------------------------------------------------- - Nested Loop Left Join - -> Values Scan on "*VALUES*" - -> Finalize GroupAggregate - Group Key: tenk1.string4 - -> Gather Merge - Workers Planned: 4 - -> Partial GroupAggregate - Group Key: tenk1.string4 - -> Sort - Sort Key: tenk1.string4 - -> Parallel Seq Scan on tenk1 -(11 rows) - -select * from - (select string4, count(unique2) - from tenk1 group by string4 order by string4) ss - right join (values (1),(2),(3)) v(x) on true; - string4 | count | x ----------+-------+--- - AAAAxx | 2500 | 1 - HHHHxx | 2500 | 1 - OOOOxx | 2500 | 1 - VVVVxx | 2500 | 1 - AAAAxx | 2500 | 2 - HHHHxx | 2500 | 2 - OOOOxx | 2500 | 2 - VVVVxx | 2500 | 2 - AAAAxx | 2500 | 3 - HHHHxx | 2500 | 3 - OOOOxx | 2500 | 3 - VVVVxx | 2500 | 3 -(12 rows) - -reset enable_material; -reset enable_hashagg; --- check parallelized int8 aggregate (bug #14897) -explain (costs off) -select avg(unique1::int8) from tenk1; - QUERY PLAN -------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 4 - -> Partial Aggregate - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 -(5 rows) - -select avg(unique1::int8) from tenk1; - avg ------------------------ - 4999.5000000000000000 -(1 row) - --- gather merge test with a LIMIT -explain (costs off) - select fivethous from tenk1 order by fivethous limit 4; - QUERY PLAN ----------------------------------------------- - Limit - -> Gather Merge - Workers Planned: 4 - -> Sort - Sort Key: fivethous - -> Parallel Seq Scan on tenk1 -(6 rows) - -select fivethous from tenk1 order by fivethous limit 4; - fivethous ------------ - 0 - 0 - 1 - 1 -(4 rows) - --- gather merge test with 0 worker -set max_parallel_workers = 0; -explain (costs off) - select string4 from tenk1 order by string4 limit 5; - QUERY PLAN ----------------------------------------------- - Limit - -> Gather Merge - Workers Planned: 4 - -> Sort - Sort Key: string4 - -> Parallel Seq Scan on tenk1 -(6 rows) - -select string4 from tenk1 order by string4 limit 5; - string4 ---------- - AAAAxx - AAAAxx - AAAAxx - AAAAxx - AAAAxx -(5 rows) - --- gather merge test with 0 workers, with parallel leader --- participation disabled (the leader will have to run the plan --- despite the setting) -set parallel_leader_participation = off; -explain (costs off) - select string4 from tenk1 order by string4 limit 5; - QUERY PLAN ----------------------------------------------- - Limit - -> Gather Merge - Workers Planned: 4 - -> Sort - Sort Key: string4 - -> Parallel Seq Scan on tenk1 -(6 rows) - -select string4 from tenk1 order by string4 limit 5; - string4 ---------- - AAAAxx - AAAAxx - AAAAxx - AAAAxx - AAAAxx -(5 rows) - -reset parallel_leader_participation; -reset max_parallel_workers; -SAVEPOINT settings; -SET LOCAL force_parallel_mode = 1; -explain (costs off) - select stringu1::int2 from tenk1 where unique1 = 1; - QUERY PLAN ------------------------------------------------ - Gather - Workers Planned: 1 - Single Copy: true - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(5 rows) - -ROLLBACK TO SAVEPOINT settings; --- exercise record typmod remapping between backends -CREATE FUNCTION make_record(n int) - RETURNS RECORD LANGUAGE plpgsql PARALLEL SAFE AS -$$ -BEGIN - RETURN CASE n - WHEN 1 THEN ROW(1) - WHEN 2 THEN ROW(1, 2) - WHEN 3 THEN ROW(1, 2, 3) - WHEN 4 THEN ROW(1, 2, 3, 4) - ELSE ROW(1, 2, 3, 4, 5) - END; -END; -$$; -SAVEPOINT settings; -SET LOCAL force_parallel_mode = 1; -SELECT make_record(x) FROM (SELECT generate_series(1, 5) x) ss ORDER BY x; - make_record -------------- - (1) - (1,2) - (1,2,3) - (1,2,3,4) - (1,2,3,4,5) -(5 rows) - -ROLLBACK TO SAVEPOINT settings; -DROP function make_record(n int); --- test the sanity of parallel query after the active role is dropped. -drop role if exists regress_parallel_worker; -NOTICE: role "regress_parallel_worker" does not exist, skipping -create role regress_parallel_worker; -set role regress_parallel_worker; -reset session authorization; -drop role regress_parallel_worker; -set force_parallel_mode = 1; -select count(*) from tenk1; - count -------- - 10000 -(1 row) - -reset force_parallel_mode; -reset role; --- Window function calculation can't be pushed to workers. -explain (costs off, verbose) - select count(*) from tenk1 a where (unique1, two) in - (select unique1, row_number() over() from tenk1 b); - QUERY PLAN ----------------------------------------------------------------------------------------------- - Aggregate - Output: count(*) - -> Hash Semi Join - Hash Cond: ((a.unique1 = b.unique1) AND (a.two = (row_number() OVER (?)))) - -> Gather - Output: a.unique1, a.two - Workers Planned: 4 - -> Parallel Seq Scan on public.tenk1 a - Output: a.unique1, a.two - -> Hash - Output: b.unique1, (row_number() OVER (?)) - -> WindowAgg - Output: b.unique1, row_number() OVER (?) - -> Gather - Output: b.unique1 - Workers Planned: 4 - -> Parallel Index Only Scan using tenk1_unique1 on public.tenk1 b - Output: b.unique1 -(18 rows) - --- LIMIT/OFFSET within sub-selects can't be pushed to workers. -explain (costs off) - select * from tenk1 a where two in - (select two from tenk1 b where stringu1 like '%AAAA' limit 3); - QUERY PLAN ---------------------------------------------------------------- - Hash Semi Join - Hash Cond: (a.two = b.two) - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 a - -> Hash - -> Limit - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 b - Filter: (stringu1 ~~ '%AAAA'::text) -(11 rows) - --- to increase the parallel query test coverage -SAVEPOINT settings; -SET LOCAL force_parallel_mode = 1; -EXPLAIN (analyze, timing off, summary off, costs off) SELECT * FROM tenk1; - QUERY PLAN -------------------------------------------------------------- - Gather (actual rows=10000 loops=1) - Workers Planned: 4 - Workers Launched: 4 - -> Parallel Seq Scan on tenk1 (actual rows=2000 loops=5) -(4 rows) - -ROLLBACK TO SAVEPOINT settings; --- provoke error in worker --- (make the error message long enough to require multiple bufferloads) -SAVEPOINT settings; -SET LOCAL force_parallel_mode = 1; -select (stringu1 || repeat('abcd', 5000))::int2 from tenk1 where unique1 = 1; -ERROR: invalid input syntax for type smallint: "BAAAAAabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd" -CONTEXT: parallel worker -ROLLBACK TO SAVEPOINT settings; --- test interaction with set-returning functions -SAVEPOINT settings; --- multiple subqueries under a single Gather node --- must set parallel_setup_cost > 0 to discourage multiple Gather nodes -SET LOCAL parallel_setup_cost = 10; -EXPLAIN (COSTS OFF) -SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1 -UNION ALL -SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1; - QUERY PLAN ----------------------------------------------------- - Gather - Workers Planned: 4 - -> Parallel Append - -> Parallel Seq Scan on tenk1 - Filter: (fivethous = (tenthous + 1)) - -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = (tenthous + 1)) -(7 rows) - -ROLLBACK TO SAVEPOINT settings; --- can't use multiple subqueries under a single Gather node due to initPlans -EXPLAIN (COSTS OFF) -SELECT unique1 FROM tenk1 WHERE fivethous = - (SELECT unique1 FROM tenk1 WHERE fivethous = 1 LIMIT 1) -UNION ALL -SELECT unique1 FROM tenk1 WHERE fivethous = - (SELECT unique2 FROM tenk1 WHERE fivethous = 1 LIMIT 1) -ORDER BY 1; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: tenk1.unique1 - -> Append - -> Gather - Workers Planned: 4 - Params Evaluated: $1 - InitPlan 1 (returns $1) - -> Limit - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 tenk1_2 - Filter: (fivethous = 1) - -> Parallel Seq Scan on tenk1 - Filter: (fivethous = $1) - -> Gather - Workers Planned: 4 - Params Evaluated: $3 - InitPlan 2 (returns $3) - -> Limit - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 tenk1_3 - Filter: (fivethous = 1) - -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = $3) -(25 rows) - --- test interaction with SRFs -SELECT * FROM information_schema.foreign_data_wrapper_options -ORDER BY 1, 2, 3; - foreign_data_wrapper_catalog | foreign_data_wrapper_name | option_name | option_value -------------------------------+---------------------------+-------------+-------------- -(0 rows) - -EXPLAIN (VERBOSE, COSTS OFF) -SELECT generate_series(1, two), array(select generate_series(1, two)) - FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- - ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous - -> Gather Merge - Output: tenk1.two, tenk1.tenthous - Workers Planned: 4 - -> Result - Output: tenk1.two, tenk1.tenthous - -> Sort - Output: tenk1.tenthous, tenk1.two - Sort Key: tenk1.tenthous - -> Parallel Seq Scan on public.tenk1 - Output: tenk1.tenthous, tenk1.two - SubPlan 1 - -> ProjectSet - Output: generate_series(1, tenk1.two) - -> Result -(16 rows) - --- test passing expanded-value representations to workers -CREATE FUNCTION make_some_array(int,int) returns int[] as -$$declare x int[]; - begin - x[1] := $1; - x[2] := $2; - return x; - end$$ language plpgsql parallel safe; -CREATE TABLE fooarr(f1 text, f2 int[], f3 text); -INSERT INTO fooarr VALUES('1', ARRAY[1,2], 'one'); -PREPARE pstmt(text, int[]) AS SELECT * FROM fooarr WHERE f1 = $1 AND f2 = $2; -EXPLAIN (COSTS OFF) EXECUTE pstmt('1', make_some_array(1,2)); - QUERY PLAN ------------------------------------------------------------------- - Gather - Workers Planned: 3 - -> Parallel Seq Scan on fooarr - Filter: ((f1 = '1'::text) AND (f2 = '{1,2}'::integer[])) -(4 rows) - -EXECUTE pstmt('1', make_some_array(1,2)); - f1 | f2 | f3 -----+-------+----- - 1 | {1,2} | one -(1 row) - -DEALLOCATE pstmt; --- test interaction between subquery and partial_paths -CREATE VIEW tenk1_vw_sec WITH (security_barrier) AS SELECT * FROM tenk1; -EXPLAIN (COSTS OFF) -SELECT 1 FROM tenk1_vw_sec - WHERE (SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100; - QUERY PLAN -------------------------------------------------------------------- - Subquery Scan on tenk1_vw_sec - Filter: ((SubPlan 1) < 100) - -> Gather - Workers Planned: 4 - -> Parallel Index Only Scan using tenk1_unique1 on tenk1 - SubPlan 1 - -> Aggregate - -> Seq Scan on int4_tbl - Filter: (f1 < tenk1_vw_sec.unique1) -(9 rows) - -rollback; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/write_parallel.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/write_parallel.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/write_parallel.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/write_parallel.out 2021-10-30 22:04:17 -0600 @@ -1,80 +1,2 @@ --- --- PARALLEL --- -begin; --- encourage use of parallel plans -set parallel_setup_cost=0; -set parallel_tuple_cost=0; -set min_parallel_table_scan_size=0; -set max_parallel_workers_per_gather=4; --- --- Test write operations that has an underlying query that is eligible --- for parallel plans --- -explain (costs off) create table parallel_write as - select length(stringu1) from tenk1 group by length(stringu1); - QUERY PLAN ---------------------------------------------------- - Finalize HashAggregate - Group Key: (length((stringu1)::text)) - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: length((stringu1)::text) - -> Parallel Seq Scan on tenk1 -(7 rows) - -create table parallel_write as - select length(stringu1) from tenk1 group by length(stringu1); -drop table parallel_write; -explain (costs off) select length(stringu1) into parallel_write - from tenk1 group by length(stringu1); - QUERY PLAN ---------------------------------------------------- - Finalize HashAggregate - Group Key: (length((stringu1)::text)) - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: length((stringu1)::text) - -> Parallel Seq Scan on tenk1 -(7 rows) - -select length(stringu1) into parallel_write - from tenk1 group by length(stringu1); -drop table parallel_write; -explain (costs off) create materialized view parallel_mat_view as - select length(stringu1) from tenk1 group by length(stringu1); - QUERY PLAN ---------------------------------------------------- - Finalize HashAggregate - Group Key: (length((stringu1)::text)) - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: length((stringu1)::text) - -> Parallel Seq Scan on tenk1 -(7 rows) - -create materialized view parallel_mat_view as - select length(stringu1) from tenk1 group by length(stringu1); -create unique index on parallel_mat_view(length); -refresh materialized view parallel_mat_view; -refresh materialized view concurrently parallel_mat_view; -drop materialized view parallel_mat_view; -prepare prep_stmt as select length(stringu1) from tenk1 group by length(stringu1); -explain (costs off) create table parallel_write as execute prep_stmt; - QUERY PLAN ---------------------------------------------------- - Finalize HashAggregate - Group Key: (length((stringu1)::text)) - -> Gather - Workers Planned: 4 - -> Partial HashAggregate - Group Key: length((stringu1)::text) - -> Parallel Seq Scan on tenk1 -(7 rows) - -create table parallel_write as execute prep_stmt; -drop table parallel_write; -rollback; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/publication.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/publication.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/publication.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/publication.out 2021-10-30 22:04:17 -0600 @@ -1,323 +1,2 @@ --- --- PUBLICATION --- -CREATE ROLE regress_publication_user LOGIN SUPERUSER; -CREATE ROLE regress_publication_user2; -CREATE ROLE regress_publication_user_dummy LOGIN NOSUPERUSER; -SET SESSION AUTHORIZATION 'regress_publication_user'; --- suppress warning that depends on wal_level -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub_default; -RESET client_min_messages; -COMMENT ON PUBLICATION testpub_default IS 'test publication'; -SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; - obj_description ------------------- - test publication -(1 row) - -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpib_ins_trunct WITH (publish = insert); -RESET client_min_messages; -ALTER PUBLICATION testpub_default SET (publish = update); --- error cases -CREATE PUBLICATION testpub_xxx WITH (foo); -ERROR: unrecognized publication parameter: "foo" -CREATE PUBLICATION testpub_xxx WITH (publish = 'cluster, vacuum'); -ERROR: unrecognized "publish" value: "cluster" -CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publish_via_partition_root = '0'); -ERROR: conflicting or redundant options -LINE 1: ...ub_xxx WITH (publish_via_partition_root = 'true', publish_vi... - ^ -\dRp - List of publications - Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------+--------------------------+------------+---------+---------+---------+-----------+---------- - testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f - testpub_default | regress_publication_user | f | f | t | f | f | f -(2 rows) - -ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete'); -\dRp - List of publications - Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------+--------------------------+------------+---------+---------+---------+-----------+---------- - testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f - testpub_default | regress_publication_user | f | t | t | t | f | f -(2 rows) - ---- adding tables -CREATE SCHEMA pub_test; -CREATE TABLE testpub_tbl1 (id serial primary key, data text); -CREATE TABLE pub_test.testpub_nopk (foo int, bar int); -CREATE VIEW testpub_view AS SELECT 1; -CREATE TABLE testpub_parted (a int) PARTITION BY LIST (a); -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub_foralltables FOR ALL TABLES WITH (publish = 'insert'); -RESET client_min_messages; -ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update'); -CREATE TABLE testpub_tbl2 (id serial primary key, data text); --- fail - can't add to for all tables publication -ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2; -ERROR: publication "testpub_foralltables" is defined as FOR ALL TABLES -DETAIL: Tables cannot be added to or dropped from FOR ALL TABLES publications. --- fail - can't drop from all tables publication -ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2; -ERROR: publication "testpub_foralltables" is defined as FOR ALL TABLES -DETAIL: Tables cannot be added to or dropped from FOR ALL TABLES publications. --- fail - can't add to for all tables publication -ALTER PUBLICATION testpub_foralltables SET TABLE pub_test.testpub_nopk; -ERROR: publication "testpub_foralltables" is defined as FOR ALL TABLES -DETAIL: Tables cannot be added to or dropped from FOR ALL TABLES publications. -SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_foralltables'; - pubname | puballtables -----------------------+-------------- - testpub_foralltables | t -(1 row) - -\d+ testpub_tbl2 - Table "public.testpub_tbl2" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | - data | text | | | | extended | | -Indexes: - "testpub_tbl2_pkey" PRIMARY KEY, btree (id) -Publications: - "testpub_foralltables" - -\dRp+ testpub_foralltables - Publication testpub_foralltables - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | t | t | t | f | f | f -(1 row) - -DROP TABLE testpub_tbl2; -DROP PUBLICATION testpub_foralltables; -CREATE TABLE testpub_tbl3 (a int); -CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3); -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3; -CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3; -RESET client_min_messages; -\dRp+ testpub3 - Publication testpub3 - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | t | f -Tables: - "public.testpub_tbl3" - "public.testpub_tbl3a" - -\dRp+ testpub4 - Publication testpub4 - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | t | f -Tables: - "public.testpub_tbl3" - -DROP TABLE testpub_tbl3, testpub_tbl3a; -DROP PUBLICATION testpub3, testpub4; --- Tests for partitioned tables -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub_forparted; -CREATE PUBLICATION testpub_forparted1; -RESET client_min_messages; -CREATE TABLE testpub_parted1 (LIKE testpub_parted); -CREATE TABLE testpub_parted2 (LIKE testpub_parted); -ALTER PUBLICATION testpub_forparted1 SET (publish='insert'); -ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted1 FOR VALUES IN (1); -ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted2 FOR VALUES IN (2); --- works despite missing REPLICA IDENTITY, because updates are not replicated -UPDATE testpub_parted1 SET a = 1; --- only parent is listed as being in publication, not the partition -ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted; -\dRp+ testpub_forparted - Publication testpub_forparted - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | t | f -Tables: - "public.testpub_parted" - --- should now fail, because parent's publication replicates updates -UPDATE testpub_parted1 SET a = 1; -ERROR: cannot update table "testpub_parted1" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1; --- works again, because parent's publication is no longer considered -UPDATE testpub_parted1 SET a = 1; -ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true); -\dRp+ testpub_forparted - Publication testpub_forparted - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | t | t -Tables: - "public.testpub_parted" - --- still fail, because parent's publication replicates updates -UPDATE testpub_parted2 SET a = 2; -ERROR: cannot update table "testpub_parted2" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -ALTER PUBLICATION testpub_forparted DROP TABLE testpub_parted; --- works again, because update is no longer replicated -UPDATE testpub_parted2 SET a = 2; -DROP TABLE testpub_parted1, testpub_parted2; -DROP PUBLICATION testpub_forparted, testpub_forparted1; --- Test cache invalidation FOR ALL TABLES publication -SET client_min_messages = 'ERROR'; -CREATE TABLE testpub_tbl4(a int); -INSERT INTO testpub_tbl4 values(1); -UPDATE testpub_tbl4 set a = 2; -CREATE PUBLICATION testpub_foralltables FOR ALL TABLES; -RESET client_min_messages; --- fail missing REPLICA IDENTITY -UPDATE testpub_tbl4 set a = 3; -ERROR: cannot update table "testpub_tbl4" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -DROP PUBLICATION testpub_foralltables; --- should pass after dropping the publication -UPDATE testpub_tbl4 set a = 3; -DROP TABLE testpub_tbl4; --- fail - view -CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; -ERROR: cannot add relation "testpub_view" to publication -DETAIL: This operation is not supported for views. -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; -RESET client_min_messages; --- fail - already added -ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; -ERROR: relation "testpub_tbl1" is already member of publication "testpub_fortbl" --- fail - already added -CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1; -ERROR: publication "testpub_fortbl" already exists -\dRp+ testpub_fortbl - Publication testpub_fortbl - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | t | f -Tables: - "pub_test.testpub_nopk" - "public.testpub_tbl1" - --- fail - view -ALTER PUBLICATION testpub_default ADD TABLE testpub_view; -ERROR: cannot add relation "testpub_view" to publication -DETAIL: This operation is not supported for views. -ALTER PUBLICATION testpub_default ADD TABLE testpub_tbl1; -ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1; -ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk; -ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1; -\d+ pub_test.testpub_nopk - Table "pub_test.testpub_nopk" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - foo | integer | | | | plain | | - bar | integer | | | | plain | | -Publications: - "testpib_ins_trunct" - "testpub_default" - "testpub_fortbl" - -\d+ testpub_tbl1 - Table "public.testpub_tbl1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | - data | text | | | | extended | | -Indexes: - "testpub_tbl1_pkey" PRIMARY KEY, btree (id) -Publications: - "testpib_ins_trunct" - "testpub_default" - "testpub_fortbl" - -\dRp+ testpub_default - Publication testpub_default - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | f | f -Tables: - "pub_test.testpub_nopk" - "public.testpub_tbl1" - -ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk; --- fail - nonexistent -ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk; -ERROR: relation "testpub_nopk" is not part of the publication -\d+ testpub_tbl1 - Table "public.testpub_tbl1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | - data | text | | | | extended | | -Indexes: - "testpub_tbl1_pkey" PRIMARY KEY, btree (id) -Publications: - "testpib_ins_trunct" - "testpub_fortbl" - --- permissions -SET ROLE regress_publication_user2; -CREATE PUBLICATION testpub2; -- fail -ERROR: permission denied for database regression -SET ROLE regress_publication_user; -GRANT CREATE ON DATABASE regression TO regress_publication_user2; -SET ROLE regress_publication_user2; -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION testpub2; -- ok -RESET client_min_messages; -ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- fail -ERROR: must be owner of table testpub_tbl1 -SET ROLE regress_publication_user; -GRANT regress_publication_user TO regress_publication_user2; -SET ROLE regress_publication_user2; -ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- ok -DROP PUBLICATION testpub2; -SET ROLE regress_publication_user; -REVOKE CREATE ON DATABASE regression FROM regress_publication_user2; -DROP TABLE testpub_parted; -DROP VIEW testpub_view; -DROP TABLE testpub_tbl1; -\dRp+ testpub_default - Publication testpub_default - Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ---------------------------+------------+---------+---------+---------+-----------+---------- - regress_publication_user | f | t | t | t | f | f -(1 row) - --- fail - must be owner of publication -SET ROLE regress_publication_user_dummy; -ALTER PUBLICATION testpub_default RENAME TO testpub_dummy; -ERROR: must be owner of publication testpub_default -RESET ROLE; -ALTER PUBLICATION testpub_default RENAME TO testpub_foo; -\dRp testpub_foo - List of publications - Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root --------------+--------------------------+------------+---------+---------+---------+-----------+---------- - testpub_foo | regress_publication_user | f | t | t | t | f | f -(1 row) - --- rename back to keep the rest simple -ALTER PUBLICATION testpub_foo RENAME TO testpub_default; -ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; -\dRp testpub_default - List of publications - Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root ------------------+---------------------------+------------+---------+---------+---------+-----------+---------- - testpub_default | regress_publication_user2 | f | t | t | t | f | f -(1 row) - -DROP PUBLICATION testpub_default; -DROP PUBLICATION testpib_ins_trunct; -DROP PUBLICATION testpub_fortbl; -DROP SCHEMA pub_test CASCADE; -NOTICE: drop cascades to table pub_test.testpub_nopk -RESET SESSION AUTHORIZATION; -DROP ROLE regress_publication_user, regress_publication_user2; -DROP ROLE regress_publication_user_dummy; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/subscription.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/subscription.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/subscription.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/subscription.out 2021-10-30 22:04:17 -0600 @@ -1,308 +1,2 @@ --- --- SUBSCRIPTION --- -CREATE ROLE regress_subscription_user LOGIN SUPERUSER; -CREATE ROLE regress_subscription_user2; -CREATE ROLE regress_subscription_user_dummy LOGIN NOSUPERUSER; -SET SESSION AUTHORIZATION 'regress_subscription_user'; --- fail - no publications -CREATE SUBSCRIPTION regress_testsub CONNECTION 'foo'; -ERROR: syntax error at or near ";" -LINE 1: CREATE SUBSCRIPTION regress_testsub CONNECTION 'foo'; - ^ --- fail - no connection -CREATE SUBSCRIPTION regress_testsub PUBLICATION foo; -ERROR: syntax error at or near "PUBLICATION" -LINE 1: CREATE SUBSCRIPTION regress_testsub PUBLICATION foo; - ^ --- fail - cannot do CREATE SUBSCRIPTION CREATE SLOT inside transaction block -BEGIN; -CREATE SUBSCRIPTION regress_testsub CONNECTION 'testconn' PUBLICATION testpub WITH (create_slot); -ERROR: CREATE SUBSCRIPTION ... WITH (create_slot = true) cannot run inside a transaction block -COMMIT; --- fail - invalid connection string -CREATE SUBSCRIPTION regress_testsub CONNECTION 'testconn' PUBLICATION testpub; -ERROR: invalid connection string syntax: missing "=" after "testconn" in connection info string - --- fail - duplicate publications -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION foo, testpub, foo WITH (connect = false); -ERROR: publication name "foo" used more than once --- ok -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -COMMENT ON SUBSCRIPTION regress_testsub IS 'test subscription'; -SELECT obj_description(s.oid, 'pg_subscription') FROM pg_subscription s; - obj_description -------------------- - test subscription -(1 row) - --- fail - name already exists -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); -ERROR: subscription "regress_testsub" already exists --- fail - must be superuser -SET SESSION AUTHORIZATION 'regress_subscription_user2'; -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION foo WITH (connect = false); -ERROR: must be superuser to create subscriptions -SET SESSION AUTHORIZATION 'regress_subscription_user'; --- fail - invalid option combinations -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, copy_data = true); -ERROR: connect = false and copy_data = true are mutually exclusive options -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, enabled = true); -ERROR: connect = false and enabled = true are mutually exclusive options -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, create_slot = true); -ERROR: connect = false and create_slot = true are mutually exclusive options -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = true); -ERROR: slot_name = NONE and enabled = true are mutually exclusive options -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, create_slot = true); -ERROR: slot_name = NONE and create_slot = true are mutually exclusive options -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE); -ERROR: subscription with slot_name = NONE must also set enabled = false -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = false); -ERROR: subscription with slot_name = NONE must also set create_slot = false -CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, create_slot = false); -ERROR: subscription with slot_name = NONE must also set enabled = false --- ok - with slot_name = NONE -CREATE SUBSCRIPTION regress_testsub3 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, connect = false); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables --- fail -ALTER SUBSCRIPTION regress_testsub3 ENABLE; -ERROR: cannot enable subscription that does not have a slot name -ALTER SUBSCRIPTION regress_testsub3 REFRESH PUBLICATION; -ERROR: ALTER SUBSCRIPTION ... REFRESH is not allowed for disabled subscriptions -DROP SUBSCRIPTION regress_testsub3; --- fail - invalid connection string -ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar'; -ERROR: invalid connection string syntax: missing "=" after "foobar" in connection info string - -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | f | d | off | dbname=regress_doesnotexist -(1 row) - -ALTER SUBSCRIPTION regress_testsub SET PUBLICATION testpub2, testpub3 WITH (refresh = false); -ALTER SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist2'; -ALTER SUBSCRIPTION regress_testsub SET (slot_name = 'newname'); --- fail -ALTER SUBSCRIPTION regress_testsub SET (slot_name = ''); -ERROR: replication slot name "" is too short --- fail -ALTER SUBSCRIPTION regress_doesnotexist CONNECTION 'dbname=regress_doesnotexist2'; -ERROR: subscription "regress_doesnotexist" does not exist -ALTER SUBSCRIPTION regress_testsub SET (create_slot = false); -ERROR: unrecognized subscription parameter: "create_slot" -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+---------------------+--------+-----------+------------------+--------------------+------------------------------ - regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | f | d | off | dbname=regress_doesnotexist2 -(1 row) - -BEGIN; -ALTER SUBSCRIPTION regress_testsub ENABLE; -\dRs - List of subscriptions - Name | Owner | Enabled | Publication ------------------+---------------------------+---------+--------------------- - regress_testsub | regress_subscription_user | t | {testpub2,testpub3} -(1 row) - -ALTER SUBSCRIPTION regress_testsub DISABLE; -\dRs - List of subscriptions - Name | Owner | Enabled | Publication ------------------+---------------------------+---------+--------------------- - regress_testsub | regress_subscription_user | f | {testpub2,testpub3} -(1 row) - -COMMIT; --- fail - must be owner of subscription -SET ROLE regress_subscription_user_dummy; -ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub_dummy; -ERROR: must be owner of subscription regress_testsub -RESET ROLE; -ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub_foo; -ALTER SUBSCRIPTION regress_testsub_foo SET (synchronous_commit = local); -ALTER SUBSCRIPTION regress_testsub_foo SET (synchronous_commit = foobar); -ERROR: invalid value for parameter "synchronous_commit": "foobar" -HINT: Available values: local, remote_write, remote_apply, on, off. -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ----------------------+---------------------------+---------+---------------------+--------+-----------+------------------+--------------------+------------------------------ - regress_testsub_foo | regress_subscription_user | f | {testpub2,testpub3} | f | f | d | local | dbname=regress_doesnotexist2 -(1 row) - --- rename back to keep the rest simple -ALTER SUBSCRIPTION regress_testsub_foo RENAME TO regress_testsub; --- fail - new owner must be superuser -ALTER SUBSCRIPTION regress_testsub OWNER TO regress_subscription_user2; -ERROR: permission denied to change owner of subscription "regress_testsub" -HINT: The owner of a subscription must be a superuser. -ALTER ROLE regress_subscription_user2 SUPERUSER; --- now it works -ALTER SUBSCRIPTION regress_testsub OWNER TO regress_subscription_user2; --- fail - cannot do DROP SUBSCRIPTION inside transaction block with slot name -BEGIN; -DROP SUBSCRIPTION regress_testsub; -ERROR: DROP SUBSCRIPTION cannot run inside a transaction block -COMMIT; -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); --- now it works -BEGIN; -DROP SUBSCRIPTION regress_testsub; -COMMIT; -DROP SUBSCRIPTION IF EXISTS regress_testsub; -NOTICE: subscription "regress_testsub" does not exist, skipping -DROP SUBSCRIPTION regress_testsub; -- fail -ERROR: subscription "regress_testsub" does not exist --- fail - binary must be boolean -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, binary = foo); -ERROR: binary requires a Boolean value --- now it works -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, binary = true); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | t | f | d | off | dbname=regress_doesnotexist -(1 row) - -ALTER SUBSCRIPTION regress_testsub SET (binary = false); -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | f | d | off | dbname=regress_doesnotexist -(1 row) - -DROP SUBSCRIPTION regress_testsub; --- fail - streaming must be boolean -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, streaming = foo); -ERROR: streaming requires a Boolean value --- now it works -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, streaming = true); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | t | d | off | dbname=regress_doesnotexist -(1 row) - -ALTER SUBSCRIPTION regress_testsub SET (streaming = false); -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | f | d | off | dbname=regress_doesnotexist -(1 row) - --- fail - publication already exists -ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub WITH (refresh = false); -ERROR: publication "testpub" is already in subscription "regress_testsub" --- fail - publication used more than once -ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub1 WITH (refresh = false); -ERROR: publication name "testpub1" used more than once --- ok - add two publications into subscription -ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refresh = false); --- fail - publications already exist -ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refresh = false); -ERROR: publication "testpub1" is already in subscription "regress_testsub" -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub,testpub1,testpub2} | f | f | d | off | dbname=regress_doesnotexist -(1 row) - --- fail - publication used more then once -ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub1, testpub1 WITH (refresh = false); -ERROR: publication name "testpub1" used more than once --- fail - all publications are deleted -ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub, testpub1, testpub2 WITH (refresh = false); -ERROR: cannot drop all the publications from a subscription --- fail - publication does not exist in subscription -ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub3 WITH (refresh = false); -ERROR: publication "testpub3" is not in subscription "regress_testsub" --- ok - delete publications -ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub1, testpub2 WITH (refresh = false); -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | f | d | off | dbname=regress_doesnotexist -(1 row) - -DROP SUBSCRIPTION regress_testsub; -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION mypub - WITH (connect = false, create_slot = false, copy_data = false); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -ALTER SUBSCRIPTION regress_testsub ENABLE; --- fail - ALTER SUBSCRIPTION with refresh is not allowed in a transaction --- block or function -BEGIN; -ALTER SUBSCRIPTION regress_testsub SET PUBLICATION mypub WITH (refresh = true); -ERROR: ALTER SUBSCRIPTION with refresh cannot run inside a transaction block -END; -BEGIN; -ALTER SUBSCRIPTION regress_testsub REFRESH PUBLICATION; -ERROR: ALTER SUBSCRIPTION ... REFRESH cannot run inside a transaction block -END; -CREATE FUNCTION func() RETURNS VOID AS -$$ ALTER SUBSCRIPTION regress_testsub SET PUBLICATION mypub WITH (refresh = true) $$ LANGUAGE SQL; -SELECT func(); -ERROR: ALTER SUBSCRIPTION with refresh cannot be executed from a function -CONTEXT: SQL function "func" statement 1 -ALTER SUBSCRIPTION regress_testsub DISABLE; -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -DROP SUBSCRIPTION regress_testsub; -DROP FUNCTION func; --- fail - two_phase must be boolean -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, two_phase = foo); -ERROR: two_phase requires a Boolean value --- now it works -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, two_phase = true); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | f | p | off | dbname=regress_doesnotexist -(1 row) - ---fail - alter of two_phase option not supported. -ALTER SUBSCRIPTION regress_testsub SET (two_phase = false); -ERROR: unrecognized subscription parameter: "two_phase" --- but can alter streaming when two_phase enabled -ALTER SUBSCRIPTION regress_testsub SET (streaming = true); -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | t | p | off | dbname=regress_doesnotexist -(1 row) - -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -DROP SUBSCRIPTION regress_testsub; --- two_phase and streaming are compatible. -CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, streaming = true, two_phase = true); -WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables -\dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two phase commit | Synchronous commit | Conninfo ------------------+---------------------------+---------+-------------+--------+-----------+------------------+--------------------+----------------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | t | p | off | dbname=regress_doesnotexist -(1 row) - -ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -DROP SUBSCRIPTION regress_testsub; -RESET SESSION AUTHORIZATION; -DROP ROLE regress_subscription_user; -DROP ROLE regress_subscription_user2; -DROP ROLE regress_subscription_user_dummy; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/plancache.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/plancache.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/plancache.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/plancache.out 2021-10-30 22:09:24 -0600 @@ -234,167 +234,10 @@ end$$ language plpgsql; select cachebug(); NOTICE: table "temptable" does not exist, skipping -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 - cachebug ----------- - -(1 row) - -select cachebug(); -NOTICE: drop cascades to view vv -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 - cachebug ----------- - -(1 row) - --- Check that addition or removal of any partition is correctly dealt with by --- default partition table when it is being used in prepared statement. -create table pc_list_parted (a int) partition by list(a); -create table pc_list_part_null partition of pc_list_parted for values in (null); -create table pc_list_part_1 partition of pc_list_parted for values in (1); -create table pc_list_part_def partition of pc_list_parted default; -prepare pstmt_def_insert (int) as insert into pc_list_part_def values($1); --- should fail -execute pstmt_def_insert(null); -ERROR: new row for relation "pc_list_part_def" violates partition constraint -DETAIL: Failing row contains (null). -execute pstmt_def_insert(1); -ERROR: new row for relation "pc_list_part_def" violates partition constraint -DETAIL: Failing row contains (1). -create table pc_list_part_2 partition of pc_list_parted for values in (2); -execute pstmt_def_insert(2); -ERROR: new row for relation "pc_list_part_def" violates partition constraint -DETAIL: Failing row contains (2). -alter table pc_list_parted detach partition pc_list_part_null; --- should be ok -execute pstmt_def_insert(null); -drop table pc_list_part_1; --- should be ok -execute pstmt_def_insert(1); -drop table pc_list_parted, pc_list_part_null; -deallocate pstmt_def_insert; --- Test plan_cache_mode -create table test_mode (a int); -insert into test_mode select 1 from generate_series(1,1000) union all select 2; -create index on test_mode (a); -analyze test_mode; -prepare test_mode_pp (int) as select count(*) from test_mode where a = $1; -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 0 | 0 -(1 row) - --- up to 5 executions, custom plan is used -set plan_cache_mode to auto; -explain (costs off) execute test_mode_pp(2); - QUERY PLAN ----------------------------------------------------------- - Aggregate - -> Index Only Scan using test_mode_a_idx on test_mode - Index Cond: (a = 2) -(3 rows) - -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 0 | 1 -(1 row) - --- force generic plan -set plan_cache_mode to force_generic_plan; -explain (costs off) execute test_mode_pp(2); - QUERY PLAN ------------------------------ - Aggregate - -> Seq Scan on test_mode - Filter: (a = $1) -(3 rows) - -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 1 | 1 -(1 row) - --- get to generic plan by 5 executions -set plan_cache_mode to auto; -execute test_mode_pp(1); -- 1x - count -------- - 1000 -(1 row) - -execute test_mode_pp(1); -- 2x - count -------- - 1000 -(1 row) - -execute test_mode_pp(1); -- 3x - count -------- - 1000 -(1 row) - -execute test_mode_pp(1); -- 4x - count -------- - 1000 -(1 row) - -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 1 | 5 -(1 row) - -execute test_mode_pp(1); -- 5x - count -------- - 1000 -(1 row) - -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 2 | 5 -(1 row) - --- we should now get a really bad plan -explain (costs off) execute test_mode_pp(2); - QUERY PLAN ------------------------------ - Aggregate - -> Seq Scan on test_mode - Filter: (a = $1) -(3 rows) - --- but we can force a custom plan -set plan_cache_mode to force_custom_plan; -explain (costs off) execute test_mode_pp(2); - QUERY PLAN ----------------------------------------------------------- - Aggregate - -> Index Only Scan using test_mode_a_idx on test_mode - Index Cond: (a = 2) -(3 rows) - -select name, generic_plans, custom_plans from pg_prepared_statements - where name = 'test_mode_pp'; - name | generic_plans | custom_plans ---------------+---------------+-------------- - test_mode_pp | 3 | 6 -(1 row) - -drop table test_mode; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/limit.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/limit.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/limit.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/limit.out 2021-10-30 22:09:24 -0600 @@ -26,669 +26,10 @@ SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 60 AND unique1 < 63 ORDER BY unique1 LIMIT 5; - two | unique1 | unique2 | stringu1 ------+---------+---------+---------- - | 61 | 560 | JCAAAA - | 62 | 633 | KCAAAA -(2 rows) - -SELECT ''::text AS three, unique1, unique2, stringu1 - FROM onek WHERE unique1 > 100 - ORDER BY unique1 LIMIT 3 OFFSET 20; - three | unique1 | unique2 | stringu1 --------+---------+---------+---------- - | 121 | 700 | REAAAA - | 122 | 519 | SEAAAA - | 123 | 777 | TEAAAA -(3 rows) - -SELECT ''::text AS zero, unique1, unique2, stringu1 - FROM onek WHERE unique1 < 50 - ORDER BY unique1 DESC LIMIT 8 OFFSET 99; - zero | unique1 | unique2 | stringu1 -------+---------+---------+---------- -(0 rows) - -SELECT ''::text AS eleven, unique1, unique2, stringu1 - FROM onek WHERE unique1 < 50 - ORDER BY unique1 DESC LIMIT 20 OFFSET 39; - eleven | unique1 | unique2 | stringu1 ---------+---------+---------+---------- - | 10 | 520 | KAAAAA - | 9 | 49 | JAAAAA - | 8 | 653 | IAAAAA - | 7 | 647 | HAAAAA - | 6 | 978 | GAAAAA - | 5 | 541 | FAAAAA - | 4 | 833 | EAAAAA - | 3 | 431 | DAAAAA - | 2 | 326 | CAAAAA - | 1 | 214 | BAAAAA - | 0 | 998 | AAAAAA -(11 rows) - -SELECT ''::text AS ten, unique1, unique2, stringu1 - FROM onek - ORDER BY unique1 OFFSET 990; - ten | unique1 | unique2 | stringu1 ------+---------+---------+---------- - | 990 | 369 | CMAAAA - | 991 | 426 | DMAAAA - | 992 | 363 | EMAAAA - | 993 | 661 | FMAAAA - | 994 | 695 | GMAAAA - | 995 | 144 | HMAAAA - | 996 | 258 | IMAAAA - | 997 | 21 | JMAAAA - | 998 | 549 | KMAAAA - | 999 | 152 | LMAAAA -(10 rows) - -SELECT ''::text AS five, unique1, unique2, stringu1 - FROM onek - ORDER BY unique1 OFFSET 990 LIMIT 5; - five | unique1 | unique2 | stringu1 -------+---------+---------+---------- - | 990 | 369 | CMAAAA - | 991 | 426 | DMAAAA - | 992 | 363 | EMAAAA - | 993 | 661 | FMAAAA - | 994 | 695 | GMAAAA -(5 rows) - -SELECT ''::text AS five, unique1, unique2, stringu1 - FROM onek - ORDER BY unique1 LIMIT 5 OFFSET 900; - five | unique1 | unique2 | stringu1 -------+---------+---------+---------- - | 900 | 913 | QIAAAA - | 901 | 931 | RIAAAA - | 902 | 702 | SIAAAA - | 903 | 641 | TIAAAA - | 904 | 793 | UIAAAA -(5 rows) - --- Test null limit and offset. The planner would discard a simple null --- constant, so to ensure executor is exercised, do this: -select * from int8_tbl limit (case when random() < 0.5 then null::bigint end); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - -select * from int8_tbl offset (case when random() < 0.5 then null::bigint end); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - --- Test assorted cases involving backwards fetch from a LIMIT plan node -begin; -declare c1 cursor for select * from int8_tbl limit 10; -fetch all in c1; - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - -fetch 1 in c1; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c1; - q1 | q2 -------------------+------------------- - 4567890123456789 | -4567890123456789 -(1 row) - -fetch backward all in c1; - q1 | q2 -------------------+------------------ - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 123 | 4567890123456789 - 123 | 456 -(4 rows) - -fetch backward 1 in c1; - q1 | q2 -----+---- -(0 rows) - -fetch all in c1; - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - -declare c2 cursor for select * from int8_tbl limit 3; -fetch all in c2; - q1 | q2 -------------------+------------------ - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 -(3 rows) - -fetch 1 in c2; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c2; - q1 | q2 -------------------+----- - 4567890123456789 | 123 -(1 row) - -fetch backward all in c2; - q1 | q2 ------+------------------ - 123 | 4567890123456789 - 123 | 456 -(2 rows) - -fetch backward 1 in c2; - q1 | q2 -----+---- -(0 rows) - -fetch all in c2; - q1 | q2 -------------------+------------------ - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 -(3 rows) - -declare c3 cursor for select * from int8_tbl offset 3; -fetch all in c3; - q1 | q2 -------------------+------------------- - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(2 rows) - -fetch 1 in c3; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c3; - q1 | q2 -------------------+------------------- - 4567890123456789 | -4567890123456789 -(1 row) - -fetch backward all in c3; - q1 | q2 -------------------+------------------ - 4567890123456789 | 4567890123456789 -(1 row) - -fetch backward 1 in c3; - q1 | q2 -----+---- -(0 rows) - -fetch all in c3; - q1 | q2 -------------------+------------------- - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(2 rows) - -declare c4 cursor for select * from int8_tbl offset 10; -fetch all in c4; - q1 | q2 -----+---- -(0 rows) - -fetch 1 in c4; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c4; - q1 | q2 -----+---- -(0 rows) - -fetch backward all in c4; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c4; - q1 | q2 -----+---- -(0 rows) - -fetch all in c4; - q1 | q2 -----+---- -(0 rows) - -declare c5 cursor for select * from int8_tbl order by q1 fetch first 2 rows with ties; -fetch all in c5; - q1 | q2 ------+------------------ - 123 | 456 - 123 | 4567890123456789 -(2 rows) - -fetch 1 in c5; - q1 | q2 -----+---- -(0 rows) - -fetch backward 1 in c5; - q1 | q2 ------+------------------ - 123 | 4567890123456789 -(1 row) - -fetch backward 1 in c5; - q1 | q2 ------+----- - 123 | 456 -(1 row) - -fetch all in c5; - q1 | q2 ------+------------------ - 123 | 4567890123456789 -(1 row) - -fetch backward all in c5; - q1 | q2 ------+------------------ - 123 | 4567890123456789 - 123 | 456 -(2 rows) - -fetch all in c5; - q1 | q2 ------+------------------ - 123 | 456 - 123 | 4567890123456789 -(2 rows) - -fetch backward all in c5; - q1 | q2 ------+------------------ - 123 | 4567890123456789 - 123 | 456 -(2 rows) - -rollback; --- Stress test for variable LIMIT in conjunction with bounded-heap sorting -SELECT - (SELECT n - FROM (VALUES (1)) AS x, - (SELECT n FROM generate_series(1,10) AS n - ORDER BY n LIMIT 1 OFFSET s-1) AS y) AS z - FROM generate_series(1,10) AS s; - z ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - --- --- Test behavior of volatile and set-returning functions in conjunction --- with ORDER BY and LIMIT. --- -create temp sequence testseq; -explain (verbose, costs off) -select unique1, unique2, nextval('testseq') - from tenk1 order by unique2 limit 10; - QUERY PLAN ----------------------------------------------------------------- - Limit - Output: unique1, unique2, (nextval('testseq'::regclass)) - -> Index Scan using tenk1_unique2 on public.tenk1 - Output: unique1, unique2, nextval('testseq'::regclass) -(4 rows) - -select unique1, unique2, nextval('testseq') - from tenk1 order by unique2 limit 10; - unique1 | unique2 | nextval ----------+---------+--------- - 8800 | 0 | 1 - 1891 | 1 | 2 - 3420 | 2 | 3 - 9850 | 3 | 4 - 7164 | 4 | 5 - 8009 | 5 | 6 - 5057 | 6 | 7 - 6701 | 7 | 8 - 4321 | 8 | 9 - 3043 | 9 | 10 -(10 rows) - -select currval('testseq'); - currval ---------- - 10 -(1 row) - -explain (verbose, costs off) -select unique1, unique2, nextval('testseq') - from tenk1 order by tenthous limit 10; - QUERY PLAN --------------------------------------------------------------------------- - Limit - Output: unique1, unique2, (nextval('testseq'::regclass)), tenthous - -> Result - Output: unique1, unique2, nextval('testseq'::regclass), tenthous - -> Sort - Output: unique1, unique2, tenthous - Sort Key: tenk1.tenthous - -> Seq Scan on public.tenk1 - Output: unique1, unique2, tenthous -(9 rows) - -select unique1, unique2, nextval('testseq') - from tenk1 order by tenthous limit 10; - unique1 | unique2 | nextval ----------+---------+--------- - 0 | 9998 | 11 - 1 | 2838 | 12 - 2 | 2716 | 13 - 3 | 5679 | 14 - 4 | 1621 | 15 - 5 | 5557 | 16 - 6 | 2855 | 17 - 7 | 8518 | 18 - 8 | 5435 | 19 - 9 | 4463 | 20 -(10 rows) - -select currval('testseq'); - currval ---------- - 20 -(1 row) - -explain (verbose, costs off) -select unique1, unique2, generate_series(1,10) - from tenk1 order by unique2 limit 7; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------- - Limit - Output: unique1, unique2, (generate_series(1, 10)) - -> ProjectSet - Output: unique1, unique2, generate_series(1, 10) - -> Index Scan using tenk1_unique2 on public.tenk1 - Output: unique1, unique2, two, four, ten, twenty, hundred, thousand, twothousand, fivethous, tenthous, odd, even, stringu1, stringu2, string4 -(6 rows) - -select unique1, unique2, generate_series(1,10) - from tenk1 order by unique2 limit 7; - unique1 | unique2 | generate_series ----------+---------+----------------- - 8800 | 0 | 1 - 8800 | 0 | 2 - 8800 | 0 | 3 - 8800 | 0 | 4 - 8800 | 0 | 5 - 8800 | 0 | 6 - 8800 | 0 | 7 -(7 rows) - -explain (verbose, costs off) -select unique1, unique2, generate_series(1,10) - from tenk1 order by tenthous limit 7; - QUERY PLAN --------------------------------------------------------------------- - Limit - Output: unique1, unique2, (generate_series(1, 10)), tenthous - -> ProjectSet - Output: unique1, unique2, generate_series(1, 10), tenthous - -> Sort - Output: unique1, unique2, tenthous - Sort Key: tenk1.tenthous - -> Seq Scan on public.tenk1 - Output: unique1, unique2, tenthous -(9 rows) - -select unique1, unique2, generate_series(1,10) - from tenk1 order by tenthous limit 7; - unique1 | unique2 | generate_series ----------+---------+----------------- - 0 | 9998 | 1 - 0 | 9998 | 2 - 0 | 9998 | 3 - 0 | 9998 | 4 - 0 | 9998 | 5 - 0 | 9998 | 6 - 0 | 9998 | 7 -(7 rows) - --- use of random() is to keep planner from folding the expressions together -explain (verbose, costs off) -select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - ProjectSet - Output: generate_series(0, 2), generate_series(((random() * '0.1'::double precision))::integer, 2) - -> Result -(3 rows) - -select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2; - s1 | s2 -----+---- - 0 | 0 - 1 | 1 - 2 | 2 -(3 rows) - -explain (verbose, costs off) -select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2 -order by s2 desc; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Sort - Output: (generate_series(0, 2)), (generate_series(((random() * '0.1'::double precision))::integer, 2)) - Sort Key: (generate_series(((random() * '0.1'::double precision))::integer, 2)) DESC - -> ProjectSet - Output: generate_series(0, 2), generate_series(((random() * '0.1'::double precision))::integer, 2) - -> Result -(6 rows) - -select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2 -order by s2 desc; - s1 | s2 -----+---- - 2 | 2 - 1 | 1 - 0 | 0 -(3 rows) - --- test for failure to set all aggregates' aggtranstype -explain (verbose, costs off) -select sum(tenthous) as s1, sum(tenthous) + random()*0 as s2 - from tenk1 group by thousand order by thousand limit 3; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------- - Limit - Output: (sum(tenthous)), (((sum(tenthous))::double precision + (random() * '0'::double precision))), thousand - -> GroupAggregate - Output: sum(tenthous), ((sum(tenthous))::double precision + (random() * '0'::double precision)), thousand - Group Key: tenk1.thousand - -> Index Only Scan using tenk1_thous_tenthous on public.tenk1 - Output: thousand, tenthous -(7 rows) - -select sum(tenthous) as s1, sum(tenthous) + random()*0 as s2 - from tenk1 group by thousand order by thousand limit 3; - s1 | s2 --------+------- - 45000 | 45000 - 45010 | 45010 - 45020 | 45020 -(3 rows) - --- --- FETCH FIRST --- Check the WITH TIES clause --- -SELECT thousand - FROM onek WHERE thousand < 5 - ORDER BY thousand FETCH FIRST 2 ROW WITH TIES; - thousand ----------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -(10 rows) - -SELECT thousand - FROM onek WHERE thousand < 5 - ORDER BY thousand FETCH FIRST ROWS WITH TIES; - thousand ----------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -(10 rows) - -SELECT thousand - FROM onek WHERE thousand < 5 - ORDER BY thousand FETCH FIRST 1 ROW WITH TIES; - thousand ----------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -(10 rows) - -SELECT thousand - FROM onek WHERE thousand < 5 - ORDER BY thousand FETCH FIRST 2 ROW ONLY; - thousand ----------- - 0 - 0 -(2 rows) - --- SKIP LOCKED and WITH TIES are incompatible -SELECT thousand - FROM onek WHERE thousand < 5 - ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED; -ERROR: SKIP LOCKED and WITH TIES options cannot be used together --- should fail -SELECT ''::text AS two, unique1, unique2, stringu1 - FROM onek WHERE unique1 > 50 - FETCH FIRST 2 ROW WITH TIES; -ERROR: WITH TIES cannot be specified without ORDER BY clause --- test ruleutils -CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand FETCH FIRST 5 ROWS WITH TIES OFFSET 10; -\d+ limit_thousand_v_1 - View "public.limit_thousand_v_1" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+---------+-----------+----------+---------+---------+------------- - thousand | integer | | | | plain | -View definition: - SELECT onek.thousand - FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand - OFFSET 10 - FETCH FIRST 5 ROWS WITH TIES; - -CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS ONLY; -\d+ limit_thousand_v_2 - View "public.limit_thousand_v_2" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+---------+-----------+----------+---------+---------+------------- - thousand | integer | | | | plain | -View definition: - SELECT onek.thousand - FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand - OFFSET 10 - LIMIT 5; - -CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand FETCH FIRST NULL ROWS WITH TIES; -- fails -ERROR: row count cannot be null in FETCH FIRST ... WITH TIES clause -CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand FETCH FIRST (NULL+1) ROWS WITH TIES; -\d+ limit_thousand_v_3 - View "public.limit_thousand_v_3" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+---------+-----------+----------+---------+---------+------------- - thousand | integer | | | | plain | -View definition: - SELECT onek.thousand - FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand - FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; - -CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand FETCH FIRST NULL ROWS ONLY; -\d+ limit_thousand_v_4 - View "public.limit_thousand_v_4" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+---------+-----------+----------+---------+---------+------------- - thousand | integer | | | | plain | -View definition: - SELECT onek.thousand - FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand - LIMIT ALL; - --- leave these views +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/plpgsql.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/plpgsql.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/plpgsql.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/plpgsql.out 2021-10-30 22:09:24 -0600 @@ -1014,4715 +1014,10 @@ insert into Room values ('001', 'Entrance'); insert into Room values ('002', 'Office'); insert into Room values ('003', 'Office'); -insert into Room values ('004', 'Technical'); -insert into Room values ('101', 'Office'); -insert into Room values ('102', 'Conference'); -insert into Room values ('103', 'Restroom'); -insert into Room values ('104', 'Technical'); -insert into Room values ('105', 'Office'); -insert into Room values ('106', 'Office'); --- --- Second we install the wall connectors --- -insert into WSlot values ('WS.001.1a', '001', '', ''); -insert into WSlot values ('WS.001.1b', '001', '', ''); -insert into WSlot values ('WS.001.2a', '001', '', ''); -insert into WSlot values ('WS.001.2b', '001', '', ''); -insert into WSlot values ('WS.001.3a', '001', '', ''); -insert into WSlot values ('WS.001.3b', '001', '', ''); -insert into WSlot values ('WS.002.1a', '002', '', ''); -insert into WSlot values ('WS.002.1b', '002', '', ''); -insert into WSlot values ('WS.002.2a', '002', '', ''); -insert into WSlot values ('WS.002.2b', '002', '', ''); -insert into WSlot values ('WS.002.3a', '002', '', ''); -insert into WSlot values ('WS.002.3b', '002', '', ''); -insert into WSlot values ('WS.003.1a', '003', '', ''); -insert into WSlot values ('WS.003.1b', '003', '', ''); -insert into WSlot values ('WS.003.2a', '003', '', ''); -insert into WSlot values ('WS.003.2b', '003', '', ''); -insert into WSlot values ('WS.003.3a', '003', '', ''); -insert into WSlot values ('WS.003.3b', '003', '', ''); -insert into WSlot values ('WS.101.1a', '101', '', ''); -insert into WSlot values ('WS.101.1b', '101', '', ''); -insert into WSlot values ('WS.101.2a', '101', '', ''); -insert into WSlot values ('WS.101.2b', '101', '', ''); -insert into WSlot values ('WS.101.3a', '101', '', ''); -insert into WSlot values ('WS.101.3b', '101', '', ''); -insert into WSlot values ('WS.102.1a', '102', '', ''); -insert into WSlot values ('WS.102.1b', '102', '', ''); -insert into WSlot values ('WS.102.2a', '102', '', ''); -insert into WSlot values ('WS.102.2b', '102', '', ''); -insert into WSlot values ('WS.102.3a', '102', '', ''); -insert into WSlot values ('WS.102.3b', '102', '', ''); -insert into WSlot values ('WS.105.1a', '105', '', ''); -insert into WSlot values ('WS.105.1b', '105', '', ''); -insert into WSlot values ('WS.105.2a', '105', '', ''); -insert into WSlot values ('WS.105.2b', '105', '', ''); -insert into WSlot values ('WS.105.3a', '105', '', ''); -insert into WSlot values ('WS.105.3b', '105', '', ''); -insert into WSlot values ('WS.106.1a', '106', '', ''); -insert into WSlot values ('WS.106.1b', '106', '', ''); -insert into WSlot values ('WS.106.2a', '106', '', ''); -insert into WSlot values ('WS.106.2b', '106', '', ''); -insert into WSlot values ('WS.106.3a', '106', '', ''); -insert into WSlot values ('WS.106.3b', '106', '', ''); --- --- Now create the patch fields and their slots --- -insert into PField values ('PF0_1', 'Wallslots basement'); --- --- The cables for these will be made later, so they are unconnected for now --- -insert into PSlot values ('PS.base.a1', 'PF0_1', '', ''); -insert into PSlot values ('PS.base.a2', 'PF0_1', '', ''); -insert into PSlot values ('PS.base.a3', 'PF0_1', '', ''); -insert into PSlot values ('PS.base.a4', 'PF0_1', '', ''); -insert into PSlot values ('PS.base.a5', 'PF0_1', '', ''); -insert into PSlot values ('PS.base.a6', 'PF0_1', '', ''); --- --- These are already wired to the wall connectors --- -insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a'); -insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b'); -insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a'); -insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b'); -insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a'); -insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b'); -insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a'); -insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b'); -insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a'); -insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b'); -insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a'); -insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b'); --- --- This patchfield will be renamed later into PF0_2 - so its --- slots references in pfname should follow --- -insert into PField values ('PF0_X', 'Phonelines basement'); -insert into PSlot values ('PS.base.ta1', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.ta2', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.ta3', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.ta4', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.ta5', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.ta6', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb1', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb2', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb3', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb4', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb5', 'PF0_X', '', ''); -insert into PSlot values ('PS.base.tb6', 'PF0_X', '', ''); -insert into PField values ('PF1_1', 'Wallslots first floor'); -insert into PSlot values ('PS.first.a1', 'PF1_1', '', 'WS.101.1a'); -insert into PSlot values ('PS.first.a2', 'PF1_1', '', 'WS.101.1b'); -insert into PSlot values ('PS.first.a3', 'PF1_1', '', 'WS.101.2a'); -insert into PSlot values ('PS.first.a4', 'PF1_1', '', 'WS.101.2b'); -insert into PSlot values ('PS.first.a5', 'PF1_1', '', 'WS.101.3a'); -insert into PSlot values ('PS.first.a6', 'PF1_1', '', 'WS.101.3b'); -insert into PSlot values ('PS.first.b1', 'PF1_1', '', 'WS.102.1a'); -insert into PSlot values ('PS.first.b2', 'PF1_1', '', 'WS.102.1b'); -insert into PSlot values ('PS.first.b3', 'PF1_1', '', 'WS.102.2a'); -insert into PSlot values ('PS.first.b4', 'PF1_1', '', 'WS.102.2b'); -insert into PSlot values ('PS.first.b5', 'PF1_1', '', 'WS.102.3a'); -insert into PSlot values ('PS.first.b6', 'PF1_1', '', 'WS.102.3b'); -insert into PSlot values ('PS.first.c1', 'PF1_1', '', 'WS.105.1a'); -insert into PSlot values ('PS.first.c2', 'PF1_1', '', 'WS.105.1b'); -insert into PSlot values ('PS.first.c3', 'PF1_1', '', 'WS.105.2a'); -insert into PSlot values ('PS.first.c4', 'PF1_1', '', 'WS.105.2b'); -insert into PSlot values ('PS.first.c5', 'PF1_1', '', 'WS.105.3a'); -insert into PSlot values ('PS.first.c6', 'PF1_1', '', 'WS.105.3b'); -insert into PSlot values ('PS.first.d1', 'PF1_1', '', 'WS.106.1a'); -insert into PSlot values ('PS.first.d2', 'PF1_1', '', 'WS.106.1b'); -insert into PSlot values ('PS.first.d3', 'PF1_1', '', 'WS.106.2a'); -insert into PSlot values ('PS.first.d4', 'PF1_1', '', 'WS.106.2b'); -insert into PSlot values ('PS.first.d5', 'PF1_1', '', 'WS.106.3a'); -insert into PSlot values ('PS.first.d6', 'PF1_1', '', 'WS.106.3b'); --- --- Now we wire the wall connectors 1a-2a in room 001 to the --- patchfield. In the second update we make an error, and --- correct it after --- -update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1'; -update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a3 - WS.001.2a | 001 | | - WS.001.2b | 001 | | - WS.001.3a | 001 | | - WS.001.3b | 001 | | -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | - PS.base.a3 | PF0_1 | | WS.001.1b - PS.base.a4 | PF0_1 | | - PS.base.a5 | PF0_1 | | - PS.base.a6 | PF0_1 | | -(6 rows) - -update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | - WS.001.3a | 001 | | - WS.001.3b | 001 | | -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | - PS.base.a5 | PF0_1 | | - PS.base.a6 | PF0_1 | | -(6 rows) - -update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a2 - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | - WS.001.3a | 001 | | - WS.001.3b | 001 | | -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | WS.001.1b - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | - PS.base.a5 | PF0_1 | | - PS.base.a6 | PF0_1 | | -(6 rows) - --- --- Same procedure for 2b-3b but this time updating the WSlot instead --- of the PSlot. Due to the triggers the result is the same: --- WSlot and corresponding PSlot point to each other. --- -update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b'; -update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a2 - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | PS.base.a4 - WS.001.3a | 001 | | PS.base.a6 - WS.001.3b | 001 | | -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | WS.001.1b - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | WS.001.2b - PS.base.a5 | PF0_1 | | - PS.base.a6 | PF0_1 | | WS.001.3a -(6 rows) - -update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a2 - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | PS.base.a4 - WS.001.3a | 001 | | - WS.001.3b | 001 | | PS.base.a6 -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | WS.001.1b - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | WS.001.2b - PS.base.a5 | PF0_1 | | - PS.base.a6 | PF0_1 | | WS.001.3b -(6 rows) - -update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a'; -select * from WSlot where roomno = '001' order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a2 - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | PS.base.a4 - WS.001.3a | 001 | | PS.base.a5 - WS.001.3b | 001 | | PS.base.a6 -(6 rows) - -select * from PSlot where slotname ~ 'PS.base.a' order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | WS.001.1b - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | WS.001.2b - PS.base.a5 | PF0_1 | | WS.001.3a - PS.base.a6 | PF0_1 | | WS.001.3b -(6 rows) - -insert into PField values ('PF1_2', 'Phonelines first floor'); -insert into PSlot values ('PS.first.ta1', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.ta2', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.ta3', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.ta4', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.ta5', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.ta6', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb1', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb2', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb3', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb4', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb5', 'PF1_2', '', ''); -insert into PSlot values ('PS.first.tb6', 'PF1_2', '', ''); --- --- Fix the wrong name for patchfield PF0_2 --- -update PField set name = 'PF0_2' where name = 'PF0_X'; -select * from PSlot order by slotname; - slotname | pfname | slotlink | backlink -----------------------+--------+----------------------+---------------------- - PS.base.a1 | PF0_1 | | WS.001.1a - PS.base.a2 | PF0_1 | | WS.001.1b - PS.base.a3 | PF0_1 | | WS.001.2a - PS.base.a4 | PF0_1 | | WS.001.2b - PS.base.a5 | PF0_1 | | WS.001.3a - PS.base.a6 | PF0_1 | | WS.001.3b - PS.base.b1 | PF0_1 | | WS.002.1a - PS.base.b2 | PF0_1 | | WS.002.1b - PS.base.b3 | PF0_1 | | WS.002.2a - PS.base.b4 | PF0_1 | | WS.002.2b - PS.base.b5 | PF0_1 | | WS.002.3a - PS.base.b6 | PF0_1 | | WS.002.3b - PS.base.c1 | PF0_1 | | WS.003.1a - PS.base.c2 | PF0_1 | | WS.003.1b - PS.base.c3 | PF0_1 | | WS.003.2a - PS.base.c4 | PF0_1 | | WS.003.2b - PS.base.c5 | PF0_1 | | WS.003.3a - PS.base.c6 | PF0_1 | | WS.003.3b - PS.base.ta1 | PF0_2 | | - PS.base.ta2 | PF0_2 | | - PS.base.ta3 | PF0_2 | | - PS.base.ta4 | PF0_2 | | - PS.base.ta5 | PF0_2 | | - PS.base.ta6 | PF0_2 | | - PS.base.tb1 | PF0_2 | | - PS.base.tb2 | PF0_2 | | - PS.base.tb3 | PF0_2 | | - PS.base.tb4 | PF0_2 | | - PS.base.tb5 | PF0_2 | | - PS.base.tb6 | PF0_2 | | - PS.first.a1 | PF1_1 | | WS.101.1a - PS.first.a2 | PF1_1 | | WS.101.1b - PS.first.a3 | PF1_1 | | WS.101.2a - PS.first.a4 | PF1_1 | | WS.101.2b - PS.first.a5 | PF1_1 | | WS.101.3a - PS.first.a6 | PF1_1 | | WS.101.3b - PS.first.b1 | PF1_1 | | WS.102.1a - PS.first.b2 | PF1_1 | | WS.102.1b - PS.first.b3 | PF1_1 | | WS.102.2a - PS.first.b4 | PF1_1 | | WS.102.2b - PS.first.b5 | PF1_1 | | WS.102.3a - PS.first.b6 | PF1_1 | | WS.102.3b - PS.first.c1 | PF1_1 | | WS.105.1a - PS.first.c2 | PF1_1 | | WS.105.1b - PS.first.c3 | PF1_1 | | WS.105.2a - PS.first.c4 | PF1_1 | | WS.105.2b - PS.first.c5 | PF1_1 | | WS.105.3a - PS.first.c6 | PF1_1 | | WS.105.3b - PS.first.d1 | PF1_1 | | WS.106.1a - PS.first.d2 | PF1_1 | | WS.106.1b - PS.first.d3 | PF1_1 | | WS.106.2a - PS.first.d4 | PF1_1 | | WS.106.2b - PS.first.d5 | PF1_1 | | WS.106.3a - PS.first.d6 | PF1_1 | | WS.106.3b - PS.first.ta1 | PF1_2 | | - PS.first.ta2 | PF1_2 | | - PS.first.ta3 | PF1_2 | | - PS.first.ta4 | PF1_2 | | - PS.first.ta5 | PF1_2 | | - PS.first.ta6 | PF1_2 | | - PS.first.tb1 | PF1_2 | | - PS.first.tb2 | PF1_2 | | - PS.first.tb3 | PF1_2 | | - PS.first.tb4 | PF1_2 | | - PS.first.tb5 | PF1_2 | | - PS.first.tb6 | PF1_2 | | -(66 rows) - -select * from WSlot order by slotname; - slotname | roomno | slotlink | backlink -----------------------+----------+----------------------+---------------------- - WS.001.1a | 001 | | PS.base.a1 - WS.001.1b | 001 | | PS.base.a2 - WS.001.2a | 001 | | PS.base.a3 - WS.001.2b | 001 | | PS.base.a4 - WS.001.3a | 001 | | PS.base.a5 - WS.001.3b | 001 | | PS.base.a6 - WS.002.1a | 002 | | PS.base.b1 - WS.002.1b | 002 | | PS.base.b2 - WS.002.2a | 002 | | PS.base.b3 - WS.002.2b | 002 | | PS.base.b4 - WS.002.3a | 002 | | PS.base.b5 - WS.002.3b | 002 | | PS.base.b6 - WS.003.1a | 003 | | PS.base.c1 - WS.003.1b | 003 | | PS.base.c2 - WS.003.2a | 003 | | PS.base.c3 - WS.003.2b | 003 | | PS.base.c4 - WS.003.3a | 003 | | PS.base.c5 - WS.003.3b | 003 | | PS.base.c6 - WS.101.1a | 101 | | PS.first.a1 - WS.101.1b | 101 | | PS.first.a2 - WS.101.2a | 101 | | PS.first.a3 - WS.101.2b | 101 | | PS.first.a4 - WS.101.3a | 101 | | PS.first.a5 - WS.101.3b | 101 | | PS.first.a6 - WS.102.1a | 102 | | PS.first.b1 - WS.102.1b | 102 | | PS.first.b2 - WS.102.2a | 102 | | PS.first.b3 - WS.102.2b | 102 | | PS.first.b4 - WS.102.3a | 102 | | PS.first.b5 - WS.102.3b | 102 | | PS.first.b6 - WS.105.1a | 105 | | PS.first.c1 - WS.105.1b | 105 | | PS.first.c2 - WS.105.2a | 105 | | PS.first.c3 - WS.105.2b | 105 | | PS.first.c4 - WS.105.3a | 105 | | PS.first.c5 - WS.105.3b | 105 | | PS.first.c6 - WS.106.1a | 106 | | PS.first.d1 - WS.106.1b | 106 | | PS.first.d2 - WS.106.2a | 106 | | PS.first.d3 - WS.106.2b | 106 | | PS.first.d4 - WS.106.3a | 106 | | PS.first.d5 - WS.106.3b | 106 | | PS.first.d6 -(42 rows) - --- --- Install the central phone system and create the phone numbers. --- They are wired on insert to the patchfields. Again the --- triggers automatically tell the PSlots to update their --- backlink field. --- -insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1'); -insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2'); -insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3'); -insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5'); -insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6'); -insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2'); -insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3'); -insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4'); -insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5'); -insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6'); -insert into PLine values ('PL.015', '-134', '', 'PS.first.ta1'); -insert into PLine values ('PL.016', '-137', '', 'PS.first.ta3'); -insert into PLine values ('PL.017', '-139', '', 'PS.first.ta4'); -insert into PLine values ('PL.018', '-362', '', 'PS.first.tb1'); -insert into PLine values ('PL.019', '-363', '', 'PS.first.tb2'); -insert into PLine values ('PL.020', '-364', '', 'PS.first.tb3'); -insert into PLine values ('PL.021', '-365', '', 'PS.first.tb5'); -insert into PLine values ('PL.022', '-367', '', 'PS.first.tb6'); -insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2'); -insert into PLine values ('PL.029', '-502', 'Fax first floor', 'PS.first.ta1'); --- --- Buy some phones, plug them into the wall and patch the --- phone lines to the corresponding patchfield slots. --- -insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a'); -update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1'; -insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a'); -update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1'; -insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a'); -update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3'; -insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a'); -update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3'; --- --- Install a hub at one of the patchfields, plug a computers --- ethernet interface into the wall and patch it to the hub. --- -insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16); -insert into System values ('orion', 'PC'); -insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b'); -update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2'; --- --- Now we take a look at the patchfield --- -select * from PField_v1 where pfname = 'PF0_1' order by slotname; - pfname | slotname | backside | patch ---------+----------------------+----------------------------------------------------------+----------------------------------------------- - PF0_1 | PS.base.a1 | WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard) | PS.base.ta1 -> Phone line -0 (Central call) - PF0_1 | PS.base.a2 | WS.001.1b in room 001 -> - | - - PF0_1 | PS.base.a3 | WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax) | PS.base.ta2 -> Phone line -501 (Fax entrance) - PF0_1 | PS.base.a4 | WS.001.2b in room 001 -> - | - - PF0_1 | PS.base.a5 | WS.001.3a in room 001 -> - | - - PF0_1 | PS.base.a6 | WS.001.3b in room 001 -> - | - - PF0_1 | PS.base.b1 | WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard) | PS.base.ta5 -> Phone line -103 - PF0_1 | PS.base.b2 | WS.002.1b in room 002 -> orion IF eth0 (PC) | Patchfield PF0_1 hub slot 1 - PF0_1 | PS.base.b3 | WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard) | PS.base.tb2 -> Phone line -106 - PF0_1 | PS.base.b4 | WS.002.2b in room 002 -> - | - - PF0_1 | PS.base.b5 | WS.002.3a in room 002 -> - | - - PF0_1 | PS.base.b6 | WS.002.3b in room 002 -> - | - - PF0_1 | PS.base.c1 | WS.003.1a in room 003 -> - | - - PF0_1 | PS.base.c2 | WS.003.1b in room 003 -> - | - - PF0_1 | PS.base.c3 | WS.003.2a in room 003 -> - | - - PF0_1 | PS.base.c4 | WS.003.2b in room 003 -> - | - - PF0_1 | PS.base.c5 | WS.003.3a in room 003 -> - | - - PF0_1 | PS.base.c6 | WS.003.3b in room 003 -> - | - -(18 rows) - -select * from PField_v1 where pfname = 'PF0_2' order by slotname; - pfname | slotname | backside | patch ---------+----------------------+--------------------------------+------------------------------------------------------------------------ - PF0_2 | PS.base.ta1 | Phone line -0 (Central call) | PS.base.a1 -> WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard) - PF0_2 | PS.base.ta2 | Phone line -501 (Fax entrance) | PS.base.a3 -> WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax) - PF0_2 | PS.base.ta3 | Phone line -102 | - - PF0_2 | PS.base.ta4 | - | - - PF0_2 | PS.base.ta5 | Phone line -103 | PS.base.b1 -> WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard) - PF0_2 | PS.base.ta6 | Phone line -104 | - - PF0_2 | PS.base.tb1 | - | - - PF0_2 | PS.base.tb2 | Phone line -106 | PS.base.b3 -> WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard) - PF0_2 | PS.base.tb3 | Phone line -108 | - - PF0_2 | PS.base.tb4 | Phone line -109 | - - PF0_2 | PS.base.tb5 | Phone line -121 | - - PF0_2 | PS.base.tb6 | Phone line -122 | - -(12 rows) - --- --- Finally we want errors --- -insert into PField values ('PF1_1', 'should fail due to unique index'); -ERROR: duplicate key value violates unique constraint "pfield_name" -DETAIL: Key (name)=(PF1_1) already exists. -update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1'; -ERROR: WS.not.there does not exist -CONTEXT: PL/pgSQL function tg_backlink_set(character,character) line 30 at RAISE -PL/pgSQL function tg_backlink_a() line 17 at assignment -update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1'; -ERROR: illegal backlink beginning with XX -CONTEXT: PL/pgSQL function tg_backlink_set(character,character) line 47 at RAISE -PL/pgSQL function tg_backlink_a() line 17 at assignment -update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1'; -ERROR: PS.not.there does not exist -CONTEXT: PL/pgSQL function tg_slotlink_set(character,character) line 30 at RAISE -PL/pgSQL function tg_slotlink_a() line 17 at assignment -update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1'; -ERROR: illegal slotlink beginning with XX -CONTEXT: PL/pgSQL function tg_slotlink_set(character,character) line 77 at RAISE -PL/pgSQL function tg_slotlink_a() line 17 at assignment -insert into HSlot values ('HS', 'base.hub1', 1, ''); -ERROR: duplicate key value violates unique constraint "hslot_name" -DETAIL: Key (slotname)=(HS.base.hub1.1 ) already exists. -insert into HSlot values ('HS', 'base.hub1', 20, ''); -ERROR: no manual manipulation of HSlot -CONTEXT: PL/pgSQL function tg_hslot_biu() line 12 at RAISE -delete from HSlot; -ERROR: no manual manipulation of HSlot -CONTEXT: PL/pgSQL function tg_hslot_bd() line 12 at RAISE -insert into IFace values ('IF', 'notthere', 'eth0', ''); -ERROR: system "notthere" does not exist -CONTEXT: PL/pgSQL function tg_iface_biu() line 8 at RAISE -insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', ''); -ERROR: IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max) -CONTEXT: PL/pgSQL function tg_iface_biu() line 14 at RAISE --- --- The following tests are unrelated to the scenario outlined above; --- they merely exercise specific parts of PL/pgSQL --- --- --- Test recursion, per bug report 7-Sep-01 --- -CREATE FUNCTION recursion_test(int,int) RETURNS text AS ' -DECLARE rslt text; -BEGIN - IF $1 <= 0 THEN - rslt = CAST($2 AS TEXT); - ELSE - rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2); - END IF; - RETURN rslt; -END;' LANGUAGE plpgsql; -SELECT recursion_test(4,3); - recursion_test ----------------- - 4,3,2,1,3 -(1 row) - --- --- Test the FOUND magic variable --- -CREATE TABLE found_test_tbl (a int); -create function test_found() - returns boolean as ' - declare - begin - insert into found_test_tbl values (1); - if FOUND then - insert into found_test_tbl values (2); - end if; - - update found_test_tbl set a = 100 where a = 1; - if FOUND then - insert into found_test_tbl values (3); - end if; - - delete from found_test_tbl where a = 9999; -- matches no rows - if not FOUND then - insert into found_test_tbl values (4); - end if; - - for i in 1 .. 10 loop - -- no need to do anything - end loop; - if FOUND then - insert into found_test_tbl values (5); - end if; - - -- never executes the loop - for i in 2 .. 1 loop - -- no need to do anything - end loop; - if not FOUND then - insert into found_test_tbl values (6); - end if; - return true; - end;' language plpgsql; -select test_found(); - test_found ------------- - t -(1 row) - -select * from found_test_tbl; - a ------ - 2 - 100 - 3 - 4 - 5 - 6 -(6 rows) - --- --- Test set-returning functions for PL/pgSQL --- -create function test_table_func_rec() returns setof found_test_tbl as ' -DECLARE - rec RECORD; -BEGIN - FOR rec IN select * from found_test_tbl LOOP - RETURN NEXT rec; - END LOOP; - RETURN; -END;' language plpgsql; -select * from test_table_func_rec(); - a ------ - 2 - 100 - 3 - 4 - 5 - 6 -(6 rows) - -create function test_table_func_row() returns setof found_test_tbl as ' -DECLARE - row found_test_tbl%ROWTYPE; -BEGIN - FOR row IN select * from found_test_tbl LOOP - RETURN NEXT row; - END LOOP; - RETURN; -END;' language plpgsql; -select * from test_table_func_row(); - a ------ - 2 - 100 - 3 - 4 - 5 - 6 -(6 rows) - -create function test_ret_set_scalar(int,int) returns setof int as ' -DECLARE - i int; -BEGIN - FOR i IN $1 .. $2 LOOP - RETURN NEXT i + 1; - END LOOP; - RETURN; -END;' language plpgsql; -select * from test_ret_set_scalar(1,10); - test_ret_set_scalar ---------------------- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 -(10 rows) - -create function test_ret_set_rec_dyn(int) returns setof record as ' -DECLARE - retval RECORD; -BEGIN - IF $1 > 10 THEN - SELECT INTO retval 5, 10, 15; - RETURN NEXT retval; - RETURN NEXT retval; - ELSE - SELECT INTO retval 50, 5::numeric, ''xxx''::text; - RETURN NEXT retval; - RETURN NEXT retval; - END IF; - RETURN; -END;' language plpgsql; -SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int); - a | b | c ----+----+---- - 5 | 10 | 15 - 5 | 10 | 15 -(2 rows) - -SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text); - a | b | c -----+---+----- - 50 | 5 | xxx - 50 | 5 | xxx -(2 rows) - -create function test_ret_rec_dyn(int) returns record as ' -DECLARE - retval RECORD; -BEGIN - IF $1 > 10 THEN - SELECT INTO retval 5, 10, 15; - RETURN retval; - ELSE - SELECT INTO retval 50, 5::numeric, ''xxx''::text; - RETURN retval; - END IF; -END;' language plpgsql; -SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int); - a | b | c ----+----+---- - 5 | 10 | 15 -(1 row) - -SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text); - a | b | c -----+---+----- - 50 | 5 | xxx -(1 row) - --- --- Test some simple polymorphism cases. --- -create function f1(x anyelement) returns anyelement as $$ -begin - return x + 1; -end$$ language plpgsql; -select f1(42) as int, f1(4.5) as num; - int | num ------+----- - 43 | 5.5 -(1 row) - -select f1(point(3,4)); -- fail for lack of + operator -ERROR: operator does not exist: point + integer -LINE 1: x + 1 - ^ -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -QUERY: x + 1 -CONTEXT: PL/pgSQL function f1(anyelement) line 3 at RETURN -drop function f1(x anyelement); -create function f1(x anyelement) returns anyarray as $$ -begin - return array[x + 1, x + 2]; -end$$ language plpgsql; -select f1(42) as int, f1(4.5) as num; - int | num ----------+----------- - {43,44} | {5.5,6.5} -(1 row) - -drop function f1(x anyelement); -create function f1(x anyarray) returns anyelement as $$ -begin - return x[1]; -end$$ language plpgsql; -select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num; - int | num ------+----- - 2 | 4.5 -(1 row) - -select f1(stavalues1) from pg_statistic; -- fail, can't infer element type -ERROR: cannot determine element type of "anyarray" argument -drop function f1(x anyarray); -create function f1(x anyarray) returns anyarray as $$ -begin - return x; -end$$ language plpgsql; -select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num; - int | num --------+----------- - {2,4} | {4.5,7.7} -(1 row) - -select f1(stavalues1) from pg_statistic; -- fail, can't infer element type -ERROR: PL/pgSQL functions cannot accept type anyarray -CONTEXT: compilation of PL/pgSQL function "f1" near line 1 -drop function f1(x anyarray); --- fail, can't infer type: -create function f1(x anyelement) returns anyrange as $$ -begin - return array[x + 1, x + 2]; -end$$ language plpgsql; -ERROR: cannot determine result data type -DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange. -create function f1(x anyrange) returns anyarray as $$ -begin - return array[lower(x), upper(x)]; -end$$ language plpgsql; -select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num; - int | num ----------+----------- - {42,49} | {4.5,7.8} -(1 row) - -drop function f1(x anyrange); -create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$ -begin - return array[x, y]; -end$$ language plpgsql; -select f1(2, 4) as int, f1(2, 4.5) as num; - int | num --------+--------- - {2,4} | {2,4.5} -(1 row) - -drop function f1(x anycompatible, y anycompatible); -create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$ -begin - return array[lower(x), upper(x), y, z]; -end$$ language plpgsql; -select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num; - int | num ---------------+------------------ - {42,49,11,2} | {4.5,7.8,7.8,11} -(1 row) - -select f1(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit -ERROR: function f1(int4range, integer, numeric) does not exist -LINE 1: select f1(int4range(42, 49), 11, 4.5) as fail; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function f1(x anycompatiblerange, y anycompatible, z anycompatible); --- fail, can't infer type: -create function f1(x anycompatible) returns anycompatiblerange as $$ -begin - return array[x + 1, x + 2]; -end$$ language plpgsql; -ERROR: cannot determine result data type -DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange. -create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$ -begin - return x; -end$$ language plpgsql; -select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num; - int | num ----------+----------- - [42,49) | [4.5,7.8) -(1 row) - -drop function f1(x anycompatiblerange, y anycompatiblearray); -create function f1(a anyelement, b anyarray, - c anycompatible, d anycompatible, - OUT x anyarray, OUT y anycompatiblearray) -as $$ -begin - x := a || b; - y := array[c, d]; -end$$ language plpgsql; -select x, pg_typeof(x), y, pg_typeof(y) - from f1(11, array[1, 2], 42, 34.5); - x | pg_typeof | y | pg_typeof -----------+-----------+-----------+----------- - {11,1,2} | integer[] | {42,34.5} | numeric[] -(1 row) - -select x, pg_typeof(x), y, pg_typeof(y) - from f1(11, array[1, 2], point(1,2), point(3,4)); - x | pg_typeof | y | pg_typeof -----------+-----------+-------------------+----------- - {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x), y, pg_typeof(y) - from f1(11, '{1,2}', point(1,2), '(3,4)'); - x | pg_typeof | y | pg_typeof -----------+-----------+-------------------+----------- - {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x), y, pg_typeof(y) - from f1(11, array[1, 2.2], 42, 34.5); -- fail -ERROR: function f1(integer, numeric[], integer, numeric) does not exist -LINE 2: from f1(11, array[1, 2.2], 42, 34.5); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function f1(a anyelement, b anyarray, - c anycompatible, d anycompatible); --- --- Test handling of OUT parameters, including polymorphic cases. --- Note that RETURN is optional with OUT params; we try both ways. --- --- wrong way to do it: -create function f1(in i int, out j int) returns int as $$ -begin - return i+1; -end$$ language plpgsql; -ERROR: RETURN cannot have a parameter in function with OUT parameters -LINE 3: return i+1; - ^ -create function f1(in i int, out j int) as $$ -begin - j := i+1; - return; -end$$ language plpgsql; -select f1(42); - f1 ----- - 43 -(1 row) - -select * from f1(42); - j ----- - 43 -(1 row) - -create or replace function f1(inout i int) as $$ -begin - i := i+1; -end$$ language plpgsql; -select f1(42); - f1 ----- - 43 -(1 row) - -select * from f1(42); - i ----- - 43 -(1 row) - -drop function f1(int); -create function f1(in i int, out j int) returns setof int as $$ -begin - j := i+1; - return next; - j := i+2; - return next; - return; -end$$ language plpgsql; -select * from f1(42); - j ----- - 43 - 44 -(2 rows) - -drop function f1(int); -create function f1(in i int, out j int, out k text) as $$ -begin - j := i; - j := j+1; - k := 'foo'; -end$$ language plpgsql; -select f1(42); - f1 ----------- - (43,foo) -(1 row) - -select * from f1(42); - j | k -----+----- - 43 | foo -(1 row) - -drop function f1(int); -create function f1(in i int, out j int, out k text) returns setof record as $$ -begin - j := i+1; - k := 'foo'; - return next; - j := j+1; - k := 'foot'; - return next; -end$$ language plpgsql; -select * from f1(42); - j | k -----+------ - 43 | foo - 44 | foot -(2 rows) - -drop function f1(int); -create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$ -begin - j := i; - k := array[j,j]; - return; -end$$ language plpgsql; -select * from duplic(42); - j | k -----+--------- - 42 | {42,42} -(1 row) - -select * from duplic('foo'::text); - j | k ------+----------- - foo | {foo,foo} -(1 row) - -drop function duplic(anyelement); -create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$ -begin - j := lower(i); - k := array[lower(i),upper(i)]; - return; -end$$ language plpgsql; -select * from duplic(int4range(42,49)); - j | k -----+--------- - 42 | {42,49} -(1 row) - -select * from duplic(textrange('aaa', 'bbb')); - j | k ------+----------- - aaa | {aaa,bbb} -(1 row) - -drop function duplic(anycompatiblerange); --- --- test PERFORM --- -create table perform_test ( - a INT, - b INT -); -create function perform_simple_func(int) returns boolean as ' -BEGIN - IF $1 < 20 THEN - INSERT INTO perform_test VALUES ($1, $1 + 10); - RETURN TRUE; - ELSE - RETURN FALSE; - END IF; -END;' language plpgsql; -create function perform_test_func() returns void as ' -BEGIN - IF FOUND then - INSERT INTO perform_test VALUES (100, 100); - END IF; - - PERFORM perform_simple_func(5); - - IF FOUND then - INSERT INTO perform_test VALUES (100, 100); - END IF; - - PERFORM perform_simple_func(50); - - IF FOUND then - INSERT INTO perform_test VALUES (100, 100); - END IF; - - RETURN; -END;' language plpgsql; -SELECT perform_test_func(); - perform_test_func -------------------- - -(1 row) - -SELECT * FROM perform_test; - a | b ------+----- - 5 | 15 - 100 | 100 - 100 | 100 -(3 rows) - -drop table perform_test; --- --- Test proper snapshot handling in simple expressions --- -create temp table users(login text, id serial); -create function sp_id_user(a_login text) returns int as $$ -declare x int; -begin - select into x id from users where login = a_login; - if found then return x; end if; - return 0; -end$$ language plpgsql stable; -insert into users values('user1'); -select sp_id_user('user1'); - sp_id_user ------------- - 1 -(1 row) - -select sp_id_user('userx'); - sp_id_user ------------- - 0 -(1 row) - -create function sp_add_user(a_login text) returns int as $$ -declare my_id_user int; -begin - my_id_user = sp_id_user( a_login ); - IF my_id_user > 0 THEN - RETURN -1; -- error code for existing user - END IF; - INSERT INTO users ( login ) VALUES ( a_login ); - my_id_user = sp_id_user( a_login ); - IF my_id_user = 0 THEN - RETURN -2; -- error code for insertion failure - END IF; - RETURN my_id_user; -end$$ language plpgsql; -select sp_add_user('user1'); - sp_add_user -------------- - -1 -(1 row) - -select sp_add_user('user2'); - sp_add_user -------------- - 2 -(1 row) - -select sp_add_user('user2'); - sp_add_user -------------- - -1 -(1 row) - -select sp_add_user('user3'); - sp_add_user -------------- - 3 -(1 row) - -select sp_add_user('user3'); - sp_add_user -------------- - -1 -(1 row) - -drop function sp_add_user(text); -drop function sp_id_user(text); --- --- tests for refcursors --- -create table rc_test (a int, b int); -copy rc_test from stdin; -create function return_unnamed_refcursor() returns refcursor as $$ -declare - rc refcursor; -begin - open rc for select a from rc_test; - return rc; -end -$$ language plpgsql; -create function use_refcursor(rc refcursor) returns int as $$ -declare - rc refcursor; - x record; -begin - rc := return_unnamed_refcursor(); - fetch next from rc into x; - return x.a; -end -$$ language plpgsql; -select use_refcursor(return_unnamed_refcursor()); - use_refcursor ---------------- - 5 -(1 row) - -create function return_refcursor(rc refcursor) returns refcursor as $$ -begin - open rc for select a from rc_test; - return rc; -end -$$ language plpgsql; -create function refcursor_test1(refcursor) returns refcursor as $$ -begin - perform return_refcursor($1); - return $1; -end -$$ language plpgsql; -begin; -select refcursor_test1('test1'); - refcursor_test1 ------------------ - test1 -(1 row) - -fetch next in test1; - a ---- - 5 -(1 row) - -select refcursor_test1('test2'); - refcursor_test1 ------------------ - test2 -(1 row) - -fetch all from test2; - a ------ - 5 - 50 - 500 -(3 rows) - -commit; --- should fail -fetch next from test1; -ERROR: cursor "test1" does not exist -create function refcursor_test2(int, int) returns boolean as $$ -declare - c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; - nonsense record; -begin - open c1($1, $2); - fetch c1 into nonsense; - close c1; - if found then - return true; - else - return false; - end if; -end -$$ language plpgsql; -select refcursor_test2(20000, 20000) as "Should be false", - refcursor_test2(20, 20) as "Should be true"; - Should be false | Should be true ------------------+---------------- - f | t -(1 row) - --- --- tests for cursors with named parameter arguments --- -create function namedparmcursor_test1(int, int) returns boolean as $$ -declare - c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12; - nonsense record; -begin - open c1(param12 := $2, param1 := $1); - fetch c1 into nonsense; - close c1; - if found then - return true; - else - return false; - end if; -end -$$ language plpgsql; -select namedparmcursor_test1(20000, 20000) as "Should be false", - namedparmcursor_test1(20, 20) as "Should be true"; - Should be false | Should be true ------------------+---------------- - f | t -(1 row) - --- mixing named and positional argument notations -create function namedparmcursor_test2(int, int) returns boolean as $$ -declare - c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; - nonsense record; -begin - open c1(param1 := $1, $2); - fetch c1 into nonsense; - close c1; - if found then - return true; - else - return false; - end if; -end -$$ language plpgsql; -select namedparmcursor_test2(20, 20); - namedparmcursor_test2 ------------------------ - t -(1 row) - --- mixing named and positional: param2 is given twice, once in named notation --- and second time in positional notation. Should throw an error at parse time -create function namedparmcursor_test3() returns void as $$ -declare - c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; -begin - open c1(param2 := 20, 21); -end -$$ language plpgsql; -ERROR: value for parameter "param2" of cursor "c1" specified more than once -LINE 5: open c1(param2 := 20, 21); - ^ --- mixing named and positional: same as previous test, but param1 is duplicated -create function namedparmcursor_test4() returns void as $$ -declare - c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; -begin - open c1(20, param1 := 21); -end -$$ language plpgsql; -ERROR: value for parameter "param1" of cursor "c1" specified more than once -LINE 5: open c1(20, param1 := 21); - ^ --- duplicate named parameter, should throw an error at parse time -create function namedparmcursor_test5() returns void as $$ -declare - c1 cursor (p1 int, p2 int) for - select * from tenk1 where thousand = p1 and tenthous = p2; -begin - open c1 (p2 := 77, p2 := 42); -end -$$ language plpgsql; -ERROR: value for parameter "p2" of cursor "c1" specified more than once -LINE 6: open c1 (p2 := 77, p2 := 42); - ^ --- not enough parameters, should throw an error at parse time -create function namedparmcursor_test6() returns void as $$ -declare - c1 cursor (p1 int, p2 int) for - select * from tenk1 where thousand = p1 and tenthous = p2; -begin - open c1 (p2 := 77); -end -$$ language plpgsql; -ERROR: not enough arguments for cursor "c1" -LINE 6: open c1 (p2 := 77); - ^ --- division by zero runtime error, the context given in the error message --- should be sensible -create function namedparmcursor_test7() returns void as $$ -declare - c1 cursor (p1 int, p2 int) for - select * from tenk1 where thousand = p1 and tenthous = p2; -begin - open c1 (p2 := 77, p1 := 42/0); -end $$ language plpgsql; -select namedparmcursor_test7(); -ERROR: division by zero -CONTEXT: SQL expression "42/0 AS p1, 77 AS p2" -PL/pgSQL function namedparmcursor_test7() line 6 at OPEN --- check that line comments work correctly within the argument list (there --- is some special handling of this case in the code: the newline after the --- comment must be preserved when the argument-evaluating query is --- constructed, otherwise the comment effectively comments out the next --- argument, too) -create function namedparmcursor_test8() returns int4 as $$ -declare - c1 cursor (p1 int, p2 int) for - select count(*) from tenk1 where thousand = p1 and tenthous = p2; - n int4; -begin - open c1 (77 -- test - , 42); - fetch c1 into n; - return n; -end $$ language plpgsql; -select namedparmcursor_test8(); - namedparmcursor_test8 ------------------------ - 0 -(1 row) - --- cursor parameter name can match plpgsql variable or unreserved keyword -create function namedparmcursor_test9(p1 int) returns int4 as $$ -declare - c1 cursor (p1 int, p2 int, debug int) for - select count(*) from tenk1 where thousand = p1 and tenthous = p2 - and four = debug; - p2 int4 := 1006; - n int4; -begin - open c1 (p1 := p1, p2 := p2, debug := 2); - fetch c1 into n; - return n; -end $$ language plpgsql; -select namedparmcursor_test9(6); - namedparmcursor_test9 ------------------------ - 1 -(1 row) - --- --- tests for "raise" processing --- -create function raise_test1(int) returns int as $$ -begin - raise notice 'This message has too many parameters!', $1; - return $1; -end; -$$ language plpgsql; -ERROR: too many parameters specified for RAISE -CONTEXT: compilation of PL/pgSQL function "raise_test1" near line 3 -create function raise_test2(int) returns int as $$ -begin - raise notice 'This message has too few parameters: %, %, %', $1, $1; - return $1; -end; -$$ language plpgsql; -ERROR: too few parameters specified for RAISE -CONTEXT: compilation of PL/pgSQL function "raise_test2" near line 3 -create function raise_test3(int) returns int as $$ -begin - raise notice 'This message has no parameters (despite having %% signs in it)!'; - return $1; -end; -$$ language plpgsql; -select raise_test3(1); -NOTICE: This message has no parameters (despite having % signs in it)! - raise_test3 -------------- - 1 -(1 row) - --- Test re-RAISE inside a nested exception block. This case is allowed --- by Oracle's PL/SQL but was handled differently by PG before 9.1. -CREATE FUNCTION reraise_test() RETURNS void AS $$ -BEGIN - BEGIN - RAISE syntax_error; - EXCEPTION - WHEN syntax_error THEN - BEGIN - raise notice 'exception % thrown in inner block, reraising', sqlerrm; - RAISE; - EXCEPTION - WHEN OTHERS THEN - raise notice 'RIGHT - exception % caught in inner block', sqlerrm; - END; - END; -EXCEPTION - WHEN OTHERS THEN - raise notice 'WRONG - exception % caught in outer block', sqlerrm; -END; -$$ LANGUAGE plpgsql; -SELECT reraise_test(); -NOTICE: exception syntax_error thrown in inner block, reraising -NOTICE: RIGHT - exception syntax_error caught in inner block - reraise_test --------------- - -(1 row) - --- --- reject function definitions that contain malformed SQL queries at --- compile-time, where possible --- -create function bad_sql1() returns int as $$ -declare a int; -begin - a := 5; - Johnny Yuma; - a := 10; - return a; -end$$ language plpgsql; -ERROR: syntax error at or near "Johnny" -LINE 5: Johnny Yuma; - ^ -create function bad_sql2() returns int as $$ -declare r record; -begin - for r in select I fought the law, the law won LOOP - raise notice 'in loop'; - end loop; - return 5; -end;$$ language plpgsql; -ERROR: syntax error at or near "the" -LINE 4: for r in select I fought the law, the law won LOOP - ^ --- a RETURN expression is mandatory, except for void-returning --- functions, where it is not allowed -create function missing_return_expr() returns int as $$ -begin - return ; -end;$$ language plpgsql; -ERROR: missing expression at or near ";" -LINE 3: return ; - ^ -create function void_return_expr() returns void as $$ -begin - return 5; -end;$$ language plpgsql; -ERROR: RETURN cannot have a parameter in function returning void -LINE 3: return 5; - ^ --- VOID functions are allowed to omit RETURN -create function void_return_expr() returns void as $$ -begin - perform 2+2; -end;$$ language plpgsql; -select void_return_expr(); - void_return_expr ------------------- - -(1 row) - --- but ordinary functions are not -create function missing_return_expr() returns int as $$ -begin - perform 2+2; -end;$$ language plpgsql; -select missing_return_expr(); -ERROR: control reached end of function without RETURN -CONTEXT: PL/pgSQL function missing_return_expr() -drop function void_return_expr(); -drop function missing_return_expr(); --- --- EXECUTE ... INTO test --- -create table eifoo (i integer, y integer); -create type eitype as (i integer, y integer); -create or replace function execute_into_test(varchar) returns record as $$ -declare - _r record; - _rt eifoo%rowtype; - _v eitype; - i int; - j int; - k int; -begin - execute 'insert into '||$1||' values(10,15)'; - execute 'select (row).* from (select row(10,1)::eifoo) s' into _r; - raise notice '% %', _r.i, _r.y; - execute 'select * from '||$1||' limit 1' into _rt; - raise notice '% %', _rt.i, _rt.y; - execute 'select *, 20 from '||$1||' limit 1' into i, j, k; - raise notice '% % %', i, j, k; - execute 'select 1,2' into _v; - return _v; -end; $$ language plpgsql; -select execute_into_test('eifoo'); -NOTICE: 10 1 -NOTICE: 10 15 -NOTICE: 10 15 20 - execute_into_test -------------------- - (1,2) -(1 row) - -drop table eifoo cascade; -drop type eitype cascade; --- --- SQLSTATE and SQLERRM test --- -create function excpt_test1() returns void as $$ -begin - raise notice '% %', sqlstate, sqlerrm; -end; $$ language plpgsql; --- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION --- blocks -select excpt_test1(); -ERROR: column "sqlstate" does not exist -LINE 1: sqlstate - ^ -QUERY: sqlstate -CONTEXT: PL/pgSQL function excpt_test1() line 3 at RAISE -create function excpt_test2() returns void as $$ -begin - begin - begin - raise notice '% %', sqlstate, sqlerrm; - end; - end; -end; $$ language plpgsql; --- should fail -select excpt_test2(); -ERROR: column "sqlstate" does not exist -LINE 1: sqlstate - ^ -QUERY: sqlstate -CONTEXT: PL/pgSQL function excpt_test2() line 5 at RAISE -create function excpt_test3() returns void as $$ -begin - begin - raise exception 'user exception'; - exception when others then - raise notice 'caught exception % %', sqlstate, sqlerrm; - begin - raise notice '% %', sqlstate, sqlerrm; - perform 10/0; - exception - when substring_error then - -- this exception handler shouldn't be invoked - raise notice 'unexpected exception: % %', sqlstate, sqlerrm; - when division_by_zero then - raise notice 'caught exception % %', sqlstate, sqlerrm; - end; - raise notice '% %', sqlstate, sqlerrm; - end; -end; $$ language plpgsql; -select excpt_test3(); -NOTICE: caught exception P0001 user exception -NOTICE: P0001 user exception -NOTICE: caught exception 22012 division by zero -NOTICE: P0001 user exception - excpt_test3 -------------- - -(1 row) - -create function excpt_test4() returns text as $$ -begin - begin perform 1/0; - exception when others then return sqlerrm; end; -end; $$ language plpgsql; -select excpt_test4(); - excpt_test4 ------------------- - division by zero -(1 row) - -drop function excpt_test1(); -drop function excpt_test2(); -drop function excpt_test3(); -drop function excpt_test4(); --- parameters of raise stmt can be expressions -create function raise_exprs() returns void as $$ -declare - a integer[] = '{10,20,30}'; - c varchar = 'xyz'; - i integer; -begin - i := 2; - raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL; -end;$$ language plpgsql; -select raise_exprs(); -NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); - raise_exprs -------------- - -(1 row) - -drop function raise_exprs(); --- regression test: verify that multiple uses of same plpgsql datum within --- a SQL command all get mapped to the same $n parameter. The return value --- of the SELECT is not important, we only care that it doesn't fail with --- a complaint about an ungrouped column reference. -create function multi_datum_use(p1 int) returns bool as $$ -declare - x int; - y int; -begin - select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1; - return x = y; -end$$ language plpgsql; -select multi_datum_use(42); - multi_datum_use ------------------ - t -(1 row) - --- --- Test STRICT limiter in both planned and EXECUTE invocations. --- Note that a data-modifying query is quasi strict (disallow multi rows) --- by default in the planned case, but not in EXECUTE. --- -create temp table foo (f1 int, f2 int); -insert into foo values (1,2), (3,4); -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should work - insert into foo values(5,6) returning * into x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -NOTICE: x.f1 = 5, x.f2 = 6 - stricttest ------------- - -(1 row) - -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should fail due to implicit strict - insert into foo values(7,8),(9,10) returning * into x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should work - execute 'insert into foo values(5,6) returning *' into x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -NOTICE: x.f1 = 5, x.f2 = 6 - stricttest ------------- - -(1 row) - -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- this should work since EXECUTE isn't as picky - execute 'insert into foo values(7,8),(9,10) returning *' into x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -NOTICE: x.f1 = 7, x.f2 = 8 - stricttest ------------- - -(1 row) - -select * from foo; - f1 | f2 -----+---- - 1 | 2 - 3 | 4 - 5 | 6 - 5 | 6 - 7 | 8 - 9 | 10 -(6 rows) - -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should work - select * from foo where f1 = 3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -NOTICE: x.f1 = 3, x.f2 = 4 - stricttest ------------- - -(1 row) - -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should fail, no rows - select * from foo where f1 = 0 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned no rows -CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should fail, too many rows - select * from foo where f1 > 3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should work - execute 'select * from foo where f1 = 3' into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -NOTICE: x.f1 = 3, x.f2 = 4 - stricttest ------------- - -(1 row) - -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should fail, no rows - execute 'select * from foo where f1 = 0' into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned no rows -CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- should fail, too many rows - execute 'select * from foo where f1 > 3' into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE -drop function stricttest(); --- test printing parameters after failure due to STRICT -set plpgsql.print_strict_params to true; -create or replace function stricttest() returns void as $$ -declare -x record; -p1 int := 2; -p3 text := 'foo'; -begin - -- no rows - select * from foo where f1 = p1 and f1::text = p3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned no rows -DETAIL: parameters: p1 = '2', p3 = 'foo' -CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement -create or replace function stricttest() returns void as $$ -declare -x record; -p1 int := 2; -p3 text := $a$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$a$; -begin - -- no rows - select * from foo where f1 = p1 and f1::text = p3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned no rows -DETAIL: parameters: p1 = '2', p3 = '''Valame Dios!'' dijo Sancho; ''no le dije yo a vuestra merced que mirase bien lo que hacia?''' -CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement -create or replace function stricttest() returns void as $$ -declare -x record; -p1 int := 2; -p3 text := 'foo'; -begin - -- too many rows - select * from foo where f1 > p1 or f1::text = p3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -DETAIL: parameters: p1 = '2', p3 = 'foo' -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- too many rows, no params - select * from foo where f1 > 3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- no rows - execute 'select * from foo where f1 = $1 or f1::text = $2' using 0, 'foo' into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned no rows -DETAIL: parameters: $1 = '0', $2 = 'foo' -CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- too many rows - execute 'select * from foo where f1 > $1' using 1 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -DETAIL: parameters: $1 = '1' -CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE -create or replace function stricttest() returns void as $$ -declare x record; -begin - -- too many rows, no parameters - execute 'select * from foo where f1 > 3' into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE -create or replace function stricttest() returns void as $$ --- override the global -#print_strict_params off -declare -x record; -p1 int := 2; -p3 text := 'foo'; -begin - -- too many rows - select * from foo where f1 > p1 or f1::text = p3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 10 at SQL statement -reset plpgsql.print_strict_params; -create or replace function stricttest() returns void as $$ --- override the global -#print_strict_params on -declare -x record; -p1 int := 2; -p3 text := 'foo'; -begin - -- too many rows - select * from foo where f1 > p1 or f1::text = p3 into strict x; - raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; -end$$ language plpgsql; -select stricttest(); -ERROR: query returned more than one row -DETAIL: parameters: p1 = '2', p3 = 'foo' -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function stricttest() line 10 at SQL statement --- test warnings and errors -set plpgsql.extra_warnings to 'all'; -set plpgsql.extra_warnings to 'none'; -set plpgsql.extra_errors to 'all'; -set plpgsql.extra_errors to 'none'; --- test warnings when shadowing a variable -set plpgsql.extra_warnings to 'shadowed_variables'; --- simple shadowing of input and output parameters -create or replace function shadowtest(in1 int) - returns table (out1 int) as $$ -declare -in1 int; -out1 int; -begin -end -$$ language plpgsql; -WARNING: variable "in1" shadows a previously defined variable -LINE 4: in1 int; - ^ -WARNING: variable "out1" shadows a previously defined variable -LINE 5: out1 int; - ^ -select shadowtest(1); - shadowtest ------------- -(0 rows) - -set plpgsql.extra_warnings to 'shadowed_variables'; -select shadowtest(1); - shadowtest ------------- -(0 rows) - -create or replace function shadowtest(in1 int) - returns table (out1 int) as $$ -declare -in1 int; -out1 int; -begin -end -$$ language plpgsql; -WARNING: variable "in1" shadows a previously defined variable -LINE 4: in1 int; - ^ -WARNING: variable "out1" shadows a previously defined variable -LINE 5: out1 int; - ^ -select shadowtest(1); - shadowtest ------------- -(0 rows) - -drop function shadowtest(int); --- shadowing in a second DECLARE block -create or replace function shadowtest() - returns void as $$ -declare -f1 int; -begin - declare - f1 int; - begin - end; -end$$ language plpgsql; -WARNING: variable "f1" shadows a previously defined variable -LINE 7: f1 int; - ^ -drop function shadowtest(); --- several levels of shadowing -create or replace function shadowtest(in1 int) - returns void as $$ -declare -in1 int; -begin - declare - in1 int; - begin - end; -end$$ language plpgsql; -WARNING: variable "in1" shadows a previously defined variable -LINE 4: in1 int; - ^ -WARNING: variable "in1" shadows a previously defined variable -LINE 7: in1 int; - ^ -drop function shadowtest(int); --- shadowing in cursor definitions -create or replace function shadowtest() - returns void as $$ -declare -f1 int; -c1 cursor (f1 int) for select 1; -begin -end$$ language plpgsql; -WARNING: variable "f1" shadows a previously defined variable -LINE 5: c1 cursor (f1 int) for select 1; - ^ -drop function shadowtest(); --- test errors when shadowing a variable -set plpgsql.extra_errors to 'shadowed_variables'; -create or replace function shadowtest(f1 int) - returns boolean as $$ -declare f1 int; begin return 1; end $$ language plpgsql; -ERROR: variable "f1" shadows a previously defined variable -LINE 3: declare f1 int; begin return 1; end $$ language plpgsql; - ^ -select shadowtest(1); -ERROR: function shadowtest(integer) does not exist -LINE 1: select shadowtest(1); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -reset plpgsql.extra_errors; -reset plpgsql.extra_warnings; -create or replace function shadowtest(f1 int) - returns boolean as $$ -declare f1 int; begin return 1; end $$ language plpgsql; -select shadowtest(1); - shadowtest ------------- - t -(1 row) - --- runtime extra checks -set plpgsql.extra_warnings to 'too_many_rows'; -do $$ -declare x int; -begin - select v from generate_series(1,2) g(v) into x; -end; -$$; -WARNING: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -set plpgsql.extra_errors to 'too_many_rows'; -do $$ -declare x int; -begin - select v from generate_series(1,2) g(v) into x; -end; -$$; -ERROR: query returned more than one row -HINT: Make sure the query returns a single row, or use LIMIT 1. -CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement -reset plpgsql.extra_errors; -reset plpgsql.extra_warnings; -set plpgsql.extra_warnings to 'strict_multi_assignment'; -do $$ -declare - x int; - y int; -begin - select 1 into x, y; - select 1,2 into x, y; - select 1,2,3 into x, y; -end -$$; -WARNING: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_warnings is active. -HINT: Make sure the query returns the exact list of columns. -WARNING: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_warnings is active. -HINT: Make sure the query returns the exact list of columns. -set plpgsql.extra_errors to 'strict_multi_assignment'; -do $$ -declare - x int; - y int; -begin - select 1 into x, y; - select 1,2 into x, y; - select 1,2,3 into x, y; -end -$$; -ERROR: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_errors is active. -HINT: Make sure the query returns the exact list of columns. -CONTEXT: PL/pgSQL function inline_code_block line 6 at SQL statement -create table test_01(a int, b int, c int); -alter table test_01 drop column a; --- the check is active only when source table is not empty -insert into test_01 values(10,20); -do $$ -declare - x int; - y int; -begin - select * from test_01 into x, y; -- should be ok - raise notice 'ok'; - select * from test_01 into x; -- should to fail -end; -$$; -NOTICE: ok -ERROR: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_errors is active. -HINT: Make sure the query returns the exact list of columns. -CONTEXT: PL/pgSQL function inline_code_block line 8 at SQL statement -do $$ -declare - t test_01; -begin - select 1, 2 into t; -- should be ok - raise notice 'ok'; - select 1, 2, 3 into t; -- should fail; -end; -$$; -NOTICE: ok -ERROR: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_errors is active. -HINT: Make sure the query returns the exact list of columns. -CONTEXT: PL/pgSQL function inline_code_block line 7 at SQL statement -do $$ -declare - t test_01; -begin - select 1 into t; -- should fail; -end; -$$; -ERROR: number of source and target fields in assignment does not match -DETAIL: strict_multi_assignment check of extra_errors is active. -HINT: Make sure the query returns the exact list of columns. -CONTEXT: PL/pgSQL function inline_code_block line 5 at SQL statement -drop table test_01; -reset plpgsql.extra_errors; -reset plpgsql.extra_warnings; --- test scrollable cursor support -create function sc_test() returns setof integer as $$ -declare - c scroll cursor for select f1 from int4_tbl; - x integer; -begin - open c; - fetch last from c into x; - while found loop - return next x; - fetch prior from c into x; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test -------------- - -2147483647 - 2147483647 - -123456 - 123456 - 0 -(5 rows) - -create or replace function sc_test() returns setof integer as $$ -declare - c no scroll cursor for select f1 from int4_tbl; - x integer; -begin - open c; - fetch last from c into x; - while found loop - return next x; - fetch prior from c into x; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); -- fails because of NO SCROLL specification -ERROR: cursor can only scan forward -HINT: Declare it with SCROLL option to enable backward scan. -CONTEXT: PL/pgSQL function sc_test() line 7 at FETCH -create or replace function sc_test() returns setof integer as $$ -declare - c refcursor; - x integer; -begin - open c scroll for select f1 from int4_tbl; - fetch last from c into x; - while found loop - return next x; - fetch prior from c into x; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test -------------- - -2147483647 - 2147483647 - -123456 - 123456 - 0 -(5 rows) - -create or replace function sc_test() returns setof integer as $$ -declare - c refcursor; - x integer; -begin - open c scroll for execute 'select f1 from int4_tbl'; - fetch last from c into x; - while found loop - return next x; - fetch relative -2 from c into x; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test -------------- - -2147483647 - -123456 - 0 -(3 rows) - -create or replace function sc_test() returns setof integer as $$ -declare - c refcursor; - x integer; -begin - open c scroll for execute 'select f1 from int4_tbl'; - fetch last from c into x; - while found loop - return next x; - move backward 2 from c; - fetch relative -1 from c into x; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test -------------- - -2147483647 - 123456 -(2 rows) - -create or replace function sc_test() returns setof integer as $$ -declare - c cursor for select * from generate_series(1, 10); - x integer; -begin - open c; - loop - move relative 2 in c; - if not found then - exit; - end if; - fetch next from c into x; - if found then - return next x; - end if; - end loop; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test ---------- - 3 - 6 - 9 -(3 rows) - -create or replace function sc_test() returns setof integer as $$ -declare - c cursor for select * from generate_series(1, 10); - x integer; -begin - open c; - move forward all in c; - fetch backward from c into x; - if found then - return next x; - end if; - close c; -end; -$$ language plpgsql; -select * from sc_test(); - sc_test ---------- - 10 -(1 row) - -drop function sc_test(); --- test qualified variable names -create function pl_qual_names (param1 int) returns void as $$ -<> -declare - param1 int := 1; -begin - <> - declare - param1 int := 2; - begin - raise notice 'param1 = %', param1; - raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1; - raise notice 'outerblock.param1 = %', outerblock.param1; - raise notice 'innerblock.param1 = %', innerblock.param1; - end; -end; -$$ language plpgsql; -select pl_qual_names(42); -NOTICE: param1 = 2 -NOTICE: pl_qual_names.param1 = 42 -NOTICE: outerblock.param1 = 1 -NOTICE: innerblock.param1 = 2 - pl_qual_names ---------------- - -(1 row) - -drop function pl_qual_names(int); --- tests for RETURN QUERY -create function ret_query1(out int, out int) returns setof record as $$ -begin - $1 := -1; - $2 := -2; - return next; - return query select x + 1, x * 10 from generate_series(0, 10) s (x); - return next; -end; -$$ language plpgsql; -select * from ret_query1(); - column1 | column2 ----------+--------- - -1 | -2 - 1 | 0 - 2 | 10 - 3 | 20 - 4 | 30 - 5 | 40 - 6 | 50 - 7 | 60 - 8 | 70 - 9 | 80 - 10 | 90 - 11 | 100 - -1 | -2 -(13 rows) - -create type record_type as (x text, y int, z boolean); -create or replace function ret_query2(lim int) returns setof record_type as $$ -begin - return query select md5(s.x::text), s.x, s.x > 0 - from generate_series(-8, lim) s (x) where s.x % 2 = 0; -end; -$$ language plpgsql; -select * from ret_query2(8); - x | y | z -----------------------------------+----+--- - a8d2ec85eaf98407310b72eb73dda247 | -8 | f - 596a3d04481816330f07e4f97510c28f | -6 | f - 0267aaf632e87a63288a08331f22c7c3 | -4 | f - 5d7b9adcbe1c629ec722529dd12e5129 | -2 | f - cfcd208495d565ef66e7dff9f98764da | 0 | f - c81e728d9d4c2f636f067f89cc14862c | 2 | t - a87ff679a2f3e71d9181a67b7542122c | 4 | t - 1679091c5a880faf6fb5e6087eb1b2dc | 6 | t - c9f0f895fb98ab9159f51fd0297e236d | 8 | t -(9 rows) - --- test EXECUTE USING -create function exc_using(int, text) returns int as $$ -declare i int; -begin - for i in execute 'select * from generate_series(1,$1)' using $1+1 loop - raise notice '%', i; - end loop; - execute 'select $2 + $2*3 + length($1)' into i using $2,$1; - return i; -end -$$ language plpgsql; -select exc_using(5, 'foobar'); -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 -NOTICE: 4 -NOTICE: 5 -NOTICE: 6 - exc_using ------------ - 26 -(1 row) - -drop function exc_using(int, text); -create or replace function exc_using(int) returns void as $$ -declare - c refcursor; - i int; -begin - open c for execute 'select * from generate_series(1,$1)' using $1+1; - loop - fetch c into i; - exit when not found; - raise notice '%', i; - end loop; - close c; - return; -end; -$$ language plpgsql; -select exc_using(5); -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 -NOTICE: 4 -NOTICE: 5 -NOTICE: 6 - exc_using ------------ - -(1 row) - -drop function exc_using(int); --- test FOR-over-cursor -create or replace function forc01() returns void as $$ -declare - c cursor(r1 integer, r2 integer) - for select * from generate_series(r1,r2) i; - c2 cursor - for select * from generate_series(41,43) i; -begin - for r in c(5,7) loop - raise notice '% from %', r.i, c; - end loop; - -- again, to test if cursor was closed properly - for r in c(9,10) loop - raise notice '% from %', r.i, c; - end loop; - -- and test a parameterless cursor - for r in c2 loop - raise notice '% from %', r.i, c2; - end loop; - -- and try it with a hand-assigned name - raise notice 'after loop, c2 = %', c2; - c2 := 'special_name'; - for r in c2 loop - raise notice '% from %', r.i, c2; - end loop; - raise notice 'after loop, c2 = %', c2; - -- and try it with a generated name - -- (which we can't show in the output because it's variable) - c2 := null; - for r in c2 loop - raise notice '%', r.i; - end loop; - raise notice 'after loop, c2 = %', c2; - return; -end; -$$ language plpgsql; -select forc01(); -NOTICE: 5 from c -NOTICE: 6 from c -NOTICE: 7 from c -NOTICE: 9 from c -NOTICE: 10 from c -NOTICE: 41 from c2 -NOTICE: 42 from c2 -NOTICE: 43 from c2 -NOTICE: after loop, c2 = c2 -NOTICE: 41 from special_name -NOTICE: 42 from special_name -NOTICE: 43 from special_name -NOTICE: after loop, c2 = special_name -NOTICE: 41 -NOTICE: 42 -NOTICE: 43 -NOTICE: after loop, c2 = - forc01 --------- - -(1 row) - --- try updating the cursor's current row -create temp table forc_test as - select n as i, n as j from generate_series(1,10) n; -create or replace function forc01() returns void as $$ -declare - c cursor for select * from forc_test; -begin - for r in c loop - raise notice '%, %', r.i, r.j; - update forc_test set i = i * 100, j = r.j * 2 where current of c; - end loop; -end; -$$ language plpgsql; -select forc01(); -NOTICE: 1, 1 -NOTICE: 2, 2 -NOTICE: 3, 3 -NOTICE: 4, 4 -NOTICE: 5, 5 -NOTICE: 6, 6 -NOTICE: 7, 7 -NOTICE: 8, 8 -NOTICE: 9, 9 -NOTICE: 10, 10 - forc01 --------- - -(1 row) - -select * from forc_test; - i | j -------+---- - 100 | 2 - 200 | 4 - 300 | 6 - 400 | 8 - 500 | 10 - 600 | 12 - 700 | 14 - 800 | 16 - 900 | 18 - 1000 | 20 -(10 rows) - --- same, with a cursor whose portal name doesn't match variable name -create or replace function forc01() returns void as $$ -declare - c refcursor := 'fooled_ya'; - r record; -begin - open c for select * from forc_test; - loop - fetch c into r; - exit when not found; - raise notice '%, %', r.i, r.j; - update forc_test set i = i * 100, j = r.j * 2 where current of c; - end loop; -end; -$$ language plpgsql; -select forc01(); -NOTICE: 100, 2 -NOTICE: 200, 4 -NOTICE: 300, 6 -NOTICE: 400, 8 -NOTICE: 500, 10 -NOTICE: 600, 12 -NOTICE: 700, 14 -NOTICE: 800, 16 -NOTICE: 900, 18 -NOTICE: 1000, 20 - forc01 --------- - -(1 row) - -select * from forc_test; - i | j ---------+---- - 10000 | 4 - 20000 | 8 - 30000 | 12 - 40000 | 16 - 50000 | 20 - 60000 | 24 - 70000 | 28 - 80000 | 32 - 90000 | 36 - 100000 | 40 -(10 rows) - -drop function forc01(); --- fail because cursor has no query bound to it -create or replace function forc_bad() returns void as $$ -declare - c refcursor; -begin - for r in c loop - raise notice '%', r.i; - end loop; -end; -$$ language plpgsql; -ERROR: cursor FOR loop must use a bound cursor variable -LINE 5: for r in c loop - ^ --- test RETURN QUERY EXECUTE -create or replace function return_dquery() -returns setof int as $$ -begin - return query execute 'select * from (values(10),(20)) f'; - return query execute 'select * from (values($1),($2)) f' using 40,50; -end; -$$ language plpgsql; -select * from return_dquery(); - return_dquery ---------------- - 10 - 20 - 40 - 50 -(4 rows) - -drop function return_dquery(); --- test RETURN QUERY with dropped columns -create table tabwithcols(a int, b int, c int, d int); -insert into tabwithcols values(10,20,30,40),(50,60,70,80); -create or replace function returnqueryf() -returns setof tabwithcols as $$ -begin - return query select * from tabwithcols; - return query execute 'select * from tabwithcols'; -end; -$$ language plpgsql; -select * from returnqueryf(); - a | b | c | d -----+----+----+---- - 10 | 20 | 30 | 40 - 50 | 60 | 70 | 80 - 10 | 20 | 30 | 40 - 50 | 60 | 70 | 80 -(4 rows) - -alter table tabwithcols drop column b; -select * from returnqueryf(); - a | c | d -----+----+---- - 10 | 30 | 40 - 50 | 70 | 80 - 10 | 30 | 40 - 50 | 70 | 80 -(4 rows) - -alter table tabwithcols drop column d; -select * from returnqueryf(); - a | c -----+---- - 10 | 30 - 50 | 70 - 10 | 30 - 50 | 70 -(4 rows) - -alter table tabwithcols add column d int; -select * from returnqueryf(); - a | c | d -----+----+--- - 10 | 30 | - 50 | 70 | - 10 | 30 | - 50 | 70 | -(4 rows) - -drop function returnqueryf(); -drop table tabwithcols; --- --- Tests for composite-type results --- -create type compostype as (x int, y varchar); --- test: use of variable of composite type in return statement -create or replace function compos() returns compostype as $$ -declare - v compostype; -begin - v := (1, 'hello'); - return v; -end; -$$ language plpgsql; -select compos(); - compos ------------ - (1,hello) -(1 row) - --- test: use of variable of record type in return statement -create or replace function compos() returns compostype as $$ -declare - v record; -begin - v := (1, 'hello'::varchar); - return v; -end; -$$ language plpgsql; -select compos(); - compos ------------ - (1,hello) -(1 row) - --- test: use of row expr in return statement -create or replace function compos() returns compostype as $$ -begin - return (1, 'hello'::varchar); -end; -$$ language plpgsql; -select compos(); - compos ------------ - (1,hello) -(1 row) - --- this does not work currently (no implicit casting) -create or replace function compos() returns compostype as $$ -begin - return (1, 'hello'); -end; -$$ language plpgsql; -select compos(); -ERROR: returned record type does not match expected record type -DETAIL: Returned type unknown does not match expected type character varying in column 2. -CONTEXT: PL/pgSQL function compos() while casting return value to function's return type --- ... but this does -create or replace function compos() returns compostype as $$ -begin - return (1, 'hello')::compostype; -end; -$$ language plpgsql; -select compos(); - compos ------------ - (1,hello) -(1 row) - -drop function compos(); --- test: return a row expr as record. -create or replace function composrec() returns record as $$ -declare - v record; -begin - v := (1, 'hello'); - return v; -end; -$$ language plpgsql; -select composrec(); - composrec ------------ - (1,hello) -(1 row) - --- test: return row expr in return statement. -create or replace function composrec() returns record as $$ -begin - return (1, 'hello'); -end; -$$ language plpgsql; -select composrec(); - composrec ------------ - (1,hello) -(1 row) - -drop function composrec(); --- test: row expr in RETURN NEXT statement. -create or replace function compos() returns setof compostype as $$ -begin - for i in 1..3 - loop - return next (1, 'hello'::varchar); - end loop; - return next null::compostype; - return next (2, 'goodbye')::compostype; -end; -$$ language plpgsql; -select * from compos(); - x | y ----+--------- - 1 | hello - 1 | hello - 1 | hello - | - 2 | goodbye -(5 rows) - -drop function compos(); --- test: use invalid expr in return statement. -create or replace function compos() returns compostype as $$ -begin - return 1 + 1; -end; -$$ language plpgsql; -select compos(); -ERROR: cannot return non-composite value from function returning composite type -CONTEXT: PL/pgSQL function compos() line 3 at RETURN --- RETURN variable is a different code path ... -create or replace function compos() returns compostype as $$ -declare x int := 42; -begin - return x; -end; -$$ language plpgsql; -select * from compos(); -ERROR: cannot return non-composite value from function returning composite type -CONTEXT: PL/pgSQL function compos() line 4 at RETURN -drop function compos(); --- test: invalid use of composite variable in scalar-returning function -create or replace function compos() returns int as $$ -declare - v compostype; -begin - v := (1, 'hello'); - return v; -end; -$$ language plpgsql; -select compos(); -ERROR: invalid input syntax for type integer: "(1,hello)" -CONTEXT: PL/pgSQL function compos() while casting return value to function's return type --- test: invalid use of composite expression in scalar-returning function -create or replace function compos() returns int as $$ -begin - return (1, 'hello')::compostype; -end; -$$ language plpgsql; -select compos(); -ERROR: invalid input syntax for type integer: "(1,hello)" -CONTEXT: PL/pgSQL function compos() while casting return value to function's return type -drop function compos(); -drop type compostype; --- --- Tests for 8.4's new RAISE features --- -create or replace function raise_test() returns void as $$ -begin - raise notice '% % %', 1, 2, 3 - using errcode = '55001', detail = 'some detail info', hint = 'some hint'; - raise '% % %', 1, 2, 3 - using errcode = 'division_by_zero', detail = 'some detail info'; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: 1 2 3 -DETAIL: some detail info -HINT: some hint -ERROR: 1 2 3 -DETAIL: some detail info -CONTEXT: PL/pgSQL function raise_test() line 5 at RAISE --- Since we can't actually see the thrown SQLSTATE in default psql output, --- test it like this; this also tests re-RAISE -create or replace function raise_test() returns void as $$ -begin - raise 'check me' - using errcode = 'division_by_zero', detail = 'some detail info'; - exception - when others then - raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; - raise; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: SQLSTATE: 22012 SQLERRM: check me -ERROR: check me -DETAIL: some detail info -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise 'check me' - using errcode = '1234F', detail = 'some detail info'; - exception - when others then - raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; - raise; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: SQLSTATE: 1234F SQLERRM: check me -ERROR: check me -DETAIL: some detail info -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE --- SQLSTATE specification in WHEN -create or replace function raise_test() returns void as $$ -begin - raise 'check me' - using errcode = '1234F', detail = 'some detail info'; - exception - when sqlstate '1234F' then - raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; - raise; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: SQLSTATE: 1234F SQLERRM: check me -ERROR: check me -DETAIL: some detail info -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise division_by_zero using detail = 'some detail info'; - exception - when others then - raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; - raise; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: SQLSTATE: 22012 SQLERRM: division_by_zero -ERROR: division_by_zero -DETAIL: some detail info -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise division_by_zero; -end; -$$ language plpgsql; -select raise_test(); -ERROR: division_by_zero -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise sqlstate '1234F'; -end; -$$ language plpgsql; -select raise_test(); -ERROR: 1234F -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise division_by_zero using message = 'custom' || ' message'; -end; -$$ language plpgsql; -select raise_test(); -ERROR: custom message -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE -create or replace function raise_test() returns void as $$ -begin - raise using message = 'custom' || ' message', errcode = '22012'; -end; -$$ language plpgsql; -select raise_test(); -ERROR: custom message -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE --- conflict on message -create or replace function raise_test() returns void as $$ -begin - raise notice 'some message' using message = 'custom' || ' message', errcode = '22012'; -end; -$$ language plpgsql; -select raise_test(); -ERROR: RAISE option already specified: MESSAGE -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE --- conflict on errcode -create or replace function raise_test() returns void as $$ -begin - raise division_by_zero using message = 'custom' || ' message', errcode = '22012'; -end; -$$ language plpgsql; -select raise_test(); -ERROR: RAISE option already specified: ERRCODE -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE --- nothing to re-RAISE -create or replace function raise_test() returns void as $$ -begin - raise; -end; -$$ language plpgsql; -select raise_test(); -ERROR: RAISE without parameters cannot be used outside an exception handler -CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE --- test access to exception data -create function zero_divide() returns int as $$ -declare v int := 0; -begin - return 10 / v; -end; -$$ language plpgsql; -create or replace function raise_test() returns void as $$ -begin - raise exception 'custom exception' - using detail = 'some detail of custom exception', - hint = 'some hint related to custom exception'; -end; -$$ language plpgsql; -create function stacked_diagnostics_test() returns void as $$ -declare _sqlstate text; - _message text; - _context text; -begin - perform zero_divide(); -exception when others then - get stacked diagnostics - _sqlstate = returned_sqlstate, - _message = message_text, - _context = pg_exception_context; - raise notice 'sqlstate: %, message: %, context: [%]', - _sqlstate, _message, replace(_context, E'\n', ' <- '); -end; -$$ language plpgsql; -select stacked_diagnostics_test(); -NOTICE: sqlstate: 22012, message: division by zero, context: [PL/pgSQL function zero_divide() line 4 at RETURN <- SQL statement "SELECT zero_divide()" <- PL/pgSQL function stacked_diagnostics_test() line 6 at PERFORM] - stacked_diagnostics_test --------------------------- - -(1 row) - -create or replace function stacked_diagnostics_test() returns void as $$ -declare _detail text; - _hint text; - _message text; -begin - perform raise_test(); -exception when others then - get stacked diagnostics - _message = message_text, - _detail = pg_exception_detail, - _hint = pg_exception_hint; - raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; -end; -$$ language plpgsql; -select stacked_diagnostics_test(); -NOTICE: message: custom exception, detail: some detail of custom exception, hint: some hint related to custom exception - stacked_diagnostics_test --------------------------- - -(1 row) - --- fail, cannot use stacked diagnostics statement outside handler -create or replace function stacked_diagnostics_test() returns void as $$ -declare _detail text; - _hint text; - _message text; -begin - get stacked diagnostics - _message = message_text, - _detail = pg_exception_detail, - _hint = pg_exception_hint; - raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; -end; -$$ language plpgsql; -select stacked_diagnostics_test(); -ERROR: GET STACKED DIAGNOSTICS cannot be used outside an exception handler -CONTEXT: PL/pgSQL function stacked_diagnostics_test() line 6 at GET STACKED DIAGNOSTICS -drop function zero_divide(); -drop function stacked_diagnostics_test(); --- check cases where implicit SQLSTATE variable could be confused with --- SQLSTATE as a keyword, cf bug #5524 -create or replace function raise_test() returns void as $$ -begin - perform 1/0; -exception - when sqlstate '22012' then - raise notice using message = sqlstate; - raise sqlstate '22012' using message = 'substitute message'; -end; -$$ language plpgsql; -select raise_test(); -NOTICE: 22012 -ERROR: substitute message -CONTEXT: PL/pgSQL function raise_test() line 7 at RAISE -drop function raise_test(); --- test passing column_name, constraint_name, datatype_name, table_name --- and schema_name error fields -create or replace function stacked_diagnostics_test() returns void as $$ -declare _column_name text; - _constraint_name text; - _datatype_name text; - _table_name text; - _schema_name text; -begin - raise exception using - column = '>>some column name<<', - constraint = '>>some constraint name<<', - datatype = '>>some datatype name<<', - table = '>>some table name<<', - schema = '>>some schema name<<'; -exception when others then - get stacked diagnostics - _column_name = column_name, - _constraint_name = constraint_name, - _datatype_name = pg_datatype_name, - _table_name = table_name, - _schema_name = schema_name; - raise notice 'column %, constraint %, type %, table %, schema %', - _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; -end; -$$ language plpgsql; -select stacked_diagnostics_test(); -NOTICE: column >>some column name<<, constraint >>some constraint name<<, type >>some datatype name<<, table >>some table name<<, schema >>some schema name<< - stacked_diagnostics_test --------------------------- - -(1 row) - -drop function stacked_diagnostics_test(); --- test variadic functions -create or replace function vari(variadic int[]) -returns void as $$ -begin - for i in array_lower($1,1)..array_upper($1,1) loop - raise notice '%', $1[i]; - end loop; end; -$$ language plpgsql; -select vari(1,2,3,4,5); -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 -NOTICE: 4 -NOTICE: 5 - vari ------- - -(1 row) - -select vari(3,4,5); -NOTICE: 3 -NOTICE: 4 -NOTICE: 5 - vari ------- - -(1 row) - -select vari(variadic array[5,6,7]); -NOTICE: 5 -NOTICE: 6 -NOTICE: 7 - vari ------- - -(1 row) - -drop function vari(int[]); --- coercion test -create or replace function pleast(variadic numeric[]) -returns numeric as $$ -declare aux numeric = $1[array_lower($1,1)]; -begin - for i in array_lower($1,1)+1..array_upper($1,1) loop - if $1[i] < aux then aux := $1[i]; end if; - end loop; - return aux; -end; -$$ language plpgsql immutable strict; -select pleast(10,1,2,3,-16); - pleast --------- - -16 -(1 row) - -select pleast(10.2,2.2,-1.1); - pleast --------- - -1.1 -(1 row) - -select pleast(10.2,10, -20); - pleast --------- - -20 -(1 row) - -select pleast(10,20, -1.0); - pleast --------- - -1.0 -(1 row) - --- in case of conflict, non-variadic version is preferred -create or replace function pleast(numeric) -returns numeric as $$ -begin - raise notice 'non-variadic function called'; - return $1; -end; -$$ language plpgsql immutable strict; -select pleast(10); -NOTICE: non-variadic function called - pleast --------- - 10 -(1 row) - -drop function pleast(numeric[]); -drop function pleast(numeric); --- test table functions -create function tftest(int) returns table(a int, b int) as $$ -begin - return query select $1, $1+i from generate_series(1,5) g(i); -end; -$$ language plpgsql immutable strict; -select * from tftest(10); - a | b -----+---- - 10 | 11 - 10 | 12 - 10 | 13 - 10 | 14 - 10 | 15 -(5 rows) - -create or replace function tftest(a1 int) returns table(a int, b int) as $$ -begin - a := a1; b := a1 + 1; - return next; - a := a1 * 10; b := a1 * 10 + 1; - return next; -end; -$$ language plpgsql immutable strict; -select * from tftest(10); - a | b ------+----- - 10 | 11 - 100 | 101 -(2 rows) - -drop function tftest(int); -create function rttest() -returns setof int as $$ -declare rc int; -begin - return query values(10),(20); - get diagnostics rc = row_count; - raise notice '% %', found, rc; - return query select * from (values(10),(20)) f(a) where false; - get diagnostics rc = row_count; - raise notice '% %', found, rc; - return query execute 'values(10),(20)'; - get diagnostics rc = row_count; - raise notice '% %', found, rc; - return query execute 'select * from (values(10),(20)) f(a) where false'; - get diagnostics rc = row_count; - raise notice '% %', found, rc; -end; -$$ language plpgsql; -select * from rttest(); -NOTICE: t 2 -NOTICE: f 0 -NOTICE: t 2 -NOTICE: f 0 - rttest --------- - 10 - 20 - 10 - 20 -(4 rows) - --- check some error cases, too -create or replace function rttest() -returns setof int as $$ -begin - return query select 10 into no_such_table; -end; -$$ language plpgsql; -select * from rttest(); -ERROR: SELECT INTO query does not return tuples -CONTEXT: SQL statement "select 10 into no_such_table" -PL/pgSQL function rttest() line 3 at RETURN QUERY -create or replace function rttest() -returns setof int as $$ -begin - return query execute 'select 10 into no_such_table'; -end; -$$ language plpgsql; -select * from rttest(); -ERROR: SELECT INTO query does not return tuples -CONTEXT: SQL statement "select 10 into no_such_table" -PL/pgSQL function rttest() line 3 at RETURN QUERY -select * from no_such_table; -ERROR: relation "no_such_table" does not exist -LINE 1: select * from no_such_table; - ^ -drop function rttest(); --- Test for proper cleanup at subtransaction exit. This example --- exposed a bug in PG 8.2. -CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$ -DECLARE - v_var INTEGER; -BEGIN - BEGIN - v_var := (leaker_2(fail)).error_code; - EXCEPTION - WHEN others THEN RETURN 0; - END; - RETURN 1; -END; -$$ LANGUAGE plpgsql; -CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER) - RETURNS RECORD AS $$ -BEGIN - IF fail THEN - RAISE EXCEPTION 'fail ...'; - END IF; - error_code := 1; - new_id := 1; - RETURN; -END; -$$ LANGUAGE plpgsql; -SELECT * FROM leaker_1(false); - leaker_1 ----------- - 1 -(1 row) - -SELECT * FROM leaker_1(true); - leaker_1 ----------- - 0 -(1 row) - -DROP FUNCTION leaker_1(bool); -DROP FUNCTION leaker_2(bool); --- Test for appropriate cleanup of non-simple expression evaluations --- (bug in all versions prior to August 2010) -CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$ -DECLARE - arr text[]; - lr text; - i integer; -BEGIN - arr := array[array['foo','bar'], array['baz', 'quux']]; - lr := 'fool'; - i := 1; - -- use sub-SELECTs to make expressions non-simple - arr[(SELECT i)][(SELECT i+1)] := (SELECT lr); - RETURN arr; -END; -$$ LANGUAGE plpgsql; -SELECT nonsimple_expr_test(); - nonsimple_expr_test -------------------------- - {{foo,fool},{baz,quux}} -(1 row) - -DROP FUNCTION nonsimple_expr_test(); -CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$ -declare - i integer NOT NULL := 0; -begin - begin - i := (SELECT NULL::integer); -- should throw error - exception - WHEN OTHERS THEN - i := (SELECT 1::integer); - end; - return i; -end; -$$ LANGUAGE plpgsql; -SELECT nonsimple_expr_test(); - nonsimple_expr_test ---------------------- - 1 -(1 row) - -DROP FUNCTION nonsimple_expr_test(); --- --- Test cases involving recursion and error recovery in simple expressions --- (bugs in all versions before October 2010). The problems are most --- easily exposed by mutual recursion between plpgsql and sql functions. --- -create function recurse(float8) returns float8 as -$$ -begin - if ($1 > 0) then - return sql_recurse($1 - 1); - else - return $1; - end if; -end; -$$ language plpgsql; --- "limit" is to prevent this from being inlined -create function sql_recurse(float8) returns float8 as -$$ select recurse($1) limit 1; $$ language sql; -select recurse(10); - recurse ---------- - 0 -(1 row) - -create function error1(text) returns text language sql as -$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$; -create function error2(p_name_table text) returns text language plpgsql as $$ -begin - return error1(p_name_table); -end$$; -BEGIN; -create table public.stuffs (stuff text); -SAVEPOINT a; -select error2('nonexistent.stuffs'); -ERROR: schema "nonexistent" does not exist -CONTEXT: SQL function "error1" statement 1 -PL/pgSQL function error2(text) line 3 at RETURN -ROLLBACK TO a; -select error2('public.stuffs'); - error2 --------- - stuffs -(1 row) - -rollback; -drop function error2(p_name_table text); -drop function error1(text); --- Test for proper handling of cast-expression caching -create function sql_to_date(integer) returns date as $$ -select $1::text::date -$$ language sql immutable strict; -create cast (integer as date) with function sql_to_date(integer) as assignment; -create function cast_invoker(integer) returns date as $$ -begin - return $1; -end$$ language plpgsql; -select cast_invoker(20150717); - cast_invoker --------------- - 07-17-2015 -(1 row) - -select cast_invoker(20150718); -- second call crashed in pre-release 9.5 - cast_invoker --------------- - 07-18-2015 -(1 row) - -begin; -select cast_invoker(20150717); - cast_invoker --------------- - 07-17-2015 -(1 row) - -select cast_invoker(20150718); - cast_invoker --------------- - 07-18-2015 -(1 row) - -savepoint s1; -select cast_invoker(20150718); - cast_invoker --------------- - 07-18-2015 -(1 row) - -select cast_invoker(-1); -- fails -ERROR: invalid input syntax for type date: "-1" -CONTEXT: SQL function "sql_to_date" statement 1 -PL/pgSQL function cast_invoker(integer) while casting return value to function's return type -rollback to savepoint s1; -select cast_invoker(20150719); - cast_invoker --------------- - 07-19-2015 -(1 row) - -select cast_invoker(20150720); - cast_invoker --------------- - 07-20-2015 -(1 row) - -commit; -drop function cast_invoker(integer); -drop function sql_to_date(integer) cascade; -NOTICE: drop cascades to cast from integer to date --- Test handling of cast cache inside DO blocks --- (to check the original crash case, this must be a cast not previously --- used in this session) -begin; -do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$; -do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$; -end; --- Test for consistent reporting of error context -create function fail() returns int language plpgsql as $$ -begin - return 1/0; -end -$$; -select fail(); -ERROR: division by zero -CONTEXT: SQL expression "1/0" -PL/pgSQL function fail() line 3 at RETURN -select fail(); -ERROR: division by zero -CONTEXT: SQL expression "1/0" -PL/pgSQL function fail() line 3 at RETURN -drop function fail(); --- Test handling of string literals. -set standard_conforming_strings = off; -create or replace function strtest() returns text as $$ -begin - raise notice 'foo\\bar\041baz'; - return 'foo\\bar\041baz'; -end -$$ language plpgsql; -WARNING: nonstandard use of \\ in a string literal -LINE 3: raise notice 'foo\\bar\041baz'; - ^ -HINT: Use the escape string syntax for backslashes, e.g., E'\\'. -WARNING: nonstandard use of \\ in a string literal -LINE 4: return 'foo\\bar\041baz'; - ^ -HINT: Use the escape string syntax for backslashes, e.g., E'\\'. -WARNING: nonstandard use of \\ in a string literal -LINE 4: return 'foo\\bar\041baz'; - ^ -HINT: Use the escape string syntax for backslashes, e.g., E'\\'. -select strtest(); -NOTICE: foo\bar!baz -WARNING: nonstandard use of \\ in a string literal -LINE 1: 'foo\\bar\041baz' - ^ -HINT: Use the escape string syntax for backslashes, e.g., E'\\'. -QUERY: 'foo\\bar\041baz' - strtest -------------- - foo\bar!baz -(1 row) - -create or replace function strtest() returns text as $$ -begin - raise notice E'foo\\bar\041baz'; - return E'foo\\bar\041baz'; -end -$$ language plpgsql; -select strtest(); -NOTICE: foo\bar!baz - strtest -------------- - foo\bar!baz -(1 row) - -set standard_conforming_strings = on; -create or replace function strtest() returns text as $$ -begin - raise notice 'foo\\bar\041baz\'; - return 'foo\\bar\041baz\'; -end -$$ language plpgsql; -select strtest(); -NOTICE: foo\\bar\041baz\ - strtest ------------------- - foo\\bar\041baz\ -(1 row) - -create or replace function strtest() returns text as $$ -begin - raise notice E'foo\\bar\041baz'; - return E'foo\\bar\041baz'; -end -$$ language plpgsql; -select strtest(); -NOTICE: foo\bar!baz - strtest -------------- - foo\bar!baz -(1 row) - -drop function strtest(); --- Test anonymous code blocks. -DO $$ -DECLARE r record; -BEGIN - FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno - LOOP - RAISE NOTICE '%, %', r.roomno, r.comment; - END LOOP; -END$$; -NOTICE: 001, Entrance -NOTICE: 002, Office -NOTICE: 003, Office -NOTICE: 004, Technical -NOTICE: 101, Office -NOTICE: 102, Conference -NOTICE: 103, Restroom -NOTICE: 104, Technical -NOTICE: 105, Office -NOTICE: 106, Office --- these are to check syntax error reporting -DO LANGUAGE plpgsql $$begin return 1; end$$; -ERROR: RETURN cannot have a parameter in function returning void -LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$; - ^ -DO $$ -DECLARE r record; -BEGIN - FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno - LOOP - RAISE NOTICE '%, %', r.roomno, r.comment; - END LOOP; -END$$; -ERROR: column "foo" does not exist -LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomn... - ^ -QUERY: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno -CONTEXT: PL/pgSQL function inline_code_block line 4 at FOR over SELECT rows --- Check handling of errors thrown from/into anonymous code blocks. -do $outer$ -begin - for i in 1..10 loop - begin - execute $ex$ - do $$ - declare x int = 0; - begin - x := 1 / x; - end; - $$; - $ex$; - exception when division_by_zero then - raise notice 'caught division by zero'; - end; - end loop; -end; -$outer$; -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero -NOTICE: caught division by zero --- Check variable scoping -- a var is not available in its own or prior --- default expressions. -create function scope_test() returns int as $$ -declare x int := 42; -begin - declare y int := x + 1; - x int := x + 2; - begin - return x * 100 + y; - end; -end; -$$ language plpgsql; -select scope_test(); - scope_test ------------- - 4443 -(1 row) - -drop function scope_test(); --- Check handling of conflicts between plpgsql vars and table columns. -set plpgsql.variable_conflict = error; -create function conflict_test() returns setof int8_tbl as $$ -declare r record; - q1 bigint := 42; -begin - for r in select q1,q2 from int8_tbl loop - return next r; - end loop; -end; -$$ language plpgsql; -select * from conflict_test(); -ERROR: column reference "q1" is ambiguous -LINE 1: select q1,q2 from int8_tbl - ^ -DETAIL: It could refer to either a PL/pgSQL variable or a table column. -QUERY: select q1,q2 from int8_tbl -CONTEXT: PL/pgSQL function conflict_test() line 5 at FOR over SELECT rows -create or replace function conflict_test() returns setof int8_tbl as $$ -#variable_conflict use_variable -declare r record; - q1 bigint := 42; -begin - for r in select q1,q2 from int8_tbl loop - return next r; - end loop; -end; -$$ language plpgsql; -select * from conflict_test(); - q1 | q2 -----+------------------- - 42 | 456 - 42 | 4567890123456789 - 42 | 123 - 42 | 4567890123456789 - 42 | -4567890123456789 -(5 rows) - -create or replace function conflict_test() returns setof int8_tbl as $$ -#variable_conflict use_column -declare r record; - q1 bigint := 42; -begin - for r in select q1,q2 from int8_tbl loop - return next r; - end loop; -end; -$$ language plpgsql; -select * from conflict_test(); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(5 rows) - -drop function conflict_test(); --- Check that an unreserved keyword can be used as a variable name -create function unreserved_test() returns int as $$ -declare - forward int := 21; -begin - forward := forward * 2; - return forward; -end -$$ language plpgsql; -select unreserved_test(); - unreserved_test ------------------ - 42 -(1 row) - -create or replace function unreserved_test() returns int as $$ -declare - return int := 42; -begin - return := return + 1; - return return; -end -$$ language plpgsql; -select unreserved_test(); - unreserved_test ------------------ - 43 -(1 row) - -create or replace function unreserved_test() returns int as $$ -declare - comment int := 21; -begin - comment := comment * 2; - comment on function unreserved_test() is 'this is a test'; - return comment; -end -$$ language plpgsql; -select unreserved_test(); - unreserved_test ------------------ - 42 -(1 row) - -select obj_description('unreserved_test()'::regprocedure, 'pg_proc'); - obj_description ------------------ - this is a test -(1 row) - -drop function unreserved_test(); --- --- Test FOREACH over arrays --- -create function foreach_test(anyarray) -returns void as $$ -declare x int; -begin - foreach x in array $1 - loop - raise notice '%', x; - end loop; - end; -$$ language plpgsql; -select foreach_test(ARRAY[1,2,3,4]); -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 -NOTICE: 4 - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[1,2],[3,4]]); -NOTICE: 1 -NOTICE: 2 -NOTICE: 3 -NOTICE: 4 - foreach_test --------------- - -(1 row) - -create or replace function foreach_test(anyarray) -returns void as $$ -declare x int; -begin - foreach x slice 1 in array $1 - loop - raise notice '%', x; - end loop; - end; -$$ language plpgsql; --- should fail -select foreach_test(ARRAY[1,2,3,4]); -ERROR: FOREACH ... SLICE loop variable must be of an array type -CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array -select foreach_test(ARRAY[[1,2],[3,4]]); -ERROR: FOREACH ... SLICE loop variable must be of an array type -CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array -create or replace function foreach_test(anyarray) -returns void as $$ -declare x int[]; -begin - foreach x slice 1 in array $1 - loop - raise notice '%', x; - end loop; - end; -$$ language plpgsql; -select foreach_test(ARRAY[1,2,3,4]); -NOTICE: {1,2,3,4} - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[1,2],[3,4]]); -NOTICE: {1,2} -NOTICE: {3,4} - foreach_test --------------- - -(1 row) - --- higher level of slicing -create or replace function foreach_test(anyarray) -returns void as $$ -declare x int[]; -begin - foreach x slice 2 in array $1 - loop - raise notice '%', x; - end loop; - end; -$$ language plpgsql; --- should fail -select foreach_test(ARRAY[1,2,3,4]); -ERROR: slice dimension (2) is out of the valid range 0..1 -CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array --- ok -select foreach_test(ARRAY[[1,2],[3,4]]); -NOTICE: {{1,2},{3,4}} - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[[1,2]],[[3,4]]]); -NOTICE: {{1,2}} -NOTICE: {{3,4}} - foreach_test --------------- - -(1 row) - -create type xy_tuple AS (x int, y int); --- iteration over array of records -create or replace function foreach_test(anyarray) -returns void as $$ -declare r record; -begin - foreach r in array $1 - loop - raise notice '%', r; - end loop; - end; -$$ language plpgsql; -select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); -NOTICE: (10,20) -NOTICE: (40,69) -NOTICE: (35,78) - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); -NOTICE: (10,20) -NOTICE: (40,69) -NOTICE: (35,78) -NOTICE: (88,76) - foreach_test --------------- - -(1 row) - -create or replace function foreach_test(anyarray) -returns void as $$ -declare x int; y int; -begin - foreach x, y in array $1 - loop - raise notice 'x = %, y = %', x, y; - end loop; - end; -$$ language plpgsql; -select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); -NOTICE: x = 10, y = 20 -NOTICE: x = 40, y = 69 -NOTICE: x = 35, y = 78 - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); -NOTICE: x = 10, y = 20 -NOTICE: x = 40, y = 69 -NOTICE: x = 35, y = 78 -NOTICE: x = 88, y = 76 - foreach_test --------------- - -(1 row) - --- slicing over array of composite types -create or replace function foreach_test(anyarray) -returns void as $$ -declare x xy_tuple[]; -begin - foreach x slice 1 in array $1 - loop - raise notice '%', x; - end loop; - end; -$$ language plpgsql; -select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); -NOTICE: {"(10,20)","(40,69)","(35,78)"} - foreach_test --------------- - -(1 row) - -select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); -NOTICE: {"(10,20)","(40,69)"} -NOTICE: {"(35,78)","(88,76)"} - foreach_test --------------- - -(1 row) - -drop function foreach_test(anyarray); -drop type xy_tuple; --- --- Assorted tests for array subscript assignment --- -create temp table rtype (id int, ar text[]); -create function arrayassign1() returns text[] language plpgsql as $$ -declare - r record; -begin - r := row(12, '{foo,bar,baz}')::rtype; - r.ar[2] := 'replace'; - return r.ar; -end$$; -select arrayassign1(); - arrayassign1 -------------------- - {foo,replace,baz} -(1 row) - -select arrayassign1(); -- try again to exercise internal caching - arrayassign1 -------------------- - {foo,replace,baz} -(1 row) - -create domain orderedarray as int[2] - constraint sorted check (value[1] < value[2]); -select '{1,2}'::orderedarray; - orderedarray --------------- - {1,2} -(1 row) - -select '{2,1}'::orderedarray; -- fail -ERROR: value for domain orderedarray violates check constraint "sorted" -create function testoa(x1 int, x2 int, x3 int) returns orderedarray -language plpgsql as $$ -declare res orderedarray; -begin - res := array[x1, x2]; - res[2] := x3; - return res; -end$$; -select testoa(1,2,3); - testoa --------- - {1,3} -(1 row) - -select testoa(1,2,3); -- try again to exercise internal caching - testoa --------- - {1,3} -(1 row) - -select testoa(2,1,3); -- fail at initial assign -ERROR: value for domain orderedarray violates check constraint "sorted" -CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 4 at assignment -select testoa(1,2,1); -- fail at update -ERROR: value for domain orderedarray violates check constraint "sorted" -CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment -drop function arrayassign1(); -drop function testoa(x1 int, x2 int, x3 int); --- --- Test handling of expanded arrays --- -create function returns_rw_array(int) returns int[] -language plpgsql as $$ - declare r int[]; - begin r := array[$1, $1]; return r; end; -$$ stable; -create function consumes_rw_array(int[]) returns int -language plpgsql as $$ - begin return $1[1]; end; -$$ stable; -select consumes_rw_array(returns_rw_array(42)); - consumes_rw_array -------------------- - 42 -(1 row) - --- bug #14174 -explain (verbose, costs off) -select i, a from - (select returns_rw_array(1) as a offset 0) ss, - lateral consumes_rw_array(a) i; - QUERY PLAN ------------------------------------------------------------------ - Nested Loop - Output: i.i, (returns_rw_array(1)) - -> Result - Output: returns_rw_array(1) - -> Function Scan on public.consumes_rw_array i - Output: i.i - Function Call: consumes_rw_array((returns_rw_array(1))) -(7 rows) - -select i, a from - (select returns_rw_array(1) as a offset 0) ss, - lateral consumes_rw_array(a) i; - i | a ----+------- - 1 | {1,1} -(1 row) - -explain (verbose, costs off) -select consumes_rw_array(a), a from returns_rw_array(1) a; - QUERY PLAN --------------------------------------------- - Function Scan on public.returns_rw_array a - Output: consumes_rw_array(a), a - Function Call: returns_rw_array(1) -(3 rows) - -select consumes_rw_array(a), a from returns_rw_array(1) a; - consumes_rw_array | a --------------------+------- - 1 | {1,1} -(1 row) - -explain (verbose, costs off) -select consumes_rw_array(a), a from - (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); - QUERY PLAN ---------------------------------------------------------------------- - Values Scan on "*VALUES*" - Output: consumes_rw_array("*VALUES*".column1), "*VALUES*".column1 -(2 rows) - -select consumes_rw_array(a), a from - (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); - consumes_rw_array | a --------------------+------- - 1 | {1,1} - 2 | {2,2} -(2 rows) - -do $$ -declare a int[] := array[1,2]; -begin - a := a || 3; - raise notice 'a = %', a; -end$$; -NOTICE: a = {1,2,3} --- --- Test access to call stack --- -create function inner_func(int) -returns int as $$ -declare _context text; -begin - get diagnostics _context = pg_context; - raise notice '***%***', _context; - -- lets do it again, just for fun.. - get diagnostics _context = pg_context; - raise notice '***%***', _context; - raise notice 'lets make sure we didnt break anything'; - return 2 * $1; -end; -$$ language plpgsql; -create or replace function outer_func(int) -returns int as $$ -declare - myresult int; -begin - raise notice 'calling down into inner_func()'; - myresult := inner_func($1); - raise notice 'inner_func() done'; - return myresult; -end; -$$ language plpgsql; -create or replace function outer_outer_func(int) -returns int as $$ -declare - myresult int; -begin - raise notice 'calling down into outer_func()'; - myresult := outer_func($1); - raise notice 'outer_func() done'; - return myresult; -end; -$$ language plpgsql; -select outer_outer_func(10); -NOTICE: calling down into outer_func() -NOTICE: calling down into inner_func() -NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: lets make sure we didnt break anything -NOTICE: inner_func() done -NOTICE: outer_func() done - outer_outer_func ------------------- - 20 -(1 row) - --- repeated call should to work -select outer_outer_func(20); -NOTICE: calling down into outer_func() -NOTICE: calling down into inner_func() -NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: lets make sure we didnt break anything -NOTICE: inner_func() done -NOTICE: outer_func() done - outer_outer_func ------------------- - 40 -(1 row) - -drop function outer_outer_func(int); -drop function outer_func(int); -drop function inner_func(int); --- access to call stack from exception -create function inner_func(int) -returns int as $$ -declare - _context text; - sx int := 5; -begin - begin - perform sx / 0; - exception - when division_by_zero then - get diagnostics _context = pg_context; - raise notice '***%***', _context; - end; - - -- lets do it again, just for fun.. - get diagnostics _context = pg_context; - raise notice '***%***', _context; - raise notice 'lets make sure we didnt break anything'; - return 2 * $1; -end; -$$ language plpgsql; -create or replace function outer_func(int) -returns int as $$ -declare - myresult int; -begin - raise notice 'calling down into inner_func()'; - myresult := inner_func($1); - raise notice 'inner_func() done'; - return myresult; -end; -$$ language plpgsql; -create or replace function outer_outer_func(int) -returns int as $$ -declare - myresult int; -begin - raise notice 'calling down into outer_func()'; - myresult := outer_func($1); - raise notice 'outer_func() done'; - return myresult; -end; -$$ language plpgsql; -select outer_outer_func(10); -NOTICE: calling down into outer_func() -NOTICE: calling down into inner_func() -NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: lets make sure we didnt break anything -NOTICE: inner_func() done -NOTICE: outer_func() done - outer_outer_func ------------------- - 20 -(1 row) - --- repeated call should to work -select outer_outer_func(20); -NOTICE: calling down into outer_func() -NOTICE: calling down into inner_func() -NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS -PL/pgSQL function outer_func(integer) line 6 at assignment -PL/pgSQL function outer_outer_func(integer) line 6 at assignment*** -NOTICE: lets make sure we didnt break anything -NOTICE: inner_func() done -NOTICE: outer_func() done - outer_outer_func ------------------- - 40 -(1 row) - -drop function outer_outer_func(int); -drop function outer_func(int); -drop function inner_func(int); --- --- Test ASSERT --- -do $$ -begin - assert 1=1; -- should succeed -end; -$$; -do $$ -begin - assert 1=0; -- should fail -end; -$$; -ERROR: assertion failed -CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT -do $$ -begin - assert NULL; -- should fail -end; -$$; -ERROR: assertion failed -CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT --- check controlling GUC -set plpgsql.check_asserts = off; -do $$ -begin - assert 1=0; -- won't be tested -end; -$$; -reset plpgsql.check_asserts; --- test custom message -do $$ -declare var text := 'some value'; -begin - assert 1=0, format('assertion failed, var = "%s"', var); -end; -$$; -ERROR: assertion failed, var = "some value" -CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT --- ensure assertions are not trapped by 'others' -do $$ -begin - assert 1=0, 'unhandled assertion'; -exception when others then - null; -- do nothing -end; -$$; -ERROR: unhandled assertion -CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT --- Test use of plpgsql in a domain check constraint (cf. bug #14414) -create function plpgsql_domain_check(val int) returns boolean as $$ -begin return val > 0; end -$$ language plpgsql immutable; -create domain plpgsql_domain as integer check(plpgsql_domain_check(value)); -do $$ -declare v_test plpgsql_domain; -begin - v_test := 1; -end; -$$; -do $$ -declare v_test plpgsql_domain := 1; -begin - v_test := 0; -- fail -end; -$$; -ERROR: value for domain plpgsql_domain violates check constraint "plpgsql_domain_check" -CONTEXT: PL/pgSQL function inline_code_block line 4 at assignment --- Test handling of expanded array passed to a domain constraint (bug #14472) -create function plpgsql_arr_domain_check(val int[]) returns boolean as $$ -begin return val[1] > 0; end -$$ language plpgsql immutable; -create domain plpgsql_arr_domain as int[] check(plpgsql_arr_domain_check(value)); -do $$ -declare v_test plpgsql_arr_domain; -begin - v_test := array[1]; - v_test := v_test || 2; -end; -$$; -do $$ -declare v_test plpgsql_arr_domain := array[1]; -begin - v_test := 0 || v_test; -- fail -end; -$$; -ERROR: value for domain plpgsql_arr_domain violates check constraint "plpgsql_arr_domain_check" -CONTEXT: PL/pgSQL function inline_code_block line 4 at assignment --- --- test usage of transition tables in AFTER triggers --- -CREATE TABLE transition_table_base (id int PRIMARY KEY, val text); -CREATE FUNCTION transition_table_base_ins_func() - RETURNS trigger - LANGUAGE plpgsql -AS $$ -DECLARE - t text; - l text; -BEGIN - t = ''; - FOR l IN EXECUTE - $q$ - EXPLAIN (TIMING off, COSTS off, VERBOSE on) - SELECT * FROM newtable - $q$ LOOP - t = t || l || E'\n'; - END LOOP; - - RAISE INFO '%', t; - RETURN new; -END; -$$; -CREATE TRIGGER transition_table_base_ins_trig - AFTER INSERT ON transition_table_base - REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable - FOR EACH STATEMENT - EXECUTE PROCEDURE transition_table_base_ins_func(); -ERROR: OLD TABLE can only be specified for a DELETE or UPDATE trigger -CREATE TRIGGER transition_table_base_ins_trig - AFTER INSERT ON transition_table_base - REFERENCING NEW TABLE AS newtable - FOR EACH STATEMENT - EXECUTE PROCEDURE transition_table_base_ins_func(); -INSERT INTO transition_table_base VALUES (1, 'One'), (2, 'Two'); -INFO: Named Tuplestore Scan - Output: id, val - -INSERT INTO transition_table_base VALUES (3, 'Three'), (4, 'Four'); -INFO: Named Tuplestore Scan - Output: id, val - -CREATE OR REPLACE FUNCTION transition_table_base_upd_func() - RETURNS trigger - LANGUAGE plpgsql -AS $$ -DECLARE - t text; - l text; -BEGIN - t = ''; - FOR l IN EXECUTE - $q$ - EXPLAIN (TIMING off, COSTS off, VERBOSE on) - SELECT * FROM oldtable ot FULL JOIN newtable nt USING (id) - $q$ LOOP - t = t || l || E'\n'; - END LOOP; - - RAISE INFO '%', t; - RETURN new; -END; -$$; -CREATE TRIGGER transition_table_base_upd_trig - AFTER UPDATE ON transition_table_base - REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable - FOR EACH STATEMENT - EXECUTE PROCEDURE transition_table_base_upd_func(); -UPDATE transition_table_base - SET val = '*' || val || '*' - WHERE id BETWEEN 2 AND 3; -INFO: Hash Full Join - Output: COALESCE(ot.id, nt.id), ot.val, nt.val - Hash Cond: (ot.id = nt.id) - -> Named Tuplestore Scan - Output: ot.id, ot.val - -> Hash - Output: nt.id, nt.val - -> Named Tuplestore Scan - Output: nt.id, nt.val - -CREATE TABLE transition_table_level1 -( - level1_no serial NOT NULL , - level1_node_name varchar(255), - PRIMARY KEY (level1_no) -) WITHOUT OIDS; -CREATE TABLE transition_table_level2 -( - level2_no serial NOT NULL , - parent_no int NOT NULL, - level1_node_name varchar(255), - PRIMARY KEY (level2_no) -) WITHOUT OIDS; -CREATE TABLE transition_table_status -( - level int NOT NULL, - node_no int NOT NULL, - status int, - PRIMARY KEY (level, node_no) -) WITHOUT OIDS; -CREATE FUNCTION transition_table_level1_ri_parent_del_func() - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ - DECLARE n bigint; - BEGIN - PERFORM FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no; - IF FOUND THEN - RAISE EXCEPTION 'RI error'; - END IF; - RETURN NULL; - END; -$$; -CREATE TRIGGER transition_table_level1_ri_parent_del_trigger - AFTER DELETE ON transition_table_level1 - REFERENCING OLD TABLE AS p - FOR EACH STATEMENT EXECUTE PROCEDURE - transition_table_level1_ri_parent_del_func(); -CREATE FUNCTION transition_table_level1_ri_parent_upd_func() - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ - DECLARE - x int; - BEGIN - WITH p AS (SELECT level1_no, sum(delta) cnt - FROM (SELECT level1_no, 1 AS delta FROM i - UNION ALL - SELECT level1_no, -1 AS delta FROM d) w - GROUP BY level1_no - HAVING sum(delta) < 0) - SELECT level1_no - FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no - INTO x; - IF FOUND THEN - RAISE EXCEPTION 'RI error'; - END IF; - RETURN NULL; - END; -$$; -CREATE TRIGGER transition_table_level1_ri_parent_upd_trigger - AFTER UPDATE ON transition_table_level1 - REFERENCING OLD TABLE AS d NEW TABLE AS i - FOR EACH STATEMENT EXECUTE PROCEDURE - transition_table_level1_ri_parent_upd_func(); -CREATE FUNCTION transition_table_level2_ri_child_insupd_func() - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ - BEGIN - PERFORM FROM i - LEFT JOIN transition_table_level1 p - ON p.level1_no IS NOT NULL AND p.level1_no = i.parent_no - WHERE p.level1_no IS NULL; - IF FOUND THEN - RAISE EXCEPTION 'RI error'; - END IF; - RETURN NULL; - END; -$$; -CREATE TRIGGER transition_table_level2_ri_child_ins_trigger - AFTER INSERT ON transition_table_level2 - REFERENCING NEW TABLE AS i - FOR EACH STATEMENT EXECUTE PROCEDURE - transition_table_level2_ri_child_insupd_func(); -CREATE TRIGGER transition_table_level2_ri_child_upd_trigger - AFTER UPDATE ON transition_table_level2 - REFERENCING NEW TABLE AS i - FOR EACH STATEMENT EXECUTE PROCEDURE - transition_table_level2_ri_child_insupd_func(); --- create initial test data -INSERT INTO transition_table_level1 (level1_no) - SELECT generate_series(1,200); -ANALYZE transition_table_level1; -INSERT INTO transition_table_level2 (level2_no, parent_no) - SELECT level2_no, level2_no / 50 + 1 AS parent_no - FROM generate_series(1,9999) level2_no; -ANALYZE transition_table_level2; -INSERT INTO transition_table_status (level, node_no, status) - SELECT 1, level1_no, 0 FROM transition_table_level1; -INSERT INTO transition_table_status (level, node_no, status) - SELECT 2, level2_no, 0 FROM transition_table_level2; -ANALYZE transition_table_status; -INSERT INTO transition_table_level1(level1_no) - SELECT generate_series(201,1000); -ANALYZE transition_table_level1; --- behave reasonably if someone tries to modify a transition table -CREATE FUNCTION transition_table_level2_bad_usage_func() - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ - BEGIN - INSERT INTO dx VALUES (1000000, 1000000, 'x'); - RETURN NULL; - END; -$$; -CREATE TRIGGER transition_table_level2_bad_usage_trigger - AFTER DELETE ON transition_table_level2 - REFERENCING OLD TABLE AS dx - FOR EACH STATEMENT EXECUTE PROCEDURE - transition_table_level2_bad_usage_func(); -DELETE FROM transition_table_level2 - WHERE level2_no BETWEEN 301 AND 305; -ERROR: relation "dx" cannot be the target of a modifying statement -CONTEXT: SQL statement "INSERT INTO dx VALUES (1000000, 1000000, 'x')" -PL/pgSQL function transition_table_level2_bad_usage_func() line 3 at SQL statement -DROP TRIGGER transition_table_level2_bad_usage_trigger - ON transition_table_level2; --- attempt modifications which would break RI (should all fail) -DELETE FROM transition_table_level1 - WHERE level1_no = 25; -ERROR: RI error -CONTEXT: PL/pgSQL function transition_table_level1_ri_parent_del_func() line 6 at RAISE -UPDATE transition_table_level1 SET level1_no = -1 - WHERE level1_no = 30; -ERROR: RI error -CONTEXT: PL/pgSQL function transition_table_level1_ri_parent_upd_func() line 15 at RAISE -INSERT INTO transition_table_level2 (level2_no, parent_no) - VALUES (10000, 10000); -ERROR: RI error -CONTEXT: PL/pgSQL function transition_table_level2_ri_child_insupd_func() line 8 at RAISE -UPDATE transition_table_level2 SET parent_no = 2000 - WHERE level2_no = 40; -ERROR: RI error -CONTEXT: PL/pgSQL function transition_table_level2_ri_child_insupd_func() line 8 at RAISE --- attempt modifications which would not break RI (should all succeed) -DELETE FROM transition_table_level1 - WHERE level1_no BETWEEN 201 AND 1000; -DELETE FROM transition_table_level1 - WHERE level1_no BETWEEN 100000000 AND 100000010; -SELECT count(*) FROM transition_table_level1; - count -------- - 200 -(1 row) - -DELETE FROM transition_table_level2 - WHERE level2_no BETWEEN 211 AND 220; -SELECT count(*) FROM transition_table_level2; - count -------- - 9989 -(1 row) - -CREATE TABLE alter_table_under_transition_tables -( - id int PRIMARY KEY, - name text -); -CREATE FUNCTION alter_table_under_transition_tables_upd_func() - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -BEGIN - RAISE WARNING 'old table = %, new table = %', - (SELECT string_agg(id || '=' || name, ',') FROM d), - (SELECT string_agg(id || '=' || name, ',') FROM i); - RAISE NOTICE 'one = %', (SELECT 1 FROM alter_table_under_transition_tables LIMIT 1); - RETURN NULL; -END; -$$; --- should fail, TRUNCATE is not compatible with transition tables -CREATE TRIGGER alter_table_under_transition_tables_upd_trigger - AFTER TRUNCATE OR UPDATE ON alter_table_under_transition_tables - REFERENCING OLD TABLE AS d NEW TABLE AS i - FOR EACH STATEMENT EXECUTE PROCEDURE - alter_table_under_transition_tables_upd_func(); -ERROR: TRUNCATE triggers with transition tables are not supported --- should work -CREATE TRIGGER alter_table_under_transition_tables_upd_trigger - AFTER UPDATE ON alter_table_under_transition_tables - REFERENCING OLD TABLE AS d NEW TABLE AS i - FOR EACH STATEMENT EXECUTE PROCEDURE - alter_table_under_transition_tables_upd_func(); -INSERT INTO alter_table_under_transition_tables - VALUES (1, '1'), (2, '2'), (3, '3'); -UPDATE alter_table_under_transition_tables - SET name = name || name; -WARNING: old table = 1=1,2=2,3=3, new table = 1=11,2=22,3=33 -NOTICE: one = 1 --- now change 'name' to an integer to see what happens... -ALTER TABLE alter_table_under_transition_tables - ALTER COLUMN name TYPE int USING name::integer; -UPDATE alter_table_under_transition_tables - SET name = (name::text || name::text)::integer; -WARNING: old table = 1=11,2=22,3=33, new table = 1=1111,2=2222,3=3333 -NOTICE: one = 1 --- now drop column 'name' -ALTER TABLE alter_table_under_transition_tables - DROP column name; -UPDATE alter_table_under_transition_tables - SET id = id; -ERROR: column "name" does not exist -LINE 1: (SELECT string_agg(id || '=' || name, ',') FROM d) - ^ -QUERY: (SELECT string_agg(id || '=' || name, ',') FROM d) -CONTEXT: PL/pgSQL function alter_table_under_transition_tables_upd_func() line 3 at RAISE --- --- Test multiple reference to a transition table --- -CREATE TABLE multi_test (i int); -INSERT INTO multi_test VALUES (1); -CREATE OR REPLACE FUNCTION multi_test_trig() RETURNS trigger -LANGUAGE plpgsql AS $$ -BEGIN - RAISE NOTICE 'count = %', (SELECT COUNT(*) FROM new_test); - RAISE NOTICE 'count union = %', - (SELECT COUNT(*) - FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss); - RETURN NULL; -END$$; -CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test - REFERENCING NEW TABLE AS new_test OLD TABLE as old_test - FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig(); -UPDATE multi_test SET i = i; -NOTICE: count = 1 -NOTICE: count union = 2 -DROP TABLE multi_test; -DROP FUNCTION multi_test_trig(); --- --- Check type parsing and record fetching from partitioned tables --- -CREATE TABLE partitioned_table (a int, b text) PARTITION BY LIST (a); -CREATE TABLE pt_part1 PARTITION OF partitioned_table FOR VALUES IN (1); -CREATE TABLE pt_part2 PARTITION OF partitioned_table FOR VALUES IN (2); -INSERT INTO partitioned_table VALUES (1, 'Row 1'); -INSERT INTO partitioned_table VALUES (2, 'Row 2'); -CREATE OR REPLACE FUNCTION get_from_partitioned_table(partitioned_table.a%type) -RETURNS partitioned_table AS $$ -DECLARE - a_val partitioned_table.a%TYPE; - result partitioned_table%ROWTYPE; -BEGIN - a_val := $1; - SELECT * INTO result FROM partitioned_table WHERE a = a_val; - RETURN result; -END; $$ LANGUAGE plpgsql; -NOTICE: type reference partitioned_table.a%TYPE converted to integer -SELECT * FROM get_from_partitioned_table(1) AS t; - a | b ----+------- - 1 | Row 1 -(1 row) - -CREATE OR REPLACE FUNCTION list_partitioned_table() -RETURNS SETOF partitioned_table.a%TYPE AS $$ -DECLARE - row partitioned_table%ROWTYPE; - a_val partitioned_table.a%TYPE; -BEGIN - FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP - a_val := row.a; - RETURN NEXT a_val; - END LOOP; - RETURN; -END; $$ LANGUAGE plpgsql; -NOTICE: type reference partitioned_table.a%TYPE converted to integer -SELECT * FROM list_partitioned_table() AS t; - t ---- - 1 - 2 -(2 rows) - --- --- Check argument name is used instead of $n in error message --- -CREATE FUNCTION fx(x WSlot) RETURNS void AS $$ -BEGIN - GET DIAGNOSTICS x = ROW_COUNT; - RETURN; -END; $$ LANGUAGE plpgsql; -ERROR: "x" is not a scalar variable -LINE 3: GET DIAGNOSTICS x = ROW_COUNT; - ^ +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/copy2.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/copy2.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/copy2.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/copy2.out 2021-10-30 22:09:24 -0600 @@ -513,153 +513,10 @@ end $$ language plpgsql immutable; alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); \d+ check_con_tbl - Table "public.check_con_tbl" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - f1 | integer | | | | plain | | -Check constraints: - "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*)) - -copy check_con_tbl from stdin; -NOTICE: input = {"f1":1} -NOTICE: input = {"f1":null} -copy check_con_tbl from stdin; -NOTICE: input = {"f1":0} -ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check" -DETAIL: Failing row contains (0). -CONTEXT: COPY check_con_tbl, line 1: "0" -select * from check_con_tbl; - f1 ----- - 1 - -(2 rows) - --- test with RLS enabled. -CREATE ROLE regress_rls_copy_user; -CREATE ROLE regress_rls_copy_user_colperms; -CREATE TABLE rls_t1 (a int, b int, c int); -COPY rls_t1 (a, b, c) from stdin; -CREATE POLICY p1 ON rls_t1 FOR SELECT USING (a % 2 = 0); -ALTER TABLE rls_t1 ENABLE ROW LEVEL SECURITY; -ALTER TABLE rls_t1 FORCE ROW LEVEL SECURITY; -GRANT SELECT ON TABLE rls_t1 TO regress_rls_copy_user; -GRANT SELECT (a, b) ON TABLE rls_t1 TO regress_rls_copy_user_colperms; --- all columns -COPY rls_t1 TO stdout; -1 4 1 -2 3 2 -3 2 3 -4 1 4 -COPY rls_t1 (a, b, c) TO stdout; -1 4 1 -2 3 2 -3 2 3 -4 1 4 --- subset of columns -COPY rls_t1 (a) TO stdout; -1 -2 -3 -4 -COPY rls_t1 (a, b) TO stdout; -1 4 -2 3 -3 2 -4 1 --- column reordering -COPY rls_t1 (b, a) TO stdout; -4 1 -3 2 -2 3 -1 4 -SET SESSION AUTHORIZATION regress_rls_copy_user; --- all columns -COPY rls_t1 TO stdout; -2 3 2 -4 1 4 -COPY rls_t1 (a, b, c) TO stdout; -2 3 2 -4 1 4 --- subset of columns -COPY rls_t1 (a) TO stdout; -2 -4 -COPY rls_t1 (a, b) TO stdout; -2 3 -4 1 --- column reordering -COPY rls_t1 (b, a) TO stdout; -3 2 -1 4 -RESET SESSION AUTHORIZATION; -SET SESSION AUTHORIZATION regress_rls_copy_user_colperms; --- attempt all columns (should fail) -COPY rls_t1 TO stdout; -ERROR: permission denied for table rls_t1 -COPY rls_t1 (a, b, c) TO stdout; -ERROR: permission denied for table rls_t1 --- try to copy column with no privileges (should fail) -COPY rls_t1 (c) TO stdout; -ERROR: permission denied for table rls_t1 --- subset of columns (should succeed) -COPY rls_t1 (a) TO stdout; -2 -4 -COPY rls_t1 (a, b) TO stdout; -2 3 -4 1 -RESET SESSION AUTHORIZATION; --- test with INSTEAD OF INSERT trigger on a view -CREATE TABLE instead_of_insert_tbl(id serial, name text); -CREATE VIEW instead_of_insert_tbl_view AS SELECT ''::text AS str; -COPY instead_of_insert_tbl_view FROM stdin; -- fail -ERROR: cannot copy to view "instead_of_insert_tbl_view" -HINT: To enable copying to a view, provide an INSTEAD OF INSERT trigger. -CREATE FUNCTION fun_instead_of_insert_tbl() RETURNS trigger AS $$ -BEGIN - INSERT INTO instead_of_insert_tbl (name) VALUES (NEW.str); - RETURN NULL; -END; -$$ LANGUAGE plpgsql; -CREATE TRIGGER trig_instead_of_insert_tbl_view - INSTEAD OF INSERT ON instead_of_insert_tbl_view - FOR EACH ROW EXECUTE PROCEDURE fun_instead_of_insert_tbl(); -COPY instead_of_insert_tbl_view FROM stdin; -SELECT * FROM instead_of_insert_tbl; - id | name -----+------- - 1 | test1 -(1 row) - --- Test of COPY optimization with view using INSTEAD OF INSERT --- trigger when relation is created in the same transaction as --- when COPY is executed. -BEGIN; -CREATE VIEW instead_of_insert_tbl_view_2 as select ''::text as str; -CREATE TRIGGER trig_instead_of_insert_tbl_view_2 - INSTEAD OF INSERT ON instead_of_insert_tbl_view_2 - FOR EACH ROW EXECUTE PROCEDURE fun_instead_of_insert_tbl(); -COPY instead_of_insert_tbl_view_2 FROM stdin; -SELECT * FROM instead_of_insert_tbl; - id | name -----+------- - 1 | test1 - 2 | test1 -(2 rows) - -COMMIT; --- clean up -DROP TABLE forcetest; -DROP TABLE vistest; -DROP FUNCTION truncate_in_subxact(); -DROP TABLE x, y; -DROP TABLE rls_t1 CASCADE; -DROP ROLE regress_rls_copy_user; -DROP ROLE regress_rls_copy_user_colperms; -DROP FUNCTION fn_x_before(); -DROP FUNCTION fn_x_after(); -DROP TABLE instead_of_insert_tbl; -DROP VIEW instead_of_insert_tbl_view; -DROP VIEW instead_of_insert_tbl_view_2; -DROP FUNCTION fun_instead_of_insert_tbl(); +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/temp.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/temp.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/temp.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/temp.out 2021-10-30 22:09:24 -0600 @@ -49,344 +49,10 @@ ^ -- Test ON COMMIT DELETE ROWS CREATE TEMP TABLE temptest(col int) ON COMMIT DELETE ROWS; --- while we're here, verify successful truncation of index with SQL function -CREATE INDEX ON temptest(bit_length('')); -BEGIN; -INSERT INTO temptest VALUES (1); -INSERT INTO temptest VALUES (2); -SELECT * FROM temptest; - col ------ - 1 - 2 -(2 rows) - -COMMIT; -SELECT * FROM temptest; - col ------ -(0 rows) - -DROP TABLE temptest; -BEGIN; -CREATE TEMP TABLE temptest(col) ON COMMIT DELETE ROWS AS SELECT 1; -SELECT * FROM temptest; - col ------ - 1 -(1 row) - -COMMIT; -SELECT * FROM temptest; - col ------ -(0 rows) - -DROP TABLE temptest; --- Test ON COMMIT DROP -BEGIN; -CREATE TEMP TABLE temptest(col int) ON COMMIT DROP; -INSERT INTO temptest VALUES (1); -INSERT INTO temptest VALUES (2); -SELECT * FROM temptest; - col ------ - 1 - 2 -(2 rows) - -COMMIT; -SELECT * FROM temptest; -ERROR: relation "temptest" does not exist -LINE 1: SELECT * FROM temptest; - ^ -BEGIN; -CREATE TEMP TABLE temptest(col) ON COMMIT DROP AS SELECT 1; -SELECT * FROM temptest; - col ------ - 1 -(1 row) - -COMMIT; -SELECT * FROM temptest; -ERROR: relation "temptest" does not exist -LINE 1: SELECT * FROM temptest; - ^ --- ON COMMIT is only allowed for TEMP -CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; -ERROR: ON COMMIT can only be used on temporary tables -CREATE TABLE temptest(col) ON COMMIT DELETE ROWS AS SELECT 1; -ERROR: ON COMMIT can only be used on temporary tables --- Test foreign keys -BEGIN; -CREATE TEMP TABLE temptest1(col int PRIMARY KEY); -CREATE TEMP TABLE temptest2(col int REFERENCES temptest1) - ON COMMIT DELETE ROWS; -INSERT INTO temptest1 VALUES (1); -INSERT INTO temptest2 VALUES (1); -COMMIT; -SELECT * FROM temptest1; - col ------ - 1 -(1 row) - -SELECT * FROM temptest2; - col ------ -(0 rows) - -BEGIN; -CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; -CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); -COMMIT; -ERROR: unsupported ON COMMIT and foreign key combination -DETAIL: Table "temptest4" references "temptest3", but they do not have the same ON COMMIT setting. --- Test manipulation of temp schema's placement in search path -create table public.whereami (f1 text); -insert into public.whereami values ('public'); -create temp table whereami (f1 text); -insert into whereami values ('temp'); -create function public.whoami() returns text - as $$select 'public'::text$$ language sql; -create function pg_temp.whoami() returns text - as $$select 'temp'::text$$ language sql; --- default should have pg_temp implicitly first, but only for tables -select * from whereami; - f1 ------- - temp -(1 row) - -select whoami(); - whoami --------- - public -(1 row) - --- can list temp first explicitly, but it still doesn't affect functions -set search_path = pg_temp, public; -select * from whereami; - f1 ------- - temp -(1 row) - -select whoami(); - whoami --------- - public -(1 row) - --- or put it last for security -set search_path = public, pg_temp; -select * from whereami; - f1 --------- - public -(1 row) - -select whoami(); - whoami --------- - public -(1 row) - --- you can invoke a temp function explicitly, though -select pg_temp.whoami(); - whoami --------- - temp -(1 row) - -drop table public.whereami; --- types in temp schema -set search_path = pg_temp, public; -create domain pg_temp.nonempty as text check (value <> ''); --- function-syntax invocation of types matches rules for functions -select nonempty(''); -ERROR: function nonempty(unknown) does not exist -LINE 1: select nonempty(''); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select pg_temp.nonempty(''); -ERROR: value for domain nonempty violates check constraint "nonempty_check" --- other syntax matches rules for tables -select ''::nonempty; -ERROR: value for domain nonempty violates check constraint "nonempty_check" -reset search_path; --- For partitioned temp tables, ON COMMIT actions ignore storage-less --- partitioned tables. -begin; -create temp table temp_parted_oncommit (a int) - partition by list (a) on commit delete rows; -create temp table temp_parted_oncommit_1 - partition of temp_parted_oncommit - for values in (1) on commit delete rows; -insert into temp_parted_oncommit values (1); -commit; --- partitions are emptied by the previous commit -select * from temp_parted_oncommit; - a ---- -(0 rows) - -drop table temp_parted_oncommit; --- Check dependencies between ON COMMIT actions with a partitioned --- table and its partitions. Using ON COMMIT DROP on a parent removes --- the whole set. -begin; -create temp table temp_parted_oncommit_test (a int) - partition by list (a) on commit drop; -create temp table temp_parted_oncommit_test1 - partition of temp_parted_oncommit_test - for values in (1) on commit delete rows; -create temp table temp_parted_oncommit_test2 - partition of temp_parted_oncommit_test - for values in (2) on commit drop; -insert into temp_parted_oncommit_test values (1), (2); -commit; --- no relations remain in this case. -select relname from pg_class where relname ~ '^temp_parted_oncommit_test'; - relname ---------- -(0 rows) - --- Using ON COMMIT DELETE on a partitioned table does not remove --- all rows if partitions preserve their data. -begin; -create temp table temp_parted_oncommit_test (a int) - partition by list (a) on commit delete rows; -create temp table temp_parted_oncommit_test1 - partition of temp_parted_oncommit_test - for values in (1) on commit preserve rows; -create temp table temp_parted_oncommit_test2 - partition of temp_parted_oncommit_test - for values in (2) on commit drop; -insert into temp_parted_oncommit_test values (1), (2); -commit; --- Data from the remaining partition is still here as its rows are --- preserved. -select * from temp_parted_oncommit_test; - a ---- - 1 -(1 row) - --- two relations remain in this case. -select relname from pg_class where relname ~ '^temp_parted_oncommit_test' - order by relname; - relname ----------------------------- - temp_parted_oncommit_test - temp_parted_oncommit_test1 -(2 rows) - -drop table temp_parted_oncommit_test; --- Check dependencies between ON COMMIT actions with inheritance trees. --- Using ON COMMIT DROP on a parent removes the whole set. -begin; -create temp table temp_inh_oncommit_test (a int) on commit drop; -create temp table temp_inh_oncommit_test1 () - inherits(temp_inh_oncommit_test) on commit delete rows; -insert into temp_inh_oncommit_test1 values (1); -commit; --- no relations remain in this case -select relname from pg_class where relname ~ '^temp_inh_oncommit_test'; - relname ---------- -(0 rows) - --- Data on the parent is removed, and the child goes away. -begin; -create temp table temp_inh_oncommit_test (a int) on commit delete rows; -create temp table temp_inh_oncommit_test1 () - inherits(temp_inh_oncommit_test) on commit drop; -insert into temp_inh_oncommit_test1 values (1); -insert into temp_inh_oncommit_test values (1); -commit; -select * from temp_inh_oncommit_test; - a ---- -(0 rows) - --- one relation remains -select relname from pg_class where relname ~ '^temp_inh_oncommit_test'; - relname ------------------------- - temp_inh_oncommit_test -(1 row) - -drop table temp_inh_oncommit_test; --- Tests with two-phase commit --- Transactions creating objects in a temporary namespace cannot be used --- with two-phase commit. --- These cases generate errors about temporary namespace. --- Function creation -begin; -create function pg_temp.twophase_func() returns void as - $$ select '2pc_func'::text $$ language sql; -prepare transaction 'twophase_func'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects --- Function drop -create function pg_temp.twophase_func() returns void as - $$ select '2pc_func'::text $$ language sql; -begin; -drop function pg_temp.twophase_func(); -prepare transaction 'twophase_func'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects --- Operator creation -begin; -create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi); -prepare transaction 'twophase_operator'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects --- These generate errors about temporary tables. -begin; -create type pg_temp.twophase_type as (a int); -prepare transaction 'twophase_type'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects -begin; -create view pg_temp.twophase_view as select 1; -prepare transaction 'twophase_view'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects -begin; -create sequence pg_temp.twophase_seq; -prepare transaction 'twophase_sequence'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects --- Temporary tables cannot be used with two-phase commit. -create temp table twophase_tab (a int); -begin; -select a from twophase_tab; - a ---- -(0 rows) - -prepare transaction 'twophase_tab'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects -begin; -insert into twophase_tab values (1); -prepare transaction 'twophase_tab'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects -begin; -lock twophase_tab in access exclusive mode; -prepare transaction 'twophase_tab'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects -begin; -drop table twophase_tab; -prepare transaction 'twophase_tab'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects --- Corner case: current_schema may create a temporary schema if namespace --- creation is pending, so check after that. First reset the connection --- to remove the temporary namespace. -\c - -SET search_path TO 'pg_temp'; -BEGIN; -SELECT current_schema() ~ 'pg_temp' AS is_temp_schema; - is_temp_schema ----------------- - t -(1 row) - -PREPARE TRANSACTION 'twophase_search'; -ERROR: cannot PREPARE a transaction that has operated on temporary objects +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/domain.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/domain.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/domain.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/domain.out 2021-10-30 22:09:24 -0600 @@ -83,1045 +83,10 @@ (1 row) drop table basictest; -drop domain domainvarchar restrict; -drop domain domainnumeric restrict; -drop domain domainint4 restrict; -drop domain domaintext; --- Test domains over array types -create domain domainint4arr int4[1]; -create domain domainchar4arr varchar(4)[2][3]; -create table domarrtest - ( testint4arr domainint4arr - , testchar4arr domainchar4arr - ); -INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"}}'); -INSERT INTO domarrtest values ('{{2,2},{2,2}}', '{{"a","b"}}'); -INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"},{"e","f"}}'); -INSERT INTO domarrtest values ('{2,2}', '{{"a"},{"c"}}'); -INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}'); -INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}'); -ERROR: value too long for type character varying(4) -INSERT INTO domarrtest (testint4arr[1], testint4arr[3]) values (11,22); -select * from domarrtest; - testint4arr | testchar4arr ----------------+--------------------- - {2,2} | {{a,b},{c,d}} - {{2,2},{2,2}} | {{a,b}} - {2,2} | {{a,b},{c,d},{e,f}} - {2,2} | {{a},{c}} - | {{a,b,c},{d,e,f}} - {11,NULL,22} | -(6 rows) - -select testint4arr[1], testchar4arr[2:2] from domarrtest; - testint4arr | testchar4arr --------------+-------------- - 2 | {{c,d}} - | {} - 2 | {{c,d}} - 2 | {{c}} - | {{d,e,f}} - 11 | -(6 rows) - -select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; - array_dims | array_dims -------------+------------ - [1:2] | [1:2][1:2] - [1:2][1:2] | [1:1][1:2] - [1:2] | [1:3][1:2] - [1:2] | [1:2][1:1] - | [1:2][1:3] - [1:3] | -(6 rows) - -COPY domarrtest FROM stdin; -COPY domarrtest FROM stdin; -- fail -ERROR: value too long for type character varying(4) -CONTEXT: COPY domarrtest, line 1, column testchar4arr: "{qwerty,w,e}" -select * from domarrtest; - testint4arr | testchar4arr ----------------+--------------------- - {2,2} | {{a,b},{c,d}} - {{2,2},{2,2}} | {{a,b}} - {2,2} | {{a,b},{c,d},{e,f}} - {2,2} | {{a},{c}} - | {{a,b,c},{d,e,f}} - {11,NULL,22} | - {3,4} | {q,w,e} - | -(8 rows) - -update domarrtest set - testint4arr[1] = testint4arr[1] + 1, - testint4arr[3] = testint4arr[3] - 1 -where testchar4arr is null; -select * from domarrtest where testchar4arr is null; - testint4arr | testchar4arr -------------------+-------------- - {12,NULL,21} | - {NULL,NULL,NULL} | -(2 rows) - -drop table domarrtest; -drop domain domainint4arr restrict; -drop domain domainchar4arr restrict; -create domain dia as int[]; -select '{1,2,3}'::dia; - dia ---------- - {1,2,3} -(1 row) - -select array_dims('{1,2,3}'::dia); - array_dims ------------- - [1:3] -(1 row) - -select pg_typeof('{1,2,3}'::dia); - pg_typeof ------------ - dia -(1 row) - -select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia - pg_typeof ------------ - integer[] -(1 row) - -drop domain dia; --- Test domains over composites -create type comptype as (r float8, i float8); -create domain dcomptype as comptype; -create table dcomptable (d1 dcomptype unique); -insert into dcomptable values (row(1,2)::dcomptype); -insert into dcomptable values (row(3,4)::comptype); -insert into dcomptable values (row(1,2)::dcomptype); -- fail on uniqueness -ERROR: duplicate key value violates unique constraint "dcomptable_d1_key" -DETAIL: Key (d1)=((1,2)) already exists. -insert into dcomptable (d1.r) values(11); -select * from dcomptable; - d1 -------- - (1,2) - (3,4) - (11,) -(3 rows) - -select (d1).r, (d1).i, (d1).* from dcomptable; - r | i | r | i -----+---+----+--- - 1 | 2 | 1 | 2 - 3 | 4 | 3 | 4 - 11 | | 11 | -(3 rows) - -update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; -select * from dcomptable; - d1 -------- - (11,) - (2,2) - (4,4) -(3 rows) - -alter domain dcomptype add constraint c1 check ((value).r <= (value).i); -alter domain dcomptype add constraint c2 check ((value).r > (value).i); -- fail -ERROR: column "d1" of table "dcomptable" contains values that violate the new constraint -select row(2,1)::dcomptype; -- fail -ERROR: value for domain dcomptype violates check constraint "c1" -insert into dcomptable values (row(1,2)::comptype); -insert into dcomptable values (row(2,1)::comptype); -- fail -ERROR: value for domain dcomptype violates check constraint "c1" -insert into dcomptable (d1.r) values(99); -insert into dcomptable (d1.r, d1.i) values(99, 100); -insert into dcomptable (d1.r, d1.i) values(100, 99); -- fail -ERROR: value for domain dcomptype violates check constraint "c1" -update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; -- fail -ERROR: value for domain dcomptype violates check constraint "c1" -update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; -select * from dcomptable; - d1 ----------- - (11,) - (99,) - (1,3) - (3,5) - (0,3) - (98,101) -(6 rows) - -explain (verbose, costs off) - update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; - QUERY PLAN ------------------------------------------------------------------------------------------------ - Update on public.dcomptable - -> Seq Scan on public.dcomptable - Output: ROW(((d1).r - '1'::double precision), ((d1).i + '1'::double precision)), ctid - Filter: ((dcomptable.d1).i > '0'::double precision) -(4 rows) - -create rule silly as on delete to dcomptable do instead - update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; -\d+ dcomptable - Table "public.dcomptable" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+-----------+-----------+----------+---------+----------+--------------+------------- - d1 | dcomptype | | | | extended | | -Indexes: - "dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1) -Rules: - silly AS - ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1.r = (dcomptable.d1).r - 1::double precision, d1.i = (dcomptable.d1).i + 1::double precision - WHERE (dcomptable.d1).i > 0::double precision - -drop table dcomptable; -drop type comptype cascade; -NOTICE: drop cascades to type dcomptype --- check altering and dropping columns used by domain constraints -create type comptype as (r float8, i float8); -create domain dcomptype as comptype; -alter domain dcomptype add constraint c1 check ((value).r > 0); -comment on constraint c1 on domain dcomptype is 'random commentary'; -select row(0,1)::dcomptype; -- fail -ERROR: value for domain dcomptype violates check constraint "c1" -alter type comptype alter attribute r type varchar; -- fail -ERROR: operator does not exist: character varying > double precision -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -alter type comptype alter attribute r type bigint; -alter type comptype drop attribute r; -- fail -ERROR: cannot drop column r of composite type comptype because other objects depend on it -DETAIL: constraint c1 depends on column r of composite type comptype -HINT: Use DROP ... CASCADE to drop the dependent objects too. -alter type comptype drop attribute i; -select conname, obj_description(oid, 'pg_constraint') from pg_constraint - where contypid = 'dcomptype'::regtype; -- check comment is still there - conname | obj_description ----------+------------------- - c1 | random commentary -(1 row) - -drop type comptype cascade; -NOTICE: drop cascades to type dcomptype --- Test domains over arrays of composite -create type comptype as (r float8, i float8); -create domain dcomptypea as comptype[]; -create table dcomptable (d1 dcomptypea unique); -insert into dcomptable values (array[row(1,2)]::dcomptypea); -insert into dcomptable values (array[row(3,4), row(5,6)]::comptype[]); -insert into dcomptable values (array[row(7,8)::comptype, row(9,10)::comptype]); -insert into dcomptable values (array[row(1,2)]::dcomptypea); -- fail on uniqueness -ERROR: duplicate key value violates unique constraint "dcomptable_d1_key" -DETAIL: Key (d1)=({"(1,2)"}) already exists. -insert into dcomptable (d1[1]) values(row(9,10)); -insert into dcomptable (d1[1].r) values(11); -select * from dcomptable; - d1 --------------------- - {"(1,2)"} - {"(3,4)","(5,6)"} - {"(7,8)","(9,10)"} - {"(9,10)"} - {"(11,)"} -(5 rows) - -select d1[2], d1[1].r, d1[1].i from dcomptable; - d1 | r | i ---------+----+---- - | 1 | 2 - (5,6) | 3 | 4 - (9,10) | 7 | 8 - | 9 | 10 - | 11 | -(5 rows) - -update dcomptable set d1[2] = row(d1[2].i, d1[2].r); -select * from dcomptable; - d1 --------------------- - {"(1,2)","(,)"} - {"(3,4)","(6,5)"} - {"(7,8)","(10,9)"} - {"(9,10)","(,)"} - {"(11,)","(,)"} -(5 rows) - -update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -select * from dcomptable; - d1 --------------------- - {"(11,)","(,)"} - {"(2,2)","(,)"} - {"(4,4)","(6,5)"} - {"(8,8)","(10,9)"} - {"(10,10)","(,)"} -(5 rows) - -alter domain dcomptypea add constraint c1 check (value[1].r <= value[1].i); -alter domain dcomptypea add constraint c2 check (value[1].r > value[1].i); -- fail -ERROR: column "d1" of table "dcomptable" contains values that violate the new constraint -select array[row(2,1)]::dcomptypea; -- fail -ERROR: value for domain dcomptypea violates check constraint "c1" -insert into dcomptable values (array[row(1,2)]::comptype[]); -insert into dcomptable values (array[row(2,1)]::comptype[]); -- fail -ERROR: value for domain dcomptypea violates check constraint "c1" -insert into dcomptable (d1[1].r) values(99); -insert into dcomptable (d1[1].r, d1[1].i) values(99, 100); -insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail -ERROR: value for domain dcomptypea violates check constraint "c1" -update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail -ERROR: value for domain dcomptypea violates check constraint "c1" -update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 - where d1[1].i > 0; -select * from dcomptable; - d1 --------------------- - {"(11,)","(,)"} - {"(99,)"} - {"(1,3)","(,)"} - {"(3,5)","(6,5)"} - {"(7,9)","(10,9)"} - {"(9,11)","(,)"} - {"(0,3)"} - {"(98,101)"} -(8 rows) - -explain (verbose, costs off) - update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 - where d1[1].i > 0; - QUERY PLAN ----------------------------------------------------------------------------------------------------------------- - Update on public.dcomptable - -> Seq Scan on public.dcomptable - Output: (d1[1].r := (d1[1].r - '1'::double precision))[1].i := (d1[1].i + '1'::double precision), ctid - Filter: (dcomptable.d1[1].i > '0'::double precision) -(4 rows) - -create rule silly as on delete to dcomptable do instead - update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 - where d1[1].i > 0; -\d+ dcomptable - Table "public.dcomptable" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+------------+-----------+----------+---------+----------+--------------+------------- - d1 | dcomptypea | | | | extended | | -Indexes: - "dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1) -Rules: - silly AS - ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1[1].r = dcomptable.d1[1].r - 1::double precision, d1[1].i = dcomptable.d1[1].i + 1::double precision - WHERE dcomptable.d1[1].i > 0::double precision - -drop table dcomptable; -drop type comptype cascade; -NOTICE: drop cascades to type dcomptypea --- Test arrays over domains -create domain posint as int check (value > 0); -create table pitable (f1 posint[]); -insert into pitable values(array[42]); -insert into pitable values(array[-1]); -- fail -ERROR: value for domain posint violates check constraint "posint_check" -insert into pitable values('{0}'); -- fail -ERROR: value for domain posint violates check constraint "posint_check" -LINE 1: insert into pitable values('{0}'); - ^ -update pitable set f1[1] = f1[1] + 1; -update pitable set f1[1] = 0; -- fail -ERROR: value for domain posint violates check constraint "posint_check" -select * from pitable; - f1 ------- - {43} -(1 row) - -drop table pitable; -create domain vc4 as varchar(4); -create table vc4table (f1 vc4[]); -insert into vc4table values(array['too long']); -- fail -ERROR: value too long for type character varying(4) -insert into vc4table values(array['too long']::vc4[]); -- cast truncates -select * from vc4table; - f1 ----------- - {"too "} -(1 row) - -drop table vc4table; -drop type vc4; --- You can sort of fake arrays-of-arrays by putting a domain in between -create domain dposinta as posint[]; -create table dposintatable (f1 dposinta[]); -insert into dposintatable values(array[array[42]]); -- fail -ERROR: column "f1" is of type dposinta[] but expression is of type integer[] -LINE 1: insert into dposintatable values(array[array[42]]); - ^ -HINT: You will need to rewrite or cast the expression. -insert into dposintatable values(array[array[42]::posint[]]); -- still fail -ERROR: column "f1" is of type dposinta[] but expression is of type posint[] -LINE 1: insert into dposintatable values(array[array[42]::posint[]])... - ^ -HINT: You will need to rewrite or cast the expression. -insert into dposintatable values(array[array[42]::dposinta]); -- but this works -select f1, f1[1], (f1[1])[1] from dposintatable; - f1 | f1 | f1 -----------+------+---- - {"{42}"} | {42} | 42 -(1 row) - -select pg_typeof(f1) from dposintatable; - pg_typeof ------------- - dposinta[] -(1 row) - -select pg_typeof(f1[1]) from dposintatable; - pg_typeof ------------ - dposinta -(1 row) - -select pg_typeof(f1[1][1]) from dposintatable; - pg_typeof ------------ - dposinta -(1 row) - -select pg_typeof((f1[1])[1]) from dposintatable; - pg_typeof ------------ - posint -(1 row) - -update dposintatable set f1[2] = array[99]; -select f1, f1[1], (f1[2])[1] from dposintatable; - f1 | f1 | f1 ------------------+------+---- - {"{42}","{99}"} | {42} | 99 -(1 row) - --- it'd be nice if you could do something like this, but for now you can't: -update dposintatable set f1[2][1] = array[97]; -ERROR: wrong number of array subscripts --- maybe someday we can make this syntax work: -update dposintatable set (f1[2])[1] = array[98]; -ERROR: syntax error at or near "[" -LINE 1: update dposintatable set (f1[2])[1] = array[98]; - ^ -drop table dposintatable; -drop domain posint cascade; -NOTICE: drop cascades to type dposinta --- Test arrays over domains of composite -create type comptype as (cf1 int, cf2 int); -create domain dcomptype as comptype check ((value).cf1 > 0); -create table dcomptable (f1 dcomptype[]); -insert into dcomptable values (null); -update dcomptable set f1[1].cf2 = 5; -table dcomptable; - f1 ----------- - {"(,5)"} -(1 row) - -update dcomptable set f1[1].cf1 = -1; -- fail -ERROR: value for domain dcomptype violates check constraint "dcomptype_check" -update dcomptable set f1[1].cf1 = 1; -table dcomptable; - f1 ------------ - {"(1,5)"} -(1 row) - -drop table dcomptable; -drop type comptype cascade; -NOTICE: drop cascades to type dcomptype --- Test not-null restrictions -create domain dnotnull varchar(15) NOT NULL; -create domain dnull varchar(15); -create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd'); -create table nulltest - ( col1 dnotnull - , col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden - , col3 dnull NOT NULL - , col4 dnull - , col5 dcheck CHECK (col5 IN ('c', 'd')) - ); -INSERT INTO nulltest DEFAULT VALUES; -ERROR: domain dnotnull does not allow null values -INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good -insert into nulltest values ('a', 'b', 'c', 'd', NULL); -ERROR: domain dcheck does not allow null values -insert into nulltest values ('a', 'b', 'c', 'd', 'a'); -ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" -DETAIL: Failing row contains (a, b, c, d, a). -INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd'); -ERROR: domain dnotnull does not allow null values -INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c'); -ERROR: domain dnotnull does not allow null values -INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c'); -ERROR: null value in column "col3" of relation "nulltest" violates not-null constraint -DETAIL: Failing row contains (a, b, null, d, c). -INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good --- Test copy -COPY nulltest FROM stdin; --fail -ERROR: null value in column "col3" of relation "nulltest" violates not-null constraint -DETAIL: Failing row contains (a, b, null, d, d). -CONTEXT: COPY nulltest, line 1: "a b \N d d" -COPY nulltest FROM stdin; --fail -ERROR: domain dcheck does not allow null values -CONTEXT: COPY nulltest, line 1, column col5: null input --- Last row is bad -COPY nulltest FROM stdin; -ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" -DETAIL: Failing row contains (a, b, c, null, a). -CONTEXT: COPY nulltest, line 3: "a b c \N a" -select * from nulltest; - col1 | col2 | col3 | col4 | col5 -------+------+------+------+------ - a | b | c | d | c - a | b | c | | d -(2 rows) - --- Test out coerced (casted) constraints -SELECT cast('1' as dnotnull); - dnotnull ----------- - 1 -(1 row) - -SELECT cast(NULL as dnotnull); -- fail -ERROR: domain dnotnull does not allow null values -SELECT cast(cast(NULL as dnull) as dnotnull); -- fail -ERROR: domain dnotnull does not allow null values -SELECT cast(col4 as dnotnull) from nulltest; -- fail -ERROR: domain dnotnull does not allow null values --- cleanup -drop table nulltest; -drop domain dnotnull restrict; -drop domain dnull restrict; -drop domain dcheck restrict; -create domain ddef1 int4 DEFAULT 3; -create domain ddef2 oid DEFAULT '12'; --- Type mixing, function returns int8 -create domain ddef3 text DEFAULT 5; -create sequence ddef4_seq; -create domain ddef4 int4 DEFAULT nextval('ddef4_seq'); -create domain ddef5 numeric(8,2) NOT NULL DEFAULT '12.12'; -create table defaulttest - ( col1 ddef1 - , col2 ddef2 - , col3 ddef3 - , col4 ddef4 PRIMARY KEY - , col5 ddef1 NOT NULL DEFAULT NULL - , col6 ddef2 DEFAULT '88' - , col7 ddef4 DEFAULT 8000 - , col8 ddef5 - ); -insert into defaulttest(col4) values(0); -- fails, col5 defaults to null -ERROR: null value in column "col5" of relation "defaulttest" violates not-null constraint -DETAIL: Failing row contains (3, 12, 5, 0, null, 88, 8000, 12.12). -alter table defaulttest alter column col5 drop default; -insert into defaulttest default values; -- succeeds, inserts domain default --- We used to treat SET DEFAULT NULL as equivalent to DROP DEFAULT; wrong -alter table defaulttest alter column col5 set default null; -insert into defaulttest(col4) values(0); -- fails -ERROR: null value in column "col5" of relation "defaulttest" violates not-null constraint -DETAIL: Failing row contains (3, 12, 5, 0, null, 88, 8000, 12.12). -alter table defaulttest alter column col5 drop default; -insert into defaulttest default values; -insert into defaulttest default values; --- Test defaults with copy -COPY defaulttest(col5) FROM stdin; -select * from defaulttest; - col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 -------+------+------+------+------+------+------+------- - 3 | 12 | 5 | 1 | 3 | 88 | 8000 | 12.12 - 3 | 12 | 5 | 2 | 3 | 88 | 8000 | 12.12 - 3 | 12 | 5 | 3 | 3 | 88 | 8000 | 12.12 - 3 | 12 | 5 | 4 | 42 | 88 | 8000 | 12.12 -(4 rows) - -drop table defaulttest cascade; --- Test ALTER DOMAIN .. NOT NULL -create domain dnotnulltest integer; -create table domnotnull -( col1 dnotnulltest -, col2 dnotnulltest -); -insert into domnotnull default values; -alter domain dnotnulltest set not null; -- fails -ERROR: column "col1" of table "domnotnull" contains null values -update domnotnull set col1 = 5; -alter domain dnotnulltest set not null; -- fails -ERROR: column "col2" of table "domnotnull" contains null values -update domnotnull set col2 = 6; -alter domain dnotnulltest set not null; -update domnotnull set col1 = null; -- fails -ERROR: domain dnotnulltest does not allow null values -alter domain dnotnulltest drop not null; -update domnotnull set col1 = null; -drop domain dnotnulltest cascade; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to column col2 of table domnotnull -drop cascades to column col1 of table domnotnull --- Test ALTER DOMAIN .. DEFAULT .. -create table domdeftest (col1 ddef1); -insert into domdeftest default values; -select * from domdeftest; - col1 ------- - 3 -(1 row) - -alter domain ddef1 set default '42'; -insert into domdeftest default values; -select * from domdeftest; - col1 ------- - 3 - 42 -(2 rows) - -alter domain ddef1 drop default; -insert into domdeftest default values; -select * from domdeftest; - col1 ------- - 3 - 42 - -(3 rows) - -drop table domdeftest; --- Test ALTER DOMAIN .. CONSTRAINT .. -create domain con as integer; -create table domcontest (col1 con); -insert into domcontest values (1); -insert into domcontest values (2); -alter domain con add constraint t check (VALUE < 1); -- fails -ERROR: column "col1" of table "domcontest" contains values that violate the new constraint -alter domain con add constraint t check (VALUE < 34); -alter domain con add check (VALUE > 0); -insert into domcontest values (-5); -- fails -ERROR: value for domain con violates check constraint "con_check" -insert into domcontest values (42); -- fails -ERROR: value for domain con violates check constraint "t" -insert into domcontest values (5); -alter domain con drop constraint t; -insert into domcontest values (-5); --fails -ERROR: value for domain con violates check constraint "con_check" -insert into domcontest values (42); -alter domain con drop constraint nonexistent; -ERROR: constraint "nonexistent" of domain "con" does not exist -alter domain con drop constraint if exists nonexistent; -NOTICE: constraint "nonexistent" of domain "con" does not exist, skipping --- Test ALTER DOMAIN .. CONSTRAINT .. NOT VALID -create domain things AS INT; -CREATE TABLE thethings (stuff things); -INSERT INTO thethings (stuff) VALUES (55); -ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11); -ERROR: column "stuff" of table "thethings" contains values that violate the new constraint -ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11) NOT VALID; -ALTER DOMAIN things VALIDATE CONSTRAINT meow; -ERROR: column "stuff" of table "thethings" contains values that violate the new constraint -UPDATE thethings SET stuff = 10; -ALTER DOMAIN things VALIDATE CONSTRAINT meow; --- Confirm ALTER DOMAIN with RULES. -create table domtab (col1 integer); -create domain dom as integer; -create view domview as select cast(col1 as dom) from domtab; -insert into domtab (col1) values (null); -insert into domtab (col1) values (5); -select * from domview; - col1 ------- - - 5 -(2 rows) - -alter domain dom set not null; -select * from domview; -- fail -ERROR: domain dom does not allow null values -alter domain dom drop not null; -select * from domview; - col1 ------- - - 5 -(2 rows) - -alter domain dom add constraint domchkgt6 check(value > 6); -select * from domview; --fail -ERROR: value for domain dom violates check constraint "domchkgt6" -alter domain dom drop constraint domchkgt6 restrict; -select * from domview; - col1 ------- - - 5 -(2 rows) - --- cleanup -drop domain ddef1 restrict; -drop domain ddef2 restrict; -drop domain ddef3 restrict; -drop domain ddef4 restrict; -drop domain ddef5 restrict; -drop sequence ddef4_seq; --- Test domains over domains -create domain vchar4 varchar(4); -create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x'); -create domain dtop dinter check (substring(VALUE, 2, 1) = '1'); -select 'x123'::dtop; - dtop ------- - x123 -(1 row) - -select 'x1234'::dtop; -- explicit coercion should truncate - dtop ------- - x123 -(1 row) - -select 'y1234'::dtop; -- fail -ERROR: value for domain dtop violates check constraint "dinter_check" -select 'y123'::dtop; -- fail -ERROR: value for domain dtop violates check constraint "dinter_check" -select 'yz23'::dtop; -- fail -ERROR: value for domain dtop violates check constraint "dinter_check" -select 'xz23'::dtop; -- fail -ERROR: value for domain dtop violates check constraint "dtop_check" -create temp table dtest(f1 dtop); -insert into dtest values('x123'); -insert into dtest values('x1234'); -- fail, implicit coercion -ERROR: value too long for type character varying(4) -insert into dtest values('y1234'); -- fail, implicit coercion -ERROR: value too long for type character varying(4) -insert into dtest values('y123'); -- fail -ERROR: value for domain dtop violates check constraint "dinter_check" -insert into dtest values('yz23'); -- fail -ERROR: value for domain dtop violates check constraint "dinter_check" -insert into dtest values('xz23'); -- fail -ERROR: value for domain dtop violates check constraint "dtop_check" -drop table dtest; -drop domain vchar4 cascade; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to type dinter -drop cascades to type dtop --- Make sure that constraints of newly-added domain columns are --- enforced correctly, even if there's no default value for the new --- column. Per bug #1433 -create domain str_domain as text not null; -create table domain_test (a int, b int); -insert into domain_test values (1, 2); -insert into domain_test values (1, 2); --- should fail -alter table domain_test add column c str_domain; -ERROR: domain str_domain does not allow null values -create domain str_domain2 as text check (value <> 'foo') default 'foo'; --- should fail -alter table domain_test add column d str_domain2; -ERROR: value for domain str_domain2 violates check constraint "str_domain2_check" --- Check that domain constraints on prepared statement parameters of --- unknown type are enforced correctly. -create domain pos_int as int4 check (value > 0) not null; -prepare s1 as select $1::pos_int = 10 as "is_ten"; -execute s1(10); - is_ten --------- - t -(1 row) - -execute s1(0); -- should fail -ERROR: value for domain pos_int violates check constraint "pos_int_check" -execute s1(NULL); -- should fail -ERROR: domain pos_int does not allow null values --- Check that domain constraints on plpgsql function parameters, results, --- and local variables are enforced correctly. -create function doubledecrement(p1 pos_int) returns pos_int as $$ -declare v pos_int; -begin - return p1; -end$$ language plpgsql; -select doubledecrement(3); -- fail because of implicit null assignment -ERROR: domain pos_int does not allow null values -CONTEXT: PL/pgSQL function doubledecrement(pos_int) line 3 during statement block local variable initialization -create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ -declare v pos_int := 0; -begin - return p1; -end$$ language plpgsql; -select doubledecrement(3); -- fail at initialization assignment -ERROR: value for domain pos_int violates check constraint "pos_int_check" -CONTEXT: PL/pgSQL function doubledecrement(pos_int) line 3 during statement block local variable initialization -create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ -declare v pos_int := 1; -begin - v := p1 - 1; - return v - 1; -end$$ language plpgsql; -select doubledecrement(null); -- fail before call -ERROR: domain pos_int does not allow null values -select doubledecrement(0); -- fail before call -ERROR: value for domain pos_int violates check constraint "pos_int_check" -select doubledecrement(1); -- fail at assignment to v -ERROR: value for domain pos_int violates check constraint "pos_int_check" -CONTEXT: PL/pgSQL function doubledecrement(pos_int) line 4 at assignment -select doubledecrement(2); -- fail at return -ERROR: value for domain pos_int violates check constraint "pos_int_check" -CONTEXT: PL/pgSQL function doubledecrement(pos_int) while casting return value to function's return type -select doubledecrement(3); -- good - doubledecrement ------------------ - 1 -(1 row) - --- Check that ALTER DOMAIN tests columns of derived types -create domain posint as int4; --- Currently, this doesn't work for composite types, but verify it complains -create type ddtest1 as (f1 posint); -create table ddtest2(f1 ddtest1); -insert into ddtest2 values(row(-1)); -alter domain posint add constraint c1 check(value >= 0); -ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it -drop table ddtest2; --- Likewise for domains within arrays of composite -create table ddtest2(f1 ddtest1[]); -insert into ddtest2 values('{(-1)}'); -alter domain posint add constraint c1 check(value >= 0); -ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it -drop table ddtest2; --- Likewise for domains within domains over composite -create domain ddtest1d as ddtest1; -create table ddtest2(f1 ddtest1d); -insert into ddtest2 values('(-1)'); -alter domain posint add constraint c1 check(value >= 0); -ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it -drop table ddtest2; -drop domain ddtest1d; --- Likewise for domains within domains over array of composite -create domain ddtest1d as ddtest1[]; -create table ddtest2(f1 ddtest1d); -insert into ddtest2 values('{(-1)}'); -alter domain posint add constraint c1 check(value >= 0); -ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it -drop table ddtest2; -drop domain ddtest1d; --- Doesn't work for ranges, either -create type rposint as range (subtype = posint); -create table ddtest2(f1 rposint); -insert into ddtest2 values('(-1,3]'); -alter domain posint add constraint c1 check(value >= 0); -ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it -drop table ddtest2; -drop type rposint; -alter domain posint add constraint c1 check(value >= 0); -create domain posint2 as posint check (value % 2 = 0); -create table ddtest2(f1 posint2); -insert into ddtest2 values(11); -- fail -ERROR: value for domain posint2 violates check constraint "posint2_check" -insert into ddtest2 values(-2); -- fail -ERROR: value for domain posint2 violates check constraint "c1" -insert into ddtest2 values(2); -alter domain posint add constraint c2 check(value >= 10); -- fail -ERROR: column "f1" of table "ddtest2" contains values that violate the new constraint -alter domain posint add constraint c2 check(value > 0); -- OK -drop table ddtest2; -drop type ddtest1; -drop domain posint cascade; -NOTICE: drop cascades to type posint2 --- --- Check enforcement of domain-related typmod in plpgsql (bug #5717) --- -create or replace function array_elem_check(numeric) returns numeric as $$ -declare - x numeric(4,2)[1]; -begin - x[1] := $1; - return x[1]; -end$$ language plpgsql; -select array_elem_check(121.00); -ERROR: numeric field overflow -DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. -CONTEXT: PL/pgSQL function array_elem_check(numeric) line 5 at assignment -select array_elem_check(1.23456); - array_elem_check ------------------- - 1.23 -(1 row) - -create domain mynums as numeric(4,2)[1]; -create or replace function array_elem_check(numeric) returns numeric as $$ -declare - x mynums; -begin - x[1] := $1; - return x[1]; -end$$ language plpgsql; -select array_elem_check(121.00); -ERROR: numeric field overflow -DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. -CONTEXT: PL/pgSQL function array_elem_check(numeric) line 5 at assignment -select array_elem_check(1.23456); - array_elem_check ------------------- - 1.23 -(1 row) - -create domain mynums2 as mynums; -create or replace function array_elem_check(numeric) returns numeric as $$ -declare - x mynums2; -begin - x[1] := $1; - return x[1]; -end$$ language plpgsql; -select array_elem_check(121.00); -ERROR: numeric field overflow -DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. -CONTEXT: PL/pgSQL function array_elem_check(numeric) line 5 at assignment -select array_elem_check(1.23456); - array_elem_check ------------------- - 1.23 -(1 row) - -drop function array_elem_check(numeric); --- --- Check enforcement of array-level domain constraints --- -create domain orderedpair as int[2] check (value[1] < value[2]); -select array[1,2]::orderedpair; - array -------- - {1,2} -(1 row) - -select array[2,1]::orderedpair; -- fail -ERROR: value for domain orderedpair violates check constraint "orderedpair_check" -create temp table op (f1 orderedpair); -insert into op values (array[1,2]); -insert into op values (array[2,1]); -- fail -ERROR: value for domain orderedpair violates check constraint "orderedpair_check" -update op set f1[2] = 3; -update op set f1[2] = 0; -- fail -ERROR: value for domain orderedpair violates check constraint "orderedpair_check" -select * from op; - f1 -------- - {1,3} -(1 row) - -create or replace function array_elem_check(int) returns int as $$ -declare - x orderedpair := '{1,2}'; -begin - x[2] := $1; - return x[2]; -end$$ language plpgsql; -select array_elem_check(3); - array_elem_check ------------------- - 3 -(1 row) - -select array_elem_check(-1); -ERROR: value for domain orderedpair violates check constraint "orderedpair_check" -CONTEXT: PL/pgSQL function array_elem_check(integer) line 5 at assignment -drop function array_elem_check(int); --- --- Check enforcement of changing constraints in plpgsql --- -create domain di as int; -create function dom_check(int) returns di as $$ -declare d di; -begin - d := $1::di; - return d; -end -$$ language plpgsql immutable; -select dom_check(0); - dom_check ------------ - 0 -(1 row) - -alter domain di add constraint pos check (value > 0); -select dom_check(0); -- fail -ERROR: value for domain di violates check constraint "pos" -CONTEXT: PL/pgSQL function dom_check(integer) line 4 at assignment -alter domain di drop constraint pos; -select dom_check(0); - dom_check ------------ - 0 -(1 row) - --- implicit cast during assignment is a separate code path, test that too -create or replace function dom_check(int) returns di as $$ -declare d di; -begin - d := $1; - return d; -end -$$ language plpgsql immutable; -select dom_check(0); - dom_check ------------ - 0 -(1 row) - -alter domain di add constraint pos check (value > 0); -select dom_check(0); -- fail -ERROR: value for domain di violates check constraint "pos" -CONTEXT: PL/pgSQL function dom_check(integer) line 4 at assignment -alter domain di drop constraint pos; -select dom_check(0); - dom_check ------------ - 0 -(1 row) - -drop function dom_check(int); -drop domain di; --- --- Check use of a (non-inline-able) SQL function in a domain constraint; --- this has caused issues in the past --- -create function sql_is_distinct_from(anyelement, anyelement) -returns boolean language sql -as 'select $1 is distinct from $2 limit 1'; -create domain inotnull int - check (sql_is_distinct_from(value, null)); -select 1::inotnull; - inotnull ----------- - 1 -(1 row) - -select null::inotnull; -ERROR: value for domain inotnull violates check constraint "inotnull_check" -create table dom_table (x inotnull); -insert into dom_table values ('1'); -insert into dom_table values (1); -insert into dom_table values (null); -ERROR: value for domain inotnull violates check constraint "inotnull_check" -drop table dom_table; -drop domain inotnull; -drop function sql_is_distinct_from(anyelement, anyelement); --- --- Renaming --- -create domain testdomain1 as int; -alter domain testdomain1 rename to testdomain2; -alter type testdomain2 rename to testdomain3; -- alter type also works -drop domain testdomain3; --- --- Renaming domain constraints --- -create domain testdomain1 as int constraint unsigned check (value > 0); -alter domain testdomain1 rename constraint unsigned to unsigned_foo; -alter domain testdomain1 drop constraint unsigned_foo; -drop domain testdomain1; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rangefuncs.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rangefuncs.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rangefuncs.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rangefuncs.out 2021-10-30 22:09:24 -0600 @@ -82,2366 +82,10 @@ drop view vw_ord; -- multiple functions select * from rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord); - a | b | c | d | ord ----+-----+---+----+----- - 1 | 11 | 2 | 22 | 1 - 1 | 111 | | | 2 -(2 rows) - -create temporary view vw_ord as select * from (values (1)) v(n) join rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord) on (n=ord); -select * from vw_ord; - n | a | b | c | d | ord ----+---+----+---+----+----- - 1 | 1 | 11 | 2 | 22 | 1 -(1 row) - -select definition from pg_views where viewname='vw_ord'; - definition -------------------------------------------------------------------------------------------------------- - SELECT v.n, + - z.a, + - z.b, + - z.c, + - z.d, + - z.ord + - FROM (( VALUES (1)) v(n) + - JOIN ROWS FROM(rngfunct(1), rngfunct(2)) WITH ORDINALITY z(a, b, c, d, ord) ON ((v.n = z.ord))); -(1 row) - -drop view vw_ord; --- expansions of unnest() -select * from unnest(array[10,20],array['foo','bar'],array[1.0]); - unnest | unnest | unnest ---------+--------+-------- - 10 | foo | 1.0 - 20 | bar | -(2 rows) - -select * from unnest(array[10,20],array['foo','bar'],array[1.0]) with ordinality as z(a,b,c,ord); - a | b | c | ord -----+-----+-----+----- - 10 | foo | 1.0 | 1 - 20 | bar | | 2 -(2 rows) - -select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) with ordinality as z(a,b,c,ord); - a | b | c | ord -----+-----+-----+----- - 10 | foo | 1.0 | 1 - 20 | bar | | 2 -(2 rows) - -select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(101,102)) with ordinality as z(a,b,c,ord); - a | b | c | ord -----+-----+-----+----- - 10 | foo | 101 | 1 - 20 | bar | 102 | 2 -(2 rows) - -create temporary view vw_ord as select * from unnest(array[10,20],array['foo','bar'],array[1.0]) as z(a,b,c); -select * from vw_ord; - a | b | c -----+-----+----- - 10 | foo | 1.0 - 20 | bar | -(2 rows) - -select definition from pg_views where viewname='vw_ord'; - definition ----------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + - FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); -(1 row) - -drop view vw_ord; -create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) as z(a,b,c); -select * from vw_ord; - a | b | c -----+-----+----- - 10 | foo | 1.0 - 20 | bar | -(2 rows) - -select definition from pg_views where viewname='vw_ord'; - definition ----------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + - FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); -(1 row) - -drop view vw_ord; -create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(1,2)) as z(a,b,c); -select * from vw_ord; - a | b | c -----+-----+--- - 10 | foo | 1 - 20 | bar | 2 -(2 rows) - -select definition from pg_views where viewname='vw_ord'; - definition ----------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + - FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); -(1 row) - -drop view vw_ord; --- ordinality and multiple functions vs. rewind and reverse scan -begin; -declare rf_cur scroll cursor for select * from rows from(generate_series(1,5),generate_series(1,2)) with ordinality as g(i,j,o); -fetch all from rf_cur; - i | j | o ----+---+--- - 1 | 1 | 1 - 2 | 2 | 2 - 3 | | 3 - 4 | | 4 - 5 | | 5 -(5 rows) - -fetch backward all from rf_cur; - i | j | o ----+---+--- - 5 | | 5 - 4 | | 4 - 3 | | 3 - 2 | 2 | 2 - 1 | 1 | 1 -(5 rows) - -fetch all from rf_cur; - i | j | o ----+---+--- - 1 | 1 | 1 - 2 | 2 | 2 - 3 | | 3 - 4 | | 4 - 5 | | 5 -(5 rows) - -fetch next from rf_cur; - i | j | o ----+---+--- -(0 rows) - -fetch next from rf_cur; - i | j | o ----+---+--- -(0 rows) - -fetch prior from rf_cur; - i | j | o ----+---+--- - 5 | | 5 -(1 row) - -fetch absolute 1 from rf_cur; - i | j | o ----+---+--- - 1 | 1 | 1 -(1 row) - -fetch next from rf_cur; - i | j | o ----+---+--- - 2 | 2 | 2 -(1 row) - -fetch next from rf_cur; - i | j | o ----+---+--- - 3 | | 3 -(1 row) - -fetch next from rf_cur; - i | j | o ----+---+--- - 4 | | 4 -(1 row) - -fetch prior from rf_cur; - i | j | o ----+---+--- - 3 | | 3 -(1 row) - -fetch prior from rf_cur; - i | j | o ----+---+--- - 2 | 2 | 2 -(1 row) - -fetch prior from rf_cur; - i | j | o ----+---+--- - 1 | 1 | 1 -(1 row) - -commit; --- function with implicit LATERAL -select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) z where rngfunc2.f2 = z.f2; - rngfuncid | f2 | rngfuncid | f2 ------------+-----+-----------+----- - 1 | 11 | 1 | 11 - 2 | 22 | 2 | 22 - 1 | 111 | 1 | 111 -(3 rows) - --- function with implicit LATERAL and explicit ORDINALITY -select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) with ordinality as z(rngfuncid,f2,ord) where rngfunc2.f2 = z.f2; - rngfuncid | f2 | rngfuncid | f2 | ord ------------+-----+-----------+-----+----- - 1 | 11 | 1 | 11 | 1 - 2 | 22 | 2 | 22 | 1 - 1 | 111 | 1 | 111 | 2 -(3 rows) - --- function in subselect -select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2; - rngfuncid | f2 ------------+----- - 1 | 11 - 1 | 111 - 2 | 22 -(3 rows) - --- function in subselect -select * from rngfunc2 where f2 in (select f2 from rngfunct(1) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2; - rngfuncid | f2 ------------+----- - 1 | 11 - 1 | 111 -(2 rows) - --- function in subselect -select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = 1) ORDER BY 1,2; - rngfuncid | f2 ------------+----- - 1 | 11 - 1 | 111 -(2 rows) - --- nested functions -select rngfunct.rngfuncid, rngfunct.f2 from rngfunct(sin(pi()/2)::int) ORDER BY 1,2; - rngfuncid | f2 ------------+----- - 1 | 11 - 1 | 111 -(2 rows) - -CREATE TABLE rngfunc (rngfuncid int, rngfuncsubid int, rngfuncname text, primary key(rngfuncid,rngfuncsubid)); -INSERT INTO rngfunc VALUES(1,1,'Joe'); -INSERT INTO rngfunc VALUES(1,2,'Ed'); -INSERT INTO rngfunc VALUES(2,1,'Mary'); --- sql, proretset = f, prorettype = b -CREATE FUNCTION getrngfunc1(int) RETURNS int AS 'SELECT $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc1(1) AS t1; - t1 ----- - 1 -(1 row) - -SELECT * FROM getrngfunc1(1) WITH ORDINALITY AS t1(v,o); - v | o ----+--- - 1 | 1 -(1 row) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1); -SELECT * FROM vw_getrngfunc; - getrngfunc1 -------------- - 1 -(1 row) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1) WITH ORDINALITY as t1(v,o); -SELECT * FROM vw_getrngfunc; - v | o ----+--- - 1 | 1 -(1 row) - -DROP VIEW vw_getrngfunc; --- sql, proretset = t, prorettype = b -CREATE FUNCTION getrngfunc2(int) RETURNS setof int AS 'SELECT rngfuncid FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc2(1) AS t1; - t1 ----- - 1 - 1 -(2 rows) - -SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o); - v | o ----+--- - 1 | 1 - 1 | 2 -(2 rows) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1); -SELECT * FROM vw_getrngfunc; - getrngfunc2 -------------- - 1 - 1 -(2 rows) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o); -SELECT * FROM vw_getrngfunc; - v | o ----+--- - 1 | 1 - 1 | 2 -(2 rows) - -DROP VIEW vw_getrngfunc; --- sql, proretset = t, prorettype = b -CREATE FUNCTION getrngfunc3(int) RETURNS setof text AS 'SELECT rngfuncname FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc3(1) AS t1; - t1 ------ - Joe - Ed -(2 rows) - -SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o); - v | o ------+--- - Joe | 1 - Ed | 2 -(2 rows) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1); -SELECT * FROM vw_getrngfunc; - getrngfunc3 -------------- - Joe - Ed -(2 rows) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o); -SELECT * FROM vw_getrngfunc; - v | o ------+--- - Joe | 1 - Ed | 2 -(2 rows) - -DROP VIEW vw_getrngfunc; --- sql, proretset = f, prorettype = c -CREATE FUNCTION getrngfunc4(int) RETURNS rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc4(1) AS t1; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o); - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 -(1 row) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1); -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o); -SELECT * FROM vw_getrngfunc; - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 -(1 row) - -DROP VIEW vw_getrngfunc; --- sql, proretset = t, prorettype = c -CREATE FUNCTION getrngfunc5(int) RETURNS setof rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc5(1) AS t1; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe - 1 | 2 | Ed -(2 rows) - -SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o); - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 - 1 | 2 | Ed | 2 -(2 rows) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1); -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe - 1 | 2 | Ed -(2 rows) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o); -SELECT * FROM vw_getrngfunc; - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 - 1 | 2 | Ed | 2 -(2 rows) - -DROP VIEW vw_getrngfunc; --- sql, proretset = f, prorettype = record -CREATE FUNCTION getrngfunc6(int) RETURNS RECORD AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc6(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text); - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; - rngfuncid | rngfuncsubid | rngfuncname | ordinality ------------+--------------+-------------+------------ - 1 | 1 | Joe | 1 -(1 row) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc6(1) AS -(rngfuncid int, rngfuncsubid int, rngfuncname text); -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS - SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) - WITH ORDINALITY; -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname | ordinality ------------+--------------+-------------+------------ - 1 | 1 | Joe | 1 -(1 row) - -DROP VIEW vw_getrngfunc; --- sql, proretset = t, prorettype = record -CREATE FUNCTION getrngfunc7(int) RETURNS setof record AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; -SELECT * FROM getrngfunc7(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text); - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe - 1 | 2 | Ed -(2 rows) - -SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; - rngfuncid | rngfuncsubid | rngfuncname | ordinality ------------+--------------+-------------+------------ - 1 | 1 | Joe | 1 - 1 | 2 | Ed | 2 -(2 rows) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc7(1) AS -(rngfuncid int, rngfuncsubid int, rngfuncname text); -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe - 1 | 2 | Ed -(2 rows) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS - SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) - WITH ORDINALITY; -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname | ordinality ------------+--------------+-------------+------------ - 1 | 1 | Joe | 1 - 1 | 2 | Ed | 2 -(2 rows) - -DROP VIEW vw_getrngfunc; --- plpgsql, proretset = f, prorettype = b -CREATE FUNCTION getrngfunc8(int) RETURNS int AS 'DECLARE rngfuncint int; BEGIN SELECT rngfuncid into rngfuncint FROM rngfunc WHERE rngfuncid = $1; RETURN rngfuncint; END;' LANGUAGE plpgsql; -SELECT * FROM getrngfunc8(1) AS t1; - t1 ----- - 1 -(1 row) - -SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o); - v | o ----+--- - 1 | 1 -(1 row) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1); -SELECT * FROM vw_getrngfunc; - getrngfunc8 -------------- - 1 -(1 row) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o); -SELECT * FROM vw_getrngfunc; - v | o ----+--- - 1 | 1 -(1 row) - -DROP VIEW vw_getrngfunc; --- plpgsql, proretset = f, prorettype = c -CREATE FUNCTION getrngfunc9(int) RETURNS rngfunc AS 'DECLARE rngfunctup rngfunc%ROWTYPE; BEGIN SELECT * into rngfunctup FROM rngfunc WHERE rngfuncid = $1; RETURN rngfunctup; END;' LANGUAGE plpgsql; -SELECT * FROM getrngfunc9(1) AS t1; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o); - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 -(1 row) - -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1); -SELECT * FROM vw_getrngfunc; - rngfuncid | rngfuncsubid | rngfuncname ------------+--------------+------------- - 1 | 1 | Joe -(1 row) - -DROP VIEW vw_getrngfunc; -CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o); -SELECT * FROM vw_getrngfunc; - a | b | c | o ----+---+-----+--- - 1 | 1 | Joe | 1 -(1 row) - -DROP VIEW vw_getrngfunc; --- mix 'n match kinds, to exercise expandRTE and related logic -select * from rows from(getrngfunc1(1),getrngfunc2(1),getrngfunc3(1),getrngfunc4(1),getrngfunc5(1), - getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), - getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), - getrngfunc8(1),getrngfunc9(1)) - with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u); - a | b | c | d | e | f | g | h | i | j | k | l | m | o | p | q | r | s | t | u ----+---+-----+---+---+-----+---+---+-----+---+---+-----+---+---+-----+---+---+---+-----+--- - 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | 1 | Joe | 1 - | 1 | Ed | | | | 1 | 2 | Ed | | | | 1 | 2 | Ed | | | | | 2 -(2 rows) - -select * from rows from(getrngfunc9(1),getrngfunc8(1), - getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), - getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), - getrngfunc5(1),getrngfunc4(1),getrngfunc3(1),getrngfunc2(1),getrngfunc1(1)) - with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u); - a | b | c | d | e | f | g | h | i | j | k | l | m | o | p | q | r | s | t | u ----+---+-----+---+---+---+-----+---+---+-----+---+---+-----+---+---+-----+-----+---+---+--- - 1 | 1 | Joe | 1 | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 | Joe | Joe | 1 | 1 | 1 - | | | | 1 | 2 | Ed | | | | 1 | 2 | Ed | | | | Ed | 1 | | 2 -(2 rows) - -create temporary view vw_rngfunc as - select * from rows from(getrngfunc9(1), - getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), - getrngfunc1(1)) - with ordinality as t1(a,b,c,d,e,f,g,n); -select * from vw_rngfunc; - a | b | c | d | e | f | g | n ----+---+-----+---+---+-----+---+--- - 1 | 1 | Joe | 1 | 1 | Joe | 1 | 1 - | | | 1 | 2 | Ed | | 2 -(2 rows) - -select pg_get_viewdef('vw_rngfunc'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + - FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); -(1 row) - -drop view vw_rngfunc; -DROP FUNCTION getrngfunc1(int); -DROP FUNCTION getrngfunc2(int); -DROP FUNCTION getrngfunc3(int); -DROP FUNCTION getrngfunc4(int); -DROP FUNCTION getrngfunc5(int); -DROP FUNCTION getrngfunc6(int); -DROP FUNCTION getrngfunc7(int); -DROP FUNCTION getrngfunc8(int); -DROP FUNCTION getrngfunc9(int); -DROP FUNCTION rngfunct(int); -DROP TABLE rngfunc2; -DROP TABLE rngfunc; --- Rescan tests -- -CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq1; -CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq2; -CREATE TYPE rngfunc_rescan_t AS (i integer, s bigint); -CREATE FUNCTION rngfunc_sql(int,int) RETURNS setof rngfunc_rescan_t AS 'SELECT i, nextval(''rngfunc_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL; --- plpgsql functions use materialize mode -CREATE FUNCTION rngfunc_mat(int,int) RETURNS setof rngfunc_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''rngfunc_rescan_seq2'')); end loop; end;' LANGUAGE plpgsql; ---invokes ExecReScanFunctionScan - all these cases should materialize the function only once --- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function --- is on the inner path of a nestloop join -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) ON (r+i)<100; - r | i | s ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 2 | 11 | 1 - 2 | 12 | 2 - 2 | 13 | 3 - 3 | 11 | 1 - 3 | 12 | 2 - 3 | 13 | 3 -(9 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100; - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 1 | 12 | 2 | 2 - 1 | 13 | 3 | 3 - 2 | 11 | 1 | 1 - 2 | 12 | 2 | 2 - 2 | 13 | 3 | 3 - 3 | 11 | 1 | 1 - 3 | 12 | 2 | 2 - 3 | 13 | 3 | 3 -(9 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) ON (r+i)<100; - r | i | s ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 2 | 11 | 1 - 2 | 12 | 2 - 2 | 13 | 3 - 3 | 11 | 1 - 3 | 12 | 2 - 3 | 13 | 3 -(9 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100; - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 1 | 12 | 2 | 2 - 1 | 13 | 3 | 3 - 2 | 11 | 1 | 1 - 2 | 12 | 2 | 2 - 2 | 13 | 3 | 3 - 3 | 11 | 1 | 1 - 3 | 12 | 2 | 2 - 3 | 13 | 3 | 3 -(9 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( rngfunc_sql(11,13), rngfunc_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100; - r | i1 | s1 | i2 | s2 | o ----+----+----+----+----+--- - 1 | 11 | 1 | 11 | 1 | 1 - 1 | 12 | 2 | 12 | 2 | 2 - 1 | 13 | 3 | 13 | 3 | 3 - 2 | 11 | 1 | 11 | 1 | 1 - 2 | 12 | 2 | 12 | 2 | 2 - 2 | 13 | 3 | 13 | 3 | 3 - 3 | 11 | 1 | 11 | 1 | 1 - 3 | 12 | 2 | 12 | 2 | 2 - 3 | 13 | 3 | 13 | 3 | 3 -(9 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100; - r | i ----+---- - 1 | 11 - 1 | 12 - 1 | 13 - 2 | 11 - 2 | 12 - 2 | 13 - 3 | 11 - 3 | 12 - 3 | 13 -(9 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) WITH ORDINALITY AS f(i,o) ON (r+i)<100; - r | i | o ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 2 | 11 | 1 - 2 | 12 | 2 - 2 | 13 | 3 - 3 | 11 | 1 - 3 | 12 | 2 - 3 | 13 | 3 -(9 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) f(i) ON (r+i)<100; - r | i ----+---- - 1 | 10 - 1 | 20 - 1 | 30 - 2 | 10 - 2 | 20 - 2 | 30 - 3 | 10 - 3 | 20 - 3 | 30 -(9 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH ORDINALITY AS f(i,o) ON (r+i)<100; - r | i | o ----+----+--- - 1 | 10 | 1 - 1 | 20 | 2 - 1 | 30 | 3 - 2 | 10 | 1 - 2 | 20 | 2 - 2 | 30 | 3 - 3 | 10 | 1 - 3 | 20 | 2 - 3 | 30 | 3 -(9 rows) - ---invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL) -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13); - r | i | s ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 2 | 12 | 4 - 2 | 13 | 5 - 3 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13) WITH ORDINALITY AS f(i,s,o); - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 1 | 12 | 2 | 2 - 1 | 13 | 3 | 3 - 2 | 12 | 4 | 1 - 2 | 13 | 5 | 2 - 3 | 13 | 6 | 1 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r); - r | i | s ----+----+--- - 1 | 11 | 1 - 2 | 11 | 2 - 2 | 12 | 3 - 3 | 11 | 4 - 3 | 12 | 5 - 3 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r) WITH ORDINALITY AS f(i,s,o); - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 2 | 11 | 2 | 1 - 2 | 12 | 3 | 2 - 3 | 11 | 4 | 1 - 3 | 12 | 5 | 2 - 3 | 13 | 6 | 3 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2); - r1 | r2 | i | s -----+----+----+---- - 11 | 12 | 11 | 1 - 11 | 12 | 12 | 2 - 13 | 15 | 13 | 3 - 13 | 15 | 14 | 4 - 13 | 15 | 15 | 5 - 16 | 20 | 16 | 6 - 16 | 20 | 17 | 7 - 16 | 20 | 18 | 8 - 16 | 20 | 19 | 9 - 16 | 20 | 20 | 10 -(10 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2) WITH ORDINALITY AS f(i,s,o); - r1 | r2 | i | s | o -----+----+----+----+--- - 11 | 12 | 11 | 1 | 1 - 11 | 12 | 12 | 2 | 2 - 13 | 15 | 13 | 3 | 1 - 13 | 15 | 14 | 4 | 2 - 13 | 15 | 15 | 5 | 3 - 16 | 20 | 16 | 6 | 1 - 16 | 20 | 17 | 7 | 2 - 16 | 20 | 18 | 8 | 3 - 16 | 20 | 19 | 9 | 4 - 16 | 20 | 20 | 10 | 5 -(10 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13); - r | i | s ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 2 | 12 | 4 - 2 | 13 | 5 - 3 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13) WITH ORDINALITY AS f(i,s,o); - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 1 | 12 | 2 | 2 - 1 | 13 | 3 | 3 - 2 | 12 | 4 | 1 - 2 | 13 | 5 | 2 - 3 | 13 | 6 | 1 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r); - r | i | s ----+----+--- - 1 | 11 | 1 - 2 | 11 | 2 - 2 | 12 | 3 - 3 | 11 | 4 - 3 | 12 | 5 - 3 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r) WITH ORDINALITY AS f(i,s,o); - r | i | s | o ----+----+---+--- - 1 | 11 | 1 | 1 - 2 | 11 | 2 | 1 - 2 | 12 | 3 | 2 - 3 | 11 | 4 | 1 - 3 | 12 | 5 | 2 - 3 | 13 | 6 | 3 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2); - r1 | r2 | i | s -----+----+----+---- - 11 | 12 | 11 | 1 - 11 | 12 | 12 | 2 - 13 | 15 | 13 | 3 - 13 | 15 | 14 | 4 - 13 | 15 | 15 | 5 - 16 | 20 | 16 | 6 - 16 | 20 | 17 | 7 - 16 | 20 | 18 | 8 - 16 | 20 | 19 | 9 - 16 | 20 | 20 | 10 -(10 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2) WITH ORDINALITY AS f(i,s,o); - r1 | r2 | i | s | o -----+----+----+----+--- - 11 | 12 | 11 | 1 | 1 - 11 | 12 | 12 | 2 | 2 - 13 | 15 | 13 | 3 | 1 - 13 | 15 | 14 | 4 | 2 - 13 | 15 | 15 | 5 | 3 - 16 | 20 | 16 | 6 | 1 - 16 | 20 | 17 | 7 | 2 - 16 | 20 | 18 | 8 | 3 - 16 | 20 | 19 | 9 | 4 - 16 | 20 | 20 | 10 | 5 -(10 rows) - --- selective rescan of multiple functions: -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(11,11), rngfunc_mat(10+r,13) ); - r | i | s | i | s ----+----+---+----+--- - 1 | 11 | 1 | 11 | 1 - 1 | | | 12 | 2 - 1 | | | 13 | 3 - 2 | 11 | 1 | 12 | 4 - 2 | | | 13 | 5 - 3 | 11 | 1 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(11,11) ); - r | i | s | i | s ----+----+---+----+--- - 1 | 11 | 1 | 11 | 1 - 1 | 12 | 2 | | - 1 | 13 | 3 | | - 2 | 12 | 4 | 11 | 1 - 2 | 13 | 5 | | - 3 | 13 | 6 | 11 | 1 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(10+r,13) ); - r | i | s | i | s ----+----+---+----+--- - 1 | 11 | 1 | 11 | 1 - 1 | 12 | 2 | 12 | 2 - 1 | 13 | 3 | 13 | 3 - 2 | 12 | 4 | 12 | 4 - 2 | 13 | 5 | 13 | 5 - 3 | 13 | 6 | 13 | 6 -(6 rows) - -SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); - setval | setval ---------+-------- - 1 | 1 -(1 row) - -SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( rngfunc_sql(10+r1,13), rngfunc_mat(10+r2,13) ); - r1 | r2 | i | s | i | s -----+----+----+----+----+--- - 1 | 1 | 11 | 1 | 11 | 1 - 1 | 1 | 12 | 2 | 12 | 2 - 1 | 1 | 13 | 3 | 13 | 3 - 1 | 2 | 11 | 4 | 12 | 4 - 1 | 2 | 12 | 5 | 13 | 5 - 1 | 2 | 13 | 6 | | - 1 | 3 | 11 | 7 | 13 | 6 - 1 | 3 | 12 | 8 | | - 1 | 3 | 13 | 9 | | - 2 | 2 | 12 | 10 | 12 | 7 - 2 | 2 | 13 | 11 | 13 | 8 - 2 | 3 | 12 | 12 | 13 | 9 - 2 | 3 | 13 | 13 | | -(13 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i); - r | i ----+---- - 1 | 11 - 1 | 12 - 1 | 13 - 1 | 14 - 1 | 15 - 1 | 16 - 1 | 17 - 1 | 18 - 1 | 19 - 2 | 12 - 2 | 13 - 2 | 14 - 2 | 15 - 2 | 16 - 2 | 17 - 2 | 18 - 3 | 13 - 3 | 14 - 3 | 15 - 3 | 16 - 3 | 17 -(21 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) WITH ORDINALITY AS f(i,o); - r | i | o ----+----+--- - 1 | 11 | 1 - 1 | 12 | 2 - 1 | 13 | 3 - 1 | 14 | 4 - 1 | 15 | 5 - 1 | 16 | 6 - 1 | 17 | 7 - 1 | 18 | 8 - 1 | 19 | 9 - 2 | 12 | 1 - 2 | 13 | 2 - 2 | 14 | 3 - 2 | 15 | 4 - 2 | 16 | 5 - 2 | 17 | 6 - 2 | 18 | 7 - 3 | 13 | 1 - 3 | 14 | 2 - 3 | 15 | 3 - 3 | 16 | 4 - 3 | 17 | 5 -(21 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) f(i); - r | i ----+---- - 1 | 10 - 1 | 20 - 1 | 30 - 2 | 20 - 2 | 40 - 2 | 60 - 3 | 30 - 3 | 60 - 3 | 90 -(9 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) WITH ORDINALITY AS f(i,o); - r | i | o ----+----+--- - 1 | 10 | 1 - 1 | 20 | 2 - 1 | 30 | 3 - 2 | 20 | 1 - 2 | 40 | 2 - 2 | 60 | 3 - 3 | 30 | 1 - 3 | 60 | 2 - 3 | 90 | 3 -(9 rows) - --- deep nesting -SELECT * FROM (VALUES (1),(2),(3)) v1(r1), - LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) - LEFT JOIN generate_series(21,23) f(i) ON ((r2+i)<100) OFFSET 0) s1; - r1 | r1 | r2 | i -----+----+----+---- - 1 | 1 | 10 | 21 - 1 | 1 | 10 | 22 - 1 | 1 | 10 | 23 - 1 | 1 | 20 | 21 - 1 | 1 | 20 | 22 - 1 | 1 | 20 | 23 - 1 | 1 | 30 | 21 - 1 | 1 | 30 | 22 - 1 | 1 | 30 | 23 - 2 | 2 | 10 | 21 - 2 | 2 | 10 | 22 - 2 | 2 | 10 | 23 - 2 | 2 | 20 | 21 - 2 | 2 | 20 | 22 - 2 | 2 | 20 | 23 - 2 | 2 | 30 | 21 - 2 | 2 | 30 | 22 - 2 | 2 | 30 | 23 - 3 | 3 | 10 | 21 - 3 | 3 | 10 | 22 - 3 | 3 | 10 | 23 - 3 | 3 | 20 | 21 - 3 | 3 | 20 | 22 - 3 | 3 | 20 | 23 - 3 | 3 | 30 | 21 - 3 | 3 | 30 | 22 - 3 | 3 | 30 | 23 -(27 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v1(r1), - LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) - LEFT JOIN generate_series(20+r1,23) f(i) ON ((r2+i)<100) OFFSET 0) s1; - r1 | r1 | r2 | i -----+----+----+---- - 1 | 1 | 10 | 21 - 1 | 1 | 10 | 22 - 1 | 1 | 10 | 23 - 1 | 1 | 20 | 21 - 1 | 1 | 20 | 22 - 1 | 1 | 20 | 23 - 1 | 1 | 30 | 21 - 1 | 1 | 30 | 22 - 1 | 1 | 30 | 23 - 2 | 2 | 10 | 22 - 2 | 2 | 10 | 23 - 2 | 2 | 20 | 22 - 2 | 2 | 20 | 23 - 2 | 2 | 30 | 22 - 2 | 2 | 30 | 23 - 3 | 3 | 10 | 23 - 3 | 3 | 20 | 23 - 3 | 3 | 30 | 23 -(18 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v1(r1), - LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) - LEFT JOIN generate_series(r2,r2+3) f(i) ON ((r2+i)<100) OFFSET 0) s1; - r1 | r1 | r2 | i -----+----+----+---- - 1 | 1 | 10 | 10 - 1 | 1 | 10 | 11 - 1 | 1 | 10 | 12 - 1 | 1 | 10 | 13 - 1 | 1 | 20 | 20 - 1 | 1 | 20 | 21 - 1 | 1 | 20 | 22 - 1 | 1 | 20 | 23 - 1 | 1 | 30 | 30 - 1 | 1 | 30 | 31 - 1 | 1 | 30 | 32 - 1 | 1 | 30 | 33 - 2 | 2 | 10 | 10 - 2 | 2 | 10 | 11 - 2 | 2 | 10 | 12 - 2 | 2 | 10 | 13 - 2 | 2 | 20 | 20 - 2 | 2 | 20 | 21 - 2 | 2 | 20 | 22 - 2 | 2 | 20 | 23 - 2 | 2 | 30 | 30 - 2 | 2 | 30 | 31 - 2 | 2 | 30 | 32 - 2 | 2 | 30 | 33 - 3 | 3 | 10 | 10 - 3 | 3 | 10 | 11 - 3 | 3 | 10 | 12 - 3 | 3 | 10 | 13 - 3 | 3 | 20 | 20 - 3 | 3 | 20 | 21 - 3 | 3 | 20 | 22 - 3 | 3 | 20 | 23 - 3 | 3 | 30 | 30 - 3 | 3 | 30 | 31 - 3 | 3 | 30 | 32 - 3 | 3 | 30 | 33 -(36 rows) - -SELECT * FROM (VALUES (1),(2),(3)) v1(r1), - LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) - LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1; - r1 | r1 | r2 | i -----+----+----+--- - 1 | 1 | 10 | 1 - 1 | 1 | 10 | 2 - 1 | 1 | 10 | 3 - 1 | 1 | 10 | 4 - 1 | 1 | 20 | 1 - 1 | 1 | 20 | 2 - 1 | 1 | 20 | 3 - 1 | 1 | 20 | 4 - 1 | 1 | 20 | 5 - 1 | 1 | 20 | 6 - 1 | 1 | 30 | 1 - 1 | 1 | 30 | 2 - 1 | 1 | 30 | 3 - 1 | 1 | 30 | 4 - 1 | 1 | 30 | 5 - 1 | 1 | 30 | 6 - 1 | 1 | 30 | 7 - 1 | 1 | 30 | 8 - 2 | 2 | 10 | 2 - 2 | 2 | 10 | 3 - 2 | 2 | 10 | 4 - 2 | 2 | 20 | 2 - 2 | 2 | 20 | 3 - 2 | 2 | 20 | 4 - 2 | 2 | 20 | 5 - 2 | 2 | 20 | 6 - 2 | 2 | 30 | 2 - 2 | 2 | 30 | 3 - 2 | 2 | 30 | 4 - 2 | 2 | 30 | 5 - 2 | 2 | 30 | 6 - 2 | 2 | 30 | 7 - 2 | 2 | 30 | 8 - 3 | 3 | 10 | 3 - 3 | 3 | 10 | 4 - 3 | 3 | 20 | 3 - 3 | 3 | 20 | 4 - 3 | 3 | 20 | 5 - 3 | 3 | 20 | 6 - 3 | 3 | 30 | 3 - 3 | 3 | 30 | 4 - 3 | 3 | 30 | 5 - 3 | 3 | 30 | 6 - 3 | 3 | 30 | 7 - 3 | 3 | 30 | 8 -(45 rows) - --- check handling of FULL JOIN with multiple lateral references (bug #15741) -SELECT * -FROM (VALUES (1),(2)) v1(r1) - LEFT JOIN LATERAL ( - SELECT * - FROM generate_series(1, v1.r1) AS gs1 - LEFT JOIN LATERAL ( - SELECT * - FROM generate_series(1, gs1) AS gs2 - LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE - ) AS ss1 ON TRUE - FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE - ) AS ss0 ON TRUE; - r1 | gs1 | gs2 | gs3 | gs4 -----+-----+-----+-----+----- - 1 | | | | 1 - 1 | 1 | 1 | 1 | - 2 | | | | 1 - 2 | | | | 2 - 2 | 1 | 1 | 1 | - 2 | 2 | 1 | 1 | - 2 | 2 | 2 | 1 | - 2 | 2 | 2 | 2 | -(8 rows) - -DROP FUNCTION rngfunc_sql(int,int); -DROP FUNCTION rngfunc_mat(int,int); -DROP SEQUENCE rngfunc_rescan_seq1; -DROP SEQUENCE rngfunc_rescan_seq2; --- --- Test cases involving OUT parameters --- -CREATE FUNCTION rngfunc(in f1 int, out f2 int) -AS 'select $1+1' LANGUAGE sql; -SELECT rngfunc(42); - rngfunc ---------- - 43 -(1 row) - -SELECT * FROM rngfunc(42); - f2 ----- - 43 -(1 row) - -SELECT * FROM rngfunc(42) AS p(x); - x ----- - 43 -(1 row) - --- explicit spec of return type is OK -CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS int -AS 'select $1+1' LANGUAGE sql; --- error, wrong result type -CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS float -AS 'select $1+1' LANGUAGE sql; -ERROR: function result type must be integer because of OUT parameters --- with multiple OUT params you must get a RECORD result -CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS int -AS 'select $1+1' LANGUAGE sql; -ERROR: function result type must be record because of OUT parameters -CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) -RETURNS record -AS 'select $1+1' LANGUAGE sql; -ERROR: cannot change return type of existing function -HINT: Use DROP FUNCTION rngfunc(integer) first. -CREATE OR REPLACE FUNCTION rngfuncr(in f1 int, out f2 int, out text) -AS $$select $1-1, $1::text || 'z'$$ LANGUAGE sql; -SELECT f1, rngfuncr(f1) FROM int4_tbl; - f1 | rngfuncr --------------+---------------------------- - 0 | (-1,0z) - 123456 | (123455,123456z) - -123456 | (-123457,-123456z) - 2147483647 | (2147483646,2147483647z) - -2147483647 | (-2147483648,-2147483647z) -(5 rows) - -SELECT * FROM rngfuncr(42); - f2 | column2 -----+--------- - 41 | 42z -(1 row) - -SELECT * FROM rngfuncr(42) AS p(a,b); - a | b -----+----- - 41 | 42z -(1 row) - -CREATE OR REPLACE FUNCTION rngfuncb(in f1 int, inout f2 int, out text) -AS $$select $2-1, $1::text || 'z'$$ LANGUAGE sql; -SELECT f1, rngfuncb(f1, f1/2) FROM int4_tbl; - f1 | rngfuncb --------------+---------------------------- - 0 | (-1,0z) - 123456 | (61727,123456z) - -123456 | (-61729,-123456z) - 2147483647 | (1073741822,2147483647z) - -2147483647 | (-1073741824,-2147483647z) -(5 rows) - -SELECT * FROM rngfuncb(42, 99); - f2 | column2 -----+--------- - 98 | 42z -(1 row) - -SELECT * FROM rngfuncb(42, 99) AS p(a,b); - a | b -----+----- - 98 | 42z -(1 row) - --- Can reference function with or without OUT params for DROP, etc -DROP FUNCTION rngfunc(int); -DROP FUNCTION rngfuncr(in f2 int, out f1 int, out text); -DROP FUNCTION rngfuncb(in f1 int, inout f2 int); --- --- For my next trick, polymorphic OUT parameters --- -CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; -SELECT dup(22); - dup ----------------- - (22,"{22,22}") -(1 row) - -SELECT dup('xyz'); -- fails -ERROR: could not determine polymorphic type because input has type unknown -SELECT dup('xyz'::text); - dup -------------------- - (xyz,"{xyz,xyz}") -(1 row) - -SELECT * FROM dup('xyz'::text); - f2 | f3 ------+----------- - xyz | {xyz,xyz} -(1 row) - --- fails, as we are attempting to rename first argument -CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; -ERROR: cannot change name of input parameter "f1" -HINT: Use DROP FUNCTION dup(anyelement) first. -DROP FUNCTION dup(anyelement); --- equivalent behavior, though different name exposed for input arg -CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; -SELECT dup(22); - dup ----------------- - (22,"{22,22}") -(1 row) - -DROP FUNCTION dup(anyelement); --- fails, no way to deduce outputs -CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; -ERROR: cannot determine result data type -DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray) -AS 'select $1, $2' LANGUAGE sql; -SELECT dup(22, array[44]); - dup ------------ - (22,{44}) -(1 row) - -SELECT dup(4.5, array[44]); - dup ------------- - (4.5,{44}) -(1 row) - -SELECT dup(22, array[44::bigint]); - dup ------------ - (22,{44}) -(1 row) - -SELECT *, pg_typeof(f3), pg_typeof(f4) FROM dup(22, array[44::bigint]); - f3 | f4 | pg_typeof | pg_typeof -----+------+-----------+----------- - 22 | {44} | bigint | bigint[] -(1 row) - -DROP FUNCTION dup(f1 anycompatible, f2 anycompatiblearray); -CREATE FUNCTION dup (f1 anycompatiblerange, f2 out anycompatible, f3 out anycompatiblearray, f4 out anycompatiblerange) -AS 'select lower($1), array[lower($1), upper($1)], $1' LANGUAGE sql; -SELECT dup(int4range(4,7)); - dup ---------------------- - (4,"{4,7}","[4,7)") -(1 row) - -SELECT dup(numrange(4,7)); - dup ---------------------- - (4,"{4,7}","[4,7)") -(1 row) - -SELECT dup(textrange('aaa', 'bbb')); - dup -------------------------------- - (aaa,"{aaa,bbb}","[aaa,bbb)") -(1 row) - -DROP FUNCTION dup(f1 anycompatiblerange); --- fails, no way to deduce outputs -CREATE FUNCTION bad (f1 anyarray, out f2 anycompatible, out f3 anycompatiblearray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; -ERROR: cannot determine result data type -DETAIL: A result of type anycompatible requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, anycompatiblerange, or anycompatiblemultirange. --- --- table functions --- -CREATE OR REPLACE FUNCTION rngfunc() -RETURNS TABLE(a int) -AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql; -SELECT * FROM rngfunc(); - a ---- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -DROP FUNCTION rngfunc(); -CREATE OR REPLACE FUNCTION rngfunc(int) -RETURNS TABLE(a int, b int) -AS $$ SELECT a, b - FROM generate_series(1,$1) a(a), - generate_series(1,$1) b(b) $$ LANGUAGE sql; -SELECT * FROM rngfunc(3); - a | b ----+--- - 1 | 1 - 1 | 2 - 1 | 3 - 2 | 1 - 2 | 2 - 2 | 3 - 3 | 1 - 3 | 2 - 3 | 3 -(9 rows) - -DROP FUNCTION rngfunc(int); --- case that causes change of typmod knowledge during inlining -CREATE OR REPLACE FUNCTION rngfunc() -RETURNS TABLE(a varchar(5)) -AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE; -SELECT * FROM rngfunc() GROUP BY 1; - a -------- - hello -(1 row) - -DROP FUNCTION rngfunc(); --- --- some tests on SQL functions with RETURNING --- -create temp table tt(f1 serial, data text); -create function insert_tt(text) returns int as -$$ insert into tt(data) values($1) returning f1 $$ -language sql; -select insert_tt('foo'); - insert_tt ------------ - 1 -(1 row) - -select insert_tt('bar'); - insert_tt ------------ - 2 -(1 row) - -select * from tt; - f1 | data -----+------ - 1 | foo - 2 | bar -(2 rows) - --- insert will execute to completion even if function needs just 1 row -create or replace function insert_tt(text) returns int as -$$ insert into tt(data) values($1),($1||$1) returning f1 $$ -language sql; -select insert_tt('fool'); - insert_tt ------------ - 3 -(1 row) - -select * from tt; - f1 | data -----+---------- - 1 | foo - 2 | bar - 3 | fool - 4 | foolfool -(4 rows) - --- setof does what's expected -create or replace function insert_tt2(text,text) returns setof int as -$$ insert into tt(data) values($1),($2) returning f1 $$ -language sql; -select insert_tt2('foolish','barrish'); - insert_tt2 ------------- - 5 - 6 -(2 rows) - -select * from insert_tt2('baz','quux'); - insert_tt2 ------------- - 7 - 8 -(2 rows) - -select * from tt; - f1 | data -----+---------- - 1 | foo - 2 | bar - 3 | fool - 4 | foolfool - 5 | foolish - 6 | barrish - 7 | baz - 8 | quux -(8 rows) - --- limit doesn't prevent execution to completion -select insert_tt2('foolish','barrish') limit 1; - insert_tt2 ------------- - 9 -(1 row) - -select * from tt; - f1 | data -----+---------- - 1 | foo - 2 | bar - 3 | fool - 4 | foolfool - 5 | foolish - 6 | barrish - 7 | baz - 8 | quux - 9 | foolish - 10 | barrish -(10 rows) - --- triggers will fire, too -create function noticetrigger() returns trigger as $$ -begin - raise notice 'noticetrigger % %', new.f1, new.data; - return null; -end $$ language plpgsql; -create trigger tnoticetrigger after insert on tt for each row -execute procedure noticetrigger(); -select insert_tt2('foolme','barme') limit 1; -NOTICE: noticetrigger 11 foolme -NOTICE: noticetrigger 12 barme - insert_tt2 ------------- - 11 -(1 row) - -select * from tt; - f1 | data -----+---------- - 1 | foo - 2 | bar - 3 | fool - 4 | foolfool - 5 | foolish - 6 | barrish - 7 | baz - 8 | quux - 9 | foolish - 10 | barrish - 11 | foolme - 12 | barme -(12 rows) - --- and rules work -create temp table tt_log(f1 int, data text); -create rule insert_tt_rule as on insert to tt do also - insert into tt_log values(new.*); -select insert_tt2('foollog','barlog') limit 1; -NOTICE: noticetrigger 13 foollog -NOTICE: noticetrigger 14 barlog - insert_tt2 ------------- - 13 -(1 row) - -select * from tt; - f1 | data -----+---------- - 1 | foo - 2 | bar - 3 | fool - 4 | foolfool - 5 | foolish - 6 | barrish - 7 | baz - 8 | quux - 9 | foolish - 10 | barrish - 11 | foolme - 12 | barme - 13 | foollog - 14 | barlog -(14 rows) - --- note that nextval() gets executed a second time in the rule expansion, --- which is expected. -select * from tt_log; - f1 | data -----+--------- - 15 | foollog - 16 | barlog -(2 rows) - --- test case for a whole-row-variable bug -create function rngfunc1(n integer, out a text, out b text) - returns setof record - language sql - as $$ select 'foo ' || i, 'bar ' || i from generate_series(1,$1) i $$; -set work_mem='64kB'; -select t.a, t, t.a from rngfunc1(10000) t limit 1; - a | t | a --------+-------------------+------- - foo 1 | ("foo 1","bar 1") | foo 1 -(1 row) - -reset work_mem; -select t.a, t, t.a from rngfunc1(10000) t limit 1; - a | t | a --------+-------------------+------- - foo 1 | ("foo 1","bar 1") | foo 1 -(1 row) - -drop function rngfunc1(n integer); --- test use of SQL functions returning record --- this is supported in some cases where the query doesn't specify --- the actual record type ... -create function array_to_set(anyarray) returns setof record as $$ - select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i -$$ language sql strict immutable; -select array_to_set(array['one', 'two']); - array_to_set --------------- - (1,one) - (2,two) -(2 rows) - -select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text); - f1 | f2 -----+----- - 1 | one - 2 | two -(2 rows) - -select * from array_to_set(array['one', 'two']); -- fail -ERROR: a column definition list is required for functions returning "record" -LINE 1: select * from array_to_set(array['one', 'two']); - ^ --- after-the-fact coercion of the columns is now possible, too -select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text); - f1 | f2 -------+----- - 1.00 | one - 2.00 | two -(2 rows) - --- and if it doesn't work, you get a compile-time not run-time error -select * from array_to_set(array['one', 'two']) as t(f1 point,f2 text); -ERROR: return type mismatch in function declared to return record -DETAIL: Final statement returns integer instead of point at column 1. -CONTEXT: SQL function "array_to_set" during startup --- with "strict", this function can't be inlined in FROM -explain (verbose, costs off) - select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text); - QUERY PLAN ----------------------------------------------------- - Function Scan on public.array_to_set t - Output: f1, f2 - Function Call: array_to_set('{one,two}'::text[]) -(3 rows) - --- but without, it can be: -create or replace function array_to_set(anyarray) returns setof record as $$ - select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i -$$ language sql immutable; -select array_to_set(array['one', 'two']); - array_to_set --------------- - (1,one) - (2,two) -(2 rows) - -select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text); - f1 | f2 -----+----- - 1 | one - 2 | two -(2 rows) - -select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text); - f1 | f2 -------+----- - 1.00 | one - 2.00 | two -(2 rows) - -select * from array_to_set(array['one', 'two']) as t(f1 point,f2 text); -ERROR: return type mismatch in function declared to return record -DETAIL: Final statement returns integer instead of point at column 1. -CONTEXT: SQL function "array_to_set" during inlining -explain (verbose, costs off) - select * from array_to_set(array['one', 'two']) as t(f1 numeric(4,2),f2 text); - QUERY PLAN --------------------------------------------------------------- - Function Scan on pg_catalog.generate_subscripts i - Output: i.i, ('{one,two}'::text[])[i.i] - Function Call: generate_subscripts('{one,two}'::text[], 1) -(3 rows) - -create temp table rngfunc(f1 int8, f2 int8); -create function testrngfunc() returns record as $$ - insert into rngfunc values (1,2) returning *; -$$ language sql; -select testrngfunc(); - testrngfunc -------------- - (1,2) -(1 row) - -select * from testrngfunc() as t(f1 int8,f2 int8); - f1 | f2 -----+---- - 1 | 2 -(1 row) - -select * from testrngfunc(); -- fail -ERROR: a column definition list is required for functions returning "record" -LINE 1: select * from testrngfunc(); - ^ -drop function testrngfunc(); -create function testrngfunc() returns setof record as $$ - insert into rngfunc values (1,2), (3,4) returning *; -$$ language sql; -select testrngfunc(); - testrngfunc -------------- - (1,2) - (3,4) -(2 rows) - -select * from testrngfunc() as t(f1 int8,f2 int8); - f1 | f2 -----+---- - 1 | 2 - 3 | 4 -(2 rows) - -select * from testrngfunc(); -- fail -ERROR: a column definition list is required for functions returning "record" -LINE 1: select * from testrngfunc(); - ^ -drop function testrngfunc(); --- Check that typmod imposed by a composite type is honored -create type rngfunc_type as (f1 numeric(35,6), f2 numeric(35,2)); -create function testrngfunc() returns rngfunc_type as $$ - select 7.136178319899999964, 7.136178319899999964; -$$ language sql immutable; -explain (verbose, costs off) -select testrngfunc(); - QUERY PLAN -------------------------------------------- - Result - Output: '(7.136178,7.14)'::rngfunc_type -(2 rows) - -select testrngfunc(); - testrngfunc ------------------ - (7.136178,7.14) -(1 row) - -explain (verbose, costs off) -select * from testrngfunc(); - QUERY PLAN --------------------------------------------------- - Function Scan on testrngfunc - Output: f1, f2 - Function Call: '(7.136178,7.14)'::rngfunc_type -(3 rows) - -select * from testrngfunc(); - f1 | f2 -----------+------ - 7.136178 | 7.14 -(1 row) - -create or replace function testrngfunc() returns rngfunc_type as $$ - select 7.136178319899999964, 7.136178319899999964; -$$ language sql volatile; -explain (verbose, costs off) -select testrngfunc(); - QUERY PLAN -------------------------- - Result - Output: testrngfunc() -(2 rows) - -select testrngfunc(); - testrngfunc ------------------ - (7.136178,7.14) -(1 row) - -explain (verbose, costs off) -select * from testrngfunc(); - QUERY PLAN -------------------------------------- - Function Scan on public.testrngfunc - Output: f1, f2 - Function Call: testrngfunc() -(3 rows) - -select * from testrngfunc(); - f1 | f2 -----------+------ - 7.136178 | 7.14 -(1 row) - -drop function testrngfunc(); -create function testrngfunc() returns setof rngfunc_type as $$ - select 7.136178319899999964, 7.136178319899999964; -$$ language sql immutable; -explain (verbose, costs off) -select testrngfunc(); - QUERY PLAN -------------------------- - ProjectSet - Output: testrngfunc() - -> Result -(3 rows) - -select testrngfunc(); - testrngfunc ------------------ - (7.136178,7.14) -(1 row) - -explain (verbose, costs off) -select * from testrngfunc(); - QUERY PLAN --------------------------------------------------------- - Result - Output: 7.136178::numeric(35,6), 7.14::numeric(35,2) -(2 rows) - -select * from testrngfunc(); - f1 | f2 -----------+------ - 7.136178 | 7.14 -(1 row) - -create or replace function testrngfunc() returns setof rngfunc_type as $$ - select 7.136178319899999964, 7.136178319899999964; -$$ language sql volatile; -explain (verbose, costs off) -select testrngfunc(); - QUERY PLAN -------------------------- - ProjectSet - Output: testrngfunc() - -> Result -(3 rows) - -select testrngfunc(); - testrngfunc ------------------ - (7.136178,7.14) -(1 row) - -explain (verbose, costs off) -select * from testrngfunc(); - QUERY PLAN -------------------------------------- - Function Scan on public.testrngfunc - Output: f1, f2 - Function Call: testrngfunc() -(3 rows) - -select * from testrngfunc(); - f1 | f2 -----------+------ - 7.136178 | 7.14 -(1 row) - -create or replace function testrngfunc() returns setof rngfunc_type as $$ - select 1, 2 union select 3, 4 order by 1; -$$ language sql immutable; -explain (verbose, costs off) -select testrngfunc(); - QUERY PLAN -------------------------- - ProjectSet - Output: testrngfunc() - -> Result -(3 rows) - -select testrngfunc(); - testrngfunc ------------------ - (1.000000,2.00) - (3.000000,4.00) -(2 rows) - -explain (verbose, costs off) -select * from testrngfunc(); - QUERY PLAN ----------------------------------------------------------- - Subquery Scan on "*SELECT*" - Output: "*SELECT*"."?column?", "*SELECT*"."?column?_1" - -> Unique - Output: (1), (2) - -> Sort - Output: (1), (2) - Sort Key: (1), (2) - -> Append - -> Result - Output: 1, 2 - -> Result - Output: 3, 4 -(12 rows) - -select * from testrngfunc(); - f1 | f2 -----------+------ - 1.000000 | 2.00 - 3.000000 | 4.00 -(2 rows) - --- Check a couple of error cases while we're here -select * from testrngfunc() as t(f1 int8,f2 int8); -- fail, composite result -ERROR: a column definition list is redundant for a function returning a named composite type -LINE 1: select * from testrngfunc() as t(f1 int8,f2 int8); - ^ -select * from pg_get_keywords() as t(f1 int8,f2 int8); -- fail, OUT params -ERROR: a column definition list is redundant for a function with OUT parameters -LINE 1: select * from pg_get_keywords() as t(f1 int8,f2 int8); - ^ -select * from sin(3) as t(f1 int8,f2 int8); -- fail, scalar result type -ERROR: a column definition list is only allowed for functions returning "record" -LINE 1: select * from sin(3) as t(f1 int8,f2 int8); - ^ -drop type rngfunc_type cascade; -NOTICE: drop cascades to function testrngfunc() --- --- Check some cases involving added/dropped columns in a rowtype result --- -create temp table users (userid text, seq int, email text, todrop bool, moredrop int, enabled bool); -insert into users values ('id',1,'email',true,11,true); -insert into users values ('id2',2,'email2',true,12,true); -alter table users drop column todrop; -create or replace function get_first_user() returns users as -$$ SELECT * FROM users ORDER BY userid LIMIT 1; $$ -language sql stable; -SELECT get_first_user(); - get_first_user -------------------- - (id,1,email,11,t) -(1 row) - -SELECT * FROM get_first_user(); - userid | seq | email | moredrop | enabled ---------+-----+-------+----------+--------- - id | 1 | email | 11 | t -(1 row) - -create or replace function get_users() returns setof users as -$$ SELECT * FROM users ORDER BY userid; $$ -language sql stable; -SELECT get_users(); - get_users ---------------------- - (id,1,email,11,t) - (id2,2,email2,12,t) -(2 rows) - -SELECT * FROM get_users(); - userid | seq | email | moredrop | enabled ---------+-----+--------+----------+--------- - id | 1 | email | 11 | t - id2 | 2 | email2 | 12 | t -(2 rows) - -SELECT * FROM get_users() WITH ORDINALITY; -- make sure ordinality copes - userid | seq | email | moredrop | enabled | ordinality ---------+-----+--------+----------+---------+------------ - id | 1 | email | 11 | t | 1 - id2 | 2 | email2 | 12 | t | 2 -(2 rows) - --- multiple functions vs. dropped columns -SELECT * FROM ROWS FROM(generate_series(10,11), get_users()) WITH ORDINALITY; - generate_series | userid | seq | email | moredrop | enabled | ordinality ------------------+--------+-----+--------+----------+---------+------------ - 10 | id | 1 | email | 11 | t | 1 - 11 | id2 | 2 | email2 | 12 | t | 2 -(2 rows) - -SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY; - userid | seq | email | moredrop | enabled | generate_series | ordinality ---------+-----+--------+----------+---------+-----------------+------------ - id | 1 | email | 11 | t | 10 | 1 - id2 | 2 | email2 | 12 | t | 11 | 2 -(2 rows) - --- check that we can cope with post-parsing changes in rowtypes -create temp view usersview as -SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY; -select * from usersview; - userid | seq | email | moredrop | enabled | generate_series | ordinality ---------+-----+--------+----------+---------+-----------------+------------ - id | 1 | email | 11 | t | 10 | 1 - id2 | 2 | email2 | 12 | t | 11 | 2 -(2 rows) - -alter table users add column junk text; -select * from usersview; - userid | seq | email | moredrop | enabled | generate_series | ordinality ---------+-----+--------+----------+---------+-----------------+------------ - id | 1 | email | 11 | t | 10 | 1 - id2 | 2 | email2 | 12 | t | 11 | 2 -(2 rows) - -begin; -alter table users drop column moredrop; -select * from usersview; -- expect clean failure -ERROR: attribute 5 of type record has been dropped -rollback; -alter table users alter column seq type numeric; -select * from usersview; -- expect clean failure -ERROR: attribute 2 of type record has wrong type -DETAIL: Table has type numeric, but query expects integer. -drop view usersview; -drop function get_first_user(); -drop function get_users(); -drop table users; --- check behavior with type coercion required for a set-op -create or replace function rngfuncbar() returns setof text as -$$ select 'foo'::varchar union all select 'bar'::varchar ; $$ -language sql stable; -select rngfuncbar(); - rngfuncbar ------------- - foo - bar -(2 rows) - -select * from rngfuncbar(); - rngfuncbar ------------- - foo - bar -(2 rows) - --- this function is now inlinable, too: -explain (verbose, costs off) select * from rngfuncbar(); - QUERY PLAN ------------------------------------------------- - Result - Output: ('foo'::character varying) - -> Append - -> Result - Output: 'foo'::character varying - -> Result - Output: 'bar'::character varying -(7 rows) - -drop function rngfuncbar(); --- check handling of a SQL function with multiple OUT params (bug #5777) -create or replace function rngfuncbar(out integer, out numeric) as -$$ select (1, 2.1) $$ language sql; -select * from rngfuncbar(); - column1 | column2 ----------+--------- - 1 | 2.1 -(1 row) - -create or replace function rngfuncbar(out integer, out numeric) as -$$ select (1, 2) $$ language sql; -select * from rngfuncbar(); -- fail -ERROR: function return row and query-specified return row do not match -DETAIL: Returned type integer at ordinal position 2, but query expects numeric. -create or replace function rngfuncbar(out integer, out numeric) as -$$ select (1, 2.1, 3) $$ language sql; -select * from rngfuncbar(); -- fail -ERROR: function return row and query-specified return row do not match -DETAIL: Returned row contains 3 attributes, but query expects 2. -drop function rngfuncbar(); --- check whole-row-Var handling in nested lateral functions (bug #11703) -create function extractq2(t int8_tbl) returns int8 as $$ - select t.q2 -$$ language sql immutable; -explain (verbose, costs off) -select x from int8_tbl, extractq2(int8_tbl) f(x); - QUERY PLAN ------------------------------------------- - Nested Loop - Output: f.x - -> Seq Scan on public.int8_tbl - Output: int8_tbl.q1, int8_tbl.q2 - -> Function Scan on f - Output: f.x - Function Call: int8_tbl.q2 -(7 rows) - -select x from int8_tbl, extractq2(int8_tbl) f(x); - x -------------------- - 456 - 4567890123456789 - 123 - 4567890123456789 - -4567890123456789 -(5 rows) - -create function extractq2_2(t int8_tbl) returns table(ret1 int8) as $$ - select extractq2(t) offset 0 -$$ language sql immutable; -explain (verbose, costs off) -select x from int8_tbl, extractq2_2(int8_tbl) f(x); - QUERY PLAN ------------------------------------ - Nested Loop - Output: ((int8_tbl.*).q2) - -> Seq Scan on public.int8_tbl - Output: int8_tbl.* - -> Result - Output: (int8_tbl.*).q2 -(6 rows) - -select x from int8_tbl, extractq2_2(int8_tbl) f(x); - x -------------------- - 456 - 4567890123456789 - 123 - 4567890123456789 - -4567890123456789 -(5 rows) - --- without the "offset 0", this function gets optimized quite differently -create function extractq2_2_opt(t int8_tbl) returns table(ret1 int8) as $$ - select extractq2(t) -$$ language sql immutable; -explain (verbose, costs off) -select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x); - QUERY PLAN ------------------------------ - Seq Scan on public.int8_tbl - Output: int8_tbl.q2 -(2 rows) - -select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x); - x -------------------- - 456 - 4567890123456789 - 123 - 4567890123456789 - -4567890123456789 -(5 rows) - --- check handling of nulls in SRF results (bug #7808) -create type rngfunc2 as (a integer, b text); -select *, row_to_json(u) from unnest(array[(1,'foo')::rngfunc2, null::rngfunc2]) u; - a | b | row_to_json ----+-----+--------------------- - 1 | foo | {"a":1,"b":"foo"} - | | {"a":null,"b":null} -(2 rows) - -select *, row_to_json(u) from unnest(array[null::rngfunc2, null::rngfunc2]) u; - a | b | row_to_json ----+---+--------------------- - | | {"a":null,"b":null} - | | {"a":null,"b":null} -(2 rows) - -select *, row_to_json(u) from unnest(array[null::rngfunc2, (1,'foo')::rngfunc2, null::rngfunc2]) u; - a | b | row_to_json ----+-----+--------------------- - | | {"a":null,"b":null} - 1 | foo | {"a":1,"b":"foo"} - | | {"a":null,"b":null} -(3 rows) - -select *, row_to_json(u) from unnest(array[]::rngfunc2[]) u; - a | b | row_to_json ----+---+------------- -(0 rows) - -drop type rngfunc2; --- check handling of functions pulled up into function RTEs (bug #17227) -explain (verbose, costs off) -select * from - (select jsonb_path_query_array(module->'lectures', '$[*]') as lecture - from unnest(array['{"lectures": [{"id": "1"}]}'::jsonb]) - as unnested_modules(module)) as ss, - jsonb_to_recordset(ss.lecture) as j (id text); - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: jsonb_path_query_array((unnested_modules.module -> 'lectures'::text), '$[*]'::jsonpath, '{}'::jsonb, false), j.id - -> Function Scan on pg_catalog.unnest unnested_modules - Output: unnested_modules.module - Function Call: unnest('{"{\"lectures\": [{\"id\": \"1\"}]}"}'::jsonb[]) - -> Function Scan on pg_catalog.jsonb_to_recordset j - Output: j.id - Function Call: jsonb_to_recordset(jsonb_path_query_array((unnested_modules.module -> 'lectures'::text), '$[*]'::jsonpath, '{}'::jsonb, false)) -(8 rows) - -select * from - (select jsonb_path_query_array(module->'lectures', '$[*]') as lecture - from unnest(array['{"lectures": [{"id": "1"}]}'::jsonb]) - as unnested_modules(module)) as ss, - jsonb_to_recordset(ss.lecture) as j (id text); - lecture | id ----------------+---- - [{"id": "1"}] | 1 -(1 row) - +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/prepare.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/prepare.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/prepare.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/prepare.out 2021-10-30 22:09:24 -0600 @@ -59,131 +59,10 @@ SELECT datname, datistemplate, datallowconn FROM pg_database WHERE datname = $1; EXECUTE q2('postgres'); - datname | datistemplate | datallowconn -----------+---------------+-------------- - postgres | f | t -(1 row) - -PREPARE q3(text, int, float, boolean, smallint) AS - SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR - ten = $3::bigint OR true = $4 OR odd = $5::int) - ORDER BY unique1; -EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 4::bigint); - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 2 | 2716 | 0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 4 | 5 | CAAAAA | MAEAAA | AAAAxx - 102 | 612 | 0 | 2 | 2 | 2 | 2 | 102 | 102 | 102 | 102 | 4 | 5 | YDAAAA | OXAAAA | AAAAxx - 802 | 2908 | 0 | 2 | 2 | 2 | 2 | 802 | 802 | 802 | 802 | 4 | 5 | WEAAAA | WHEAAA | AAAAxx - 902 | 1104 | 0 | 2 | 2 | 2 | 2 | 902 | 902 | 902 | 902 | 4 | 5 | SIAAAA | MQBAAA | AAAAxx - 1002 | 2580 | 0 | 2 | 2 | 2 | 2 | 2 | 1002 | 1002 | 1002 | 4 | 5 | OMAAAA | GVDAAA | AAAAxx - 1602 | 8148 | 0 | 2 | 2 | 2 | 2 | 602 | 1602 | 1602 | 1602 | 4 | 5 | QJAAAA | KBMAAA | AAAAxx - 1702 | 7940 | 0 | 2 | 2 | 2 | 2 | 702 | 1702 | 1702 | 1702 | 4 | 5 | MNAAAA | KTLAAA | AAAAxx - 2102 | 6184 | 0 | 2 | 2 | 2 | 2 | 102 | 102 | 2102 | 2102 | 4 | 5 | WCAAAA | WDJAAA | AAAAxx - 2202 | 8028 | 0 | 2 | 2 | 2 | 2 | 202 | 202 | 2202 | 2202 | 4 | 5 | SGAAAA | UWLAAA | AAAAxx - 2302 | 7112 | 0 | 2 | 2 | 2 | 2 | 302 | 302 | 2302 | 2302 | 4 | 5 | OKAAAA | ONKAAA | AAAAxx - 2902 | 6816 | 0 | 2 | 2 | 2 | 2 | 902 | 902 | 2902 | 2902 | 4 | 5 | QHAAAA | ECKAAA | AAAAxx - 3202 | 7128 | 0 | 2 | 2 | 2 | 2 | 202 | 1202 | 3202 | 3202 | 4 | 5 | ETAAAA | EOKAAA | AAAAxx - 3902 | 9224 | 0 | 2 | 2 | 2 | 2 | 902 | 1902 | 3902 | 3902 | 4 | 5 | CUAAAA | UQNAAA | AAAAxx - 4102 | 7676 | 0 | 2 | 2 | 2 | 2 | 102 | 102 | 4102 | 4102 | 4 | 5 | UBAAAA | GJLAAA | AAAAxx - 4202 | 6628 | 0 | 2 | 2 | 2 | 2 | 202 | 202 | 4202 | 4202 | 4 | 5 | QFAAAA | YUJAAA | AAAAxx - 4502 | 412 | 0 | 2 | 2 | 2 | 2 | 502 | 502 | 4502 | 4502 | 4 | 5 | ERAAAA | WPAAAA | AAAAxx - 4702 | 2520 | 0 | 2 | 2 | 2 | 2 | 702 | 702 | 4702 | 4702 | 4 | 5 | WYAAAA | YSDAAA | AAAAxx - 4902 | 1600 | 0 | 2 | 2 | 2 | 2 | 902 | 902 | 4902 | 4902 | 4 | 5 | OGAAAA | OJCAAA | AAAAxx - 5602 | 8796 | 0 | 2 | 2 | 2 | 2 | 602 | 1602 | 602 | 5602 | 4 | 5 | MHAAAA | IANAAA | AAAAxx - 6002 | 8932 | 0 | 2 | 2 | 2 | 2 | 2 | 2 | 1002 | 6002 | 4 | 5 | WWAAAA | OFNAAA | AAAAxx - 6402 | 3808 | 0 | 2 | 2 | 2 | 2 | 402 | 402 | 1402 | 6402 | 4 | 5 | GMAAAA | MQFAAA | AAAAxx - 7602 | 1040 | 0 | 2 | 2 | 2 | 2 | 602 | 1602 | 2602 | 7602 | 4 | 5 | KGAAAA | AOBAAA | AAAAxx - 7802 | 7508 | 0 | 2 | 2 | 2 | 2 | 802 | 1802 | 2802 | 7802 | 4 | 5 | COAAAA | UCLAAA | AAAAxx - 8002 | 9980 | 0 | 2 | 2 | 2 | 2 | 2 | 2 | 3002 | 8002 | 4 | 5 | UVAAAA | WTOAAA | AAAAxx - 8302 | 7800 | 0 | 2 | 2 | 2 | 2 | 302 | 302 | 3302 | 8302 | 4 | 5 | IHAAAA | AOLAAA | AAAAxx - 8402 | 5708 | 0 | 2 | 2 | 2 | 2 | 402 | 402 | 3402 | 8402 | 4 | 5 | ELAAAA | OLIAAA | AAAAxx - 8602 | 5440 | 0 | 2 | 2 | 2 | 2 | 602 | 602 | 3602 | 8602 | 4 | 5 | WSAAAA | GBIAAA | AAAAxx - 9502 | 1812 | 0 | 2 | 2 | 2 | 2 | 502 | 1502 | 4502 | 9502 | 4 | 5 | MBAAAA | SRCAAA | AAAAxx - 9602 | 9972 | 0 | 2 | 2 | 2 | 2 | 602 | 1602 | 4602 | 9602 | 4 | 5 | IFAAAA | OTOAAA | AAAAxx -(29 rows) - --- too few params -EXECUTE q3('bool'); -ERROR: wrong number of parameters for prepared statement "q3" -DETAIL: Expected 5 parameters but got 1. --- too many params -EXECUTE q3('bytea', 5::smallint, 10.5::float, false, 4::bigint, true); -ERROR: wrong number of parameters for prepared statement "q3" -DETAIL: Expected 5 parameters but got 6. --- wrong param types -EXECUTE q3(5::smallint, 10.5::float, false, 4::bigint, 'bytea'); -ERROR: parameter $3 of type boolean cannot be coerced to the expected type double precision -LINE 1: EXECUTE q3(5::smallint, 10.5::float, false, 4::bigint, 'byte... - ^ -HINT: You will need to rewrite or cast the expression. --- invalid type -PREPARE q4(nonexistenttype) AS SELECT $1; -ERROR: type "nonexistenttype" does not exist -LINE 1: PREPARE q4(nonexistenttype) AS SELECT $1; - ^ --- create table as execute -PREPARE q5(int, text) AS - SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2 - ORDER BY unique1; -CREATE TEMPORARY TABLE q5_prep_results AS EXECUTE q5(200, 'DTAAAA'); -SELECT * FROM q5_prep_results; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- - 200 | 9441 | 0 | 0 | 0 | 0 | 0 | 200 | 200 | 200 | 200 | 0 | 1 | SHAAAA | DZNAAA | HHHHxx - 497 | 9092 | 1 | 1 | 7 | 17 | 97 | 497 | 497 | 497 | 497 | 194 | 195 | DTAAAA | SLNAAA | AAAAxx - 1173 | 6699 | 1 | 1 | 3 | 13 | 73 | 173 | 1173 | 1173 | 1173 | 146 | 147 | DTAAAA | RXJAAA | VVVVxx - 1849 | 8143 | 1 | 1 | 9 | 9 | 49 | 849 | 1849 | 1849 | 1849 | 98 | 99 | DTAAAA | FBMAAA | VVVVxx - 2525 | 64 | 1 | 1 | 5 | 5 | 25 | 525 | 525 | 2525 | 2525 | 50 | 51 | DTAAAA | MCAAAA | AAAAxx - 3201 | 7309 | 1 | 1 | 1 | 1 | 1 | 201 | 1201 | 3201 | 3201 | 2 | 3 | DTAAAA | DVKAAA | HHHHxx - 3877 | 4060 | 1 | 1 | 7 | 17 | 77 | 877 | 1877 | 3877 | 3877 | 154 | 155 | DTAAAA | EAGAAA | AAAAxx - 4553 | 4113 | 1 | 1 | 3 | 13 | 53 | 553 | 553 | 4553 | 4553 | 106 | 107 | DTAAAA | FCGAAA | HHHHxx - 5229 | 6407 | 1 | 1 | 9 | 9 | 29 | 229 | 1229 | 229 | 5229 | 58 | 59 | DTAAAA | LMJAAA | VVVVxx - 5905 | 9537 | 1 | 1 | 5 | 5 | 5 | 905 | 1905 | 905 | 5905 | 10 | 11 | DTAAAA | VCOAAA | HHHHxx - 6581 | 4686 | 1 | 1 | 1 | 1 | 81 | 581 | 581 | 1581 | 6581 | 162 | 163 | DTAAAA | GYGAAA | OOOOxx - 7257 | 1895 | 1 | 1 | 7 | 17 | 57 | 257 | 1257 | 2257 | 7257 | 114 | 115 | DTAAAA | XUCAAA | VVVVxx - 7933 | 4514 | 1 | 1 | 3 | 13 | 33 | 933 | 1933 | 2933 | 7933 | 66 | 67 | DTAAAA | QRGAAA | OOOOxx - 8609 | 5918 | 1 | 1 | 9 | 9 | 9 | 609 | 609 | 3609 | 8609 | 18 | 19 | DTAAAA | QTIAAA | OOOOxx - 9285 | 8469 | 1 | 1 | 5 | 5 | 85 | 285 | 1285 | 4285 | 9285 | 170 | 171 | DTAAAA | TNMAAA | HHHHxx - 9961 | 2058 | 1 | 1 | 1 | 1 | 61 | 961 | 1961 | 4961 | 9961 | 122 | 123 | DTAAAA | EBDAAA | OOOOxx -(16 rows) - -CREATE TEMPORARY TABLE q5_prep_nodata AS EXECUTE q5(200, 'DTAAAA') - WITH NO DATA; -SELECT * FROM q5_prep_nodata; - unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ----------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- -(0 rows) - --- unknown or unspecified parameter types: should succeed -PREPARE q6 AS - SELECT * FROM tenk1 WHERE unique1 = $1 AND stringu1 = $2; -PREPARE q7(unknown) AS - SELECT * FROM road WHERE thepath = $1; -SELECT name, statement, parameter_types FROM pg_prepared_statements - ORDER BY name; - name | statement | parameter_types -------+------------------------------------------------------------------+---------------------------------------------------- - q2 | PREPARE q2(text) AS +| {text} - | SELECT datname, datistemplate, datallowconn +| - | FROM pg_database WHERE datname = $1; | - q3 | PREPARE q3(text, int, float, boolean, smallint) AS +| {text,integer,"double precision",boolean,smallint} - | SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR+| - | ten = $3::bigint OR true = $4 OR odd = $5::int) +| - | ORDER BY unique1; | - q5 | PREPARE q5(int, text) AS +| {integer,text} - | SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2 +| - | ORDER BY unique1; | - q6 | PREPARE q6 AS +| {integer,name} - | SELECT * FROM tenk1 WHERE unique1 = $1 AND stringu1 = $2; | - q7 | PREPARE q7(unknown) AS +| {path} - | SELECT * FROM road WHERE thepath = $1; | -(5 rows) - --- test DEALLOCATE ALL; -DEALLOCATE ALL; -SELECT name, statement, parameter_types FROM pg_prepared_statements - ORDER BY name; - name | statement | parameter_types -------+-----------+----------------- -(0 rows) - +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/conversion.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/conversion.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/conversion.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/conversion.out 2021-10-30 22:09:24 -0600 @@ -272,287 +272,10 @@ ('\x666f6f8fdb00', 'invalid, NUL byte'); -- Test SHIFT-JIS-2004 verification select description, inbytes, (test_conv(inbytes, 'shiftjis2004', 'shiftjis2004')).* from shiftjis2004_inputs; - description | inbytes | result | errorat | error ----------------------------------------+----------------+--------------+----------+---------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6f8fdb | \x666f6f8fdb | | - valid, no translation to UTF-8 | \x666f6f81c0 | \x666f6f81c0 | | - valid, translates to two UTF-8 chars | \x666f6f82f5 | \x666f6f82f5 | | - incomplete char | \x666f6f8fdb8f | \x666f6f8fdb | \x8f | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f - incomplete char, followed by newline | \x666f6f820a | \x666f6f | \x820a | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x82 0x0a - invalid, NUL byte | \x666f6f008fdb | \x666f6f | \x008fdb | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 - invalid, NUL byte | \x666f6f8f00db | \x666f6f | \x8f00db | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f 0x00 - invalid, NUL byte | \x666f6f8fdb00 | \x666f6f8fdb | \x00 | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 -(9 rows) - --- Test conversions from SHIFT-JIS-2004 -select description, inbytes, (test_conv(inbytes, 'shiftjis2004', 'utf8')).* from shiftjis2004_inputs; - description | inbytes | result | errorat | error ----------------------------------------+----------------+----------------------+----------+---------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6f8fdb | \x666f6fe8b1a1 | | - valid, no translation to UTF-8 | \x666f6f81c0 | \x666f6fe28a84 | | - valid, translates to two UTF-8 chars | \x666f6f82f5 | \x666f6fe3818be3829a | | - incomplete char | \x666f6f8fdb8f | \x666f6fe8b1a1 | \x8f | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f - incomplete char, followed by newline | \x666f6f820a | \x666f6f | \x820a | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x82 0x0a - invalid, NUL byte | \x666f6f008fdb | \x666f6f | \x008fdb | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 - invalid, NUL byte | \x666f6f8f00db | \x666f6f | \x8f00db | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f 0x00 - invalid, NUL byte | \x666f6f8fdb00 | \x666f6fe8b1a1 | \x00 | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 -(9 rows) - -select description, inbytes, (test_conv(inbytes, 'shiftjis2004', 'euc_jis_2004')).* from shiftjis2004_inputs; - description | inbytes | result | errorat | error ----------------------------------------+----------------+--------------+----------+---------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6f8fdb | \x666f6fbedd | | - valid, no translation to UTF-8 | \x666f6f81c0 | \x666f6fa2c2 | | - valid, translates to two UTF-8 chars | \x666f6f82f5 | \x666f6fa4f7 | | - incomplete char | \x666f6f8fdb8f | \x666f6fbedd | \x8f | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f - incomplete char, followed by newline | \x666f6f820a | \x666f6f | \x820a | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x82 0x0a - invalid, NUL byte | \x666f6f008fdb | \x666f6f | \x008fdb | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 - invalid, NUL byte | \x666f6f8f00db | \x666f6f | \x8f00db | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x8f 0x00 - invalid, NUL byte | \x666f6f8fdb00 | \x666f6fbedd | \x00 | invalid byte sequence for encoding "SHIFT_JIS_2004": 0x00 -(9 rows) - --- --- GB18030 --- -CREATE TABLE gb18030_inputs (inbytes bytea, description text); -insert into gb18030_inputs values - ('\x666f6f', 'valid, pure ASCII'), - ('\x666f6fcff3', 'valid'), - ('\x666f6f8431a530', 'valid, no translation to UTF-8'), - ('\x666f6f84309c38', 'valid, translates to UTF-8 by mapping function'), - ('\x666f6f84309c', 'incomplete char '), - ('\x666f6f84309c0a', 'incomplete char, followed by newline '), - ('\x666f6f84309c3800', 'invalid, NUL byte'), - ('\x666f6f84309c0038', 'invalid, NUL byte'); --- Test GB18030 verification -select description, inbytes, (test_conv(inbytes, 'gb18030', 'gb18030')).* from gb18030_inputs; - description | inbytes | result | errorat | error -------------------------------------------------+--------------------+------------------+--------------+------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6fcff3 | \x666f6fcff3 | | - valid, no translation to UTF-8 | \x666f6f8431a530 | \x666f6f8431a530 | | - valid, translates to UTF-8 by mapping function | \x666f6f84309c38 | \x666f6f84309c38 | | - incomplete char | \x666f6f84309c | \x666f6f | \x84309c | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c - incomplete char, followed by newline | \x666f6f84309c0a | \x666f6f | \x84309c0a | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c 0x0a - invalid, NUL byte | \x666f6f84309c3800 | \x666f6f84309c38 | \x00 | invalid byte sequence for encoding "GB18030": 0x00 - invalid, NUL byte | \x666f6f84309c0038 | \x666f6f | \x84309c0038 | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c 0x00 -(8 rows) - --- Test conversions from GB18030 -select description, inbytes, (test_conv(inbytes, 'gb18030', 'utf8')).* from gb18030_inputs; - description | inbytes | result | errorat | error -------------------------------------------------+--------------------+----------------+--------------+------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6fcff3 | \x666f6fe8b1a1 | | - valid, no translation to UTF-8 | \x666f6f8431a530 | \x666f6f | \x8431a530 | character with byte sequence 0x84 0x31 0xa5 0x30 in encoding "GB18030" has no equivalent in encoding "UTF8" - valid, translates to UTF-8 by mapping function | \x666f6f84309c38 | \x666f6fefa8aa | | - incomplete char | \x666f6f84309c | \x666f6f | \x84309c | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c - incomplete char, followed by newline | \x666f6f84309c0a | \x666f6f | \x84309c0a | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c 0x0a - invalid, NUL byte | \x666f6f84309c3800 | \x666f6fefa8aa | \x00 | invalid byte sequence for encoding "GB18030": 0x00 - invalid, NUL byte | \x666f6f84309c0038 | \x666f6f | \x84309c0038 | invalid byte sequence for encoding "GB18030": 0x84 0x30 0x9c 0x00 -(8 rows) - --- --- ISO-8859-5 --- -CREATE TABLE iso8859_5_inputs (inbytes bytea, description text); -insert into iso8859_5_inputs values - ('\x666f6f', 'valid, pure ASCII'), - ('\xe4dede', 'valid'), - ('\x00', 'invalid, NUL byte'), - ('\xe400dede', 'invalid, NUL byte'), - ('\xe4dede00', 'invalid, NUL byte'); --- Test ISO-8859-5 verification -select description, inbytes, (test_conv(inbytes, 'iso8859-5', 'iso8859-5')).* from iso8859_5_inputs; - description | inbytes | result | errorat | error --------------------+------------+----------+----------+------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \xe4dede | \xe4dede | | - invalid, NUL byte | \x00 | \x | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe400dede | \xe4 | \x00dede | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe4dede00 | \xe4dede | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 -(5 rows) - --- Test conversions from ISO-8859-5 -select description, inbytes, (test_conv(inbytes, 'iso8859-5', 'utf8')).* from iso8859_5_inputs; - description | inbytes | result | errorat | error --------------------+------------+----------------+----------+------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \xe4dede | \xd184d0bed0be | | - invalid, NUL byte | \x00 | \x | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe400dede | \xd184 | \x00dede | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe4dede00 | \xd184d0bed0be | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 -(5 rows) - -select description, inbytes, (test_conv(inbytes, 'iso8859-5', 'koi8r')).* from iso8859_5_inputs; - description | inbytes | result | errorat | error --------------------+------------+----------+----------+------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \xe4dede | \xc6cfcf | | - invalid, NUL byte | \x00 | \x | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe400dede | \xc6 | \x00dede | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe4dede00 | \xc6cfcf | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 -(5 rows) - -select description, inbytes, (test_conv(inbytes, 'iso8859_5', 'mule_internal')).* from iso8859_5_inputs; - description | inbytes | result | errorat | error --------------------+------------+----------------+----------+------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \xe4dede | \x8bc68bcf8bcf | | - invalid, NUL byte | \x00 | \x | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe400dede | \x8bc6 | \x00dede | invalid byte sequence for encoding "ISO_8859_5": 0x00 - invalid, NUL byte | \xe4dede00 | \x8bc68bcf8bcf | \x00 | invalid byte sequence for encoding "ISO_8859_5": 0x00 -(5 rows) - --- --- Big5 --- -CREATE TABLE big5_inputs (inbytes bytea, description text); -insert into big5_inputs values - ('\x666f6f', 'valid, pure ASCII'), - ('\x666f6fb648', 'valid'), - ('\x666f6fa27f', 'valid, no translation to UTF-8'), - ('\x666f6fb60048', 'invalid, NUL byte'), - ('\x666f6fb64800', 'invalid, NUL byte'); --- Test Big5 verification -select description, inbytes, (test_conv(inbytes, 'big5', 'big5')).* from big5_inputs; - description | inbytes | result | errorat | error ---------------------------------+----------------+--------------+----------+------------------------------------------------------ - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6fb648 | \x666f6fb648 | | - valid, no translation to UTF-8 | \x666f6fa27f | \x666f6fa27f | | - invalid, NUL byte | \x666f6fb60048 | \x666f6f | \xb60048 | invalid byte sequence for encoding "BIG5": 0xb6 0x00 - invalid, NUL byte | \x666f6fb64800 | \x666f6fb648 | \x00 | invalid byte sequence for encoding "BIG5": 0x00 -(5 rows) - --- Test conversions from Big5 -select description, inbytes, (test_conv(inbytes, 'big5', 'utf8')).* from big5_inputs; - description | inbytes | result | errorat | error ---------------------------------+----------------+----------------+----------+------------------------------------------------------------------------------------------------ - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6fb648 | \x666f6fe8b1a1 | | - valid, no translation to UTF-8 | \x666f6fa27f | \x666f6f | \xa27f | character with byte sequence 0xa2 0x7f in encoding "BIG5" has no equivalent in encoding "UTF8" - invalid, NUL byte | \x666f6fb60048 | \x666f6f | \xb60048 | invalid byte sequence for encoding "BIG5": 0xb6 0x00 - invalid, NUL byte | \x666f6fb64800 | \x666f6fe8b1a1 | \x00 | invalid byte sequence for encoding "BIG5": 0x00 -(5 rows) - -select description, inbytes, (test_conv(inbytes, 'big5', 'mule_internal')).* from big5_inputs; - description | inbytes | result | errorat | error ---------------------------------+----------------+----------------+----------+------------------------------------------------------ - valid, pure ASCII | \x666f6f | \x666f6f | | - valid | \x666f6fb648 | \x666f6f95e2af | | - valid, no translation to UTF-8 | \x666f6fa27f | \x666f6f95a3c1 | | - invalid, NUL byte | \x666f6fb60048 | \x666f6f | \xb60048 | invalid byte sequence for encoding "BIG5": 0xb6 0x00 - invalid, NUL byte | \x666f6fb64800 | \x666f6f95e2af | \x00 | invalid byte sequence for encoding "BIG5": 0x00 -(5 rows) - --- --- MULE_INTERNAL --- -CREATE TABLE mic_inputs (inbytes bytea, description text); -insert into mic_inputs values - ('\x666f6f', 'valid, pure ASCII'), - ('\x8bc68bcf8bcf', 'valid (in KOI8R)'), - ('\x8bc68bcf8b', 'invalid,incomplete char'), - ('\x92bedd', 'valid (in SHIFT_JIS)'), - ('\x92be', 'invalid, incomplete char)'), - ('\x666f6f95a3c1', 'valid (in Big5)'), - ('\x666f6f95a3', 'invalid, incomplete char'), - ('\x9200bedd', 'invalid, NUL byte'), - ('\x92bedd00', 'invalid, NUL byte'), - ('\x8b00c68bcf8bcf', 'invalid, NUL byte'); --- Test MULE_INTERNAL verification -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'mule_internal')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+----------------+------------------+-------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \x8bc68bcf8bcf | | - invalid,incomplete char | \x8bc68bcf8b | \x8bc68bcf | \x8b | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b - valid (in SHIFT_JIS) | \x92bedd | \x92bedd | | - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6f95a3c1 | | - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0x00 0xbe - invalid, NUL byte | \x92bedd00 | \x92bedd | \x00 | invalid byte sequence for encoding "MULE_INTERNAL": 0x00 - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b 0x00 -(10 rows) - --- Test conversions from MULE_INTERNAL -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'koi8r')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+----------+------------------+--------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \xc6cfcf | | - invalid,incomplete char | \x8bc68bcf8b | \xc6cf | \x8b | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b - valid (in SHIFT_JIS) | \x92bedd | \x | \x92bedd | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "KOI8R" - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6f | \x95a3c1 | character with byte sequence 0x95 0xa3 0xc1 in encoding "MULE_INTERNAL" has no equivalent in encoding "KOI8R" - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | character with byte sequence 0x92 0x00 0xbe in encoding "MULE_INTERNAL" has no equivalent in encoding "KOI8R" - invalid, NUL byte | \x92bedd00 | \x | \x92bedd00 | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "KOI8R" - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | character with byte sequence 0x8b 0x00 in encoding "MULE_INTERNAL" has no equivalent in encoding "KOI8R" -(10 rows) - -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'iso8859-5')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+----------+------------------+-------------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \xe4dede | | - invalid,incomplete char | \x8bc68bcf8b | \xe4de | \x8b | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b - valid (in SHIFT_JIS) | \x92bedd | \x | \x92bedd | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "ISO_8859_5" - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6f | \x95a3c1 | character with byte sequence 0x95 0xa3 0xc1 in encoding "MULE_INTERNAL" has no equivalent in encoding "ISO_8859_5" - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | character with byte sequence 0x92 0x00 0xbe in encoding "MULE_INTERNAL" has no equivalent in encoding "ISO_8859_5" - invalid, NUL byte | \x92bedd00 | \x | \x92bedd00 | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "ISO_8859_5" - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | character with byte sequence 0x8b 0x00 in encoding "MULE_INTERNAL" has no equivalent in encoding "ISO_8859_5" -(10 rows) - -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'sjis')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+----------+------------------+-------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \x | \x8bc68bcf8bcf | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "SJIS" - invalid,incomplete char | \x8bc68bcf8b | \x | \x8bc68bcf8b | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "SJIS" - valid (in SHIFT_JIS) | \x92bedd | \x8fdb | | - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6f | \x95a3c1 | character with byte sequence 0x95 0xa3 0xc1 in encoding "MULE_INTERNAL" has no equivalent in encoding "SJIS" - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0x00 0xbe - invalid, NUL byte | \x92bedd00 | \x8fdb | \x00 | invalid byte sequence for encoding "MULE_INTERNAL": 0x00 - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b 0x00 -(10 rows) - -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'big5')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+--------------+------------------+-------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \x | \x8bc68bcf8bcf | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "BIG5" - invalid,incomplete char | \x8bc68bcf8b | \x | \x8bc68bcf8b | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "BIG5" - valid (in SHIFT_JIS) | \x92bedd | \x | \x92bedd | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "BIG5" - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6fa2a1 | | - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0x00 0xbe - invalid, NUL byte | \x92bedd00 | \x | \x92bedd00 | character with byte sequence 0x92 0xbe 0xdd in encoding "MULE_INTERNAL" has no equivalent in encoding "BIG5" - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b 0x00 -(10 rows) - -select description, inbytes, (test_conv(inbytes, 'mule_internal', 'euc_jp')).* from mic_inputs; - description | inbytes | result | errorat | error ----------------------------+------------------+----------+------------------+---------------------------------------------------------------------------------------------------------------- - valid, pure ASCII | \x666f6f | \x666f6f | | - valid (in KOI8R) | \x8bc68bcf8bcf | \x | \x8bc68bcf8bcf | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "EUC_JP" - invalid,incomplete char | \x8bc68bcf8b | \x | \x8bc68bcf8b | character with byte sequence 0x8b 0xc6 in encoding "MULE_INTERNAL" has no equivalent in encoding "EUC_JP" - valid (in SHIFT_JIS) | \x92bedd | \xbedd | | - invalid, incomplete char) | \x92be | \x | \x92be | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0xbe - valid (in Big5) | \x666f6f95a3c1 | \x666f6f | \x95a3c1 | character with byte sequence 0x95 0xa3 0xc1 in encoding "MULE_INTERNAL" has no equivalent in encoding "EUC_JP" - invalid, incomplete char | \x666f6f95a3 | \x666f6f | \x95a3 | invalid byte sequence for encoding "MULE_INTERNAL": 0x95 0xa3 - invalid, NUL byte | \x9200bedd | \x | \x9200bedd | invalid byte sequence for encoding "MULE_INTERNAL": 0x92 0x00 0xbe - invalid, NUL byte | \x92bedd00 | \xbedd | \x00 | invalid byte sequence for encoding "MULE_INTERNAL": 0x00 - invalid, NUL byte | \x8b00c68bcf8bcf | \x | \x8b00c68bcf8bcf | invalid byte sequence for encoding "MULE_INTERNAL": 0x8b 0x00 -(10 rows) - +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/truncate.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/truncate.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/truncate.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/truncate.out 2021-10-30 22:09:24 -0600 @@ -25,570 +25,10 @@ TRUNCATE truncate_a; COMMIT; SELECT * FROM truncate_a; - col1 ------- -(0 rows) - --- Test foreign-key checks -CREATE TABLE trunc_b (a int REFERENCES truncate_a); -CREATE TABLE trunc_c (a serial PRIMARY KEY); -CREATE TABLE trunc_d (a int REFERENCES trunc_c); -CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c); -TRUNCATE TABLE truncate_a; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_b" references "truncate_a". -HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE truncate_a,trunc_b; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_e" references "truncate_a". -HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE truncate_a,trunc_b,trunc_e; -- ok -TRUNCATE TABLE truncate_a,trunc_e; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_b" references "truncate_a". -HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_d" references "trunc_c". -HINT: Truncate table "trunc_d" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,trunc_d; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_e" references "trunc_c". -HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,trunc_d,trunc_e; -- ok -TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_b" references "truncate_a". -HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; -- ok -TRUNCATE TABLE truncate_a RESTRICT; -- fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_b" references "truncate_a". -HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE truncate_a CASCADE; -- ok -NOTICE: truncate cascades to table "trunc_b" -NOTICE: truncate cascades to table "trunc_e" --- circular references -ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; --- Add some data to verify that truncating actually works ... -INSERT INTO trunc_c VALUES (1); -INSERT INTO truncate_a VALUES (1); -INSERT INTO trunc_b VALUES (1); -INSERT INTO trunc_d VALUES (1); -INSERT INTO trunc_e VALUES (1,1); -TRUNCATE TABLE trunc_c; -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "truncate_a" references "trunc_c". -HINT: Truncate table "truncate_a" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,truncate_a; -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_d" references "trunc_c". -HINT: Truncate table "trunc_d" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,truncate_a,trunc_d; -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_e" references "trunc_c". -HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e; -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "trunc_b" references "truncate_a". -HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. -TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e,trunc_b; --- Verify that truncating did actually work -SELECT * FROM truncate_a - UNION ALL - SELECT * FROM trunc_c - UNION ALL - SELECT * FROM trunc_b - UNION ALL - SELECT * FROM trunc_d; - col1 ------- -(0 rows) - -SELECT * FROM trunc_e; - a | b ----+--- -(0 rows) - --- Add data again to test TRUNCATE ... CASCADE -INSERT INTO trunc_c VALUES (1); -INSERT INTO truncate_a VALUES (1); -INSERT INTO trunc_b VALUES (1); -INSERT INTO trunc_d VALUES (1); -INSERT INTO trunc_e VALUES (1,1); -TRUNCATE TABLE trunc_c CASCADE; -- ok -NOTICE: truncate cascades to table "truncate_a" -NOTICE: truncate cascades to table "trunc_d" -NOTICE: truncate cascades to table "trunc_e" -NOTICE: truncate cascades to table "trunc_b" -SELECT * FROM truncate_a - UNION ALL - SELECT * FROM trunc_c - UNION ALL - SELECT * FROM trunc_b - UNION ALL - SELECT * FROM trunc_d; - col1 ------- -(0 rows) - -SELECT * FROM trunc_e; - a | b ----+--- -(0 rows) - -DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; --- Test TRUNCATE with inheritance -CREATE TABLE trunc_f (col1 integer primary key); -INSERT INTO trunc_f VALUES (1); -INSERT INTO trunc_f VALUES (2); -CREATE TABLE trunc_fa (col2a text) INHERITS (trunc_f); -INSERT INTO trunc_fa VALUES (3, 'three'); -CREATE TABLE trunc_fb (col2b int) INHERITS (trunc_f); -INSERT INTO trunc_fb VALUES (4, 444); -CREATE TABLE trunc_faa (col3 text) INHERITS (trunc_fa); -INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE'); -BEGIN; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -TRUNCATE trunc_f; -SELECT * FROM trunc_f; - col1 ------- -(0 rows) - -ROLLBACK; -BEGIN; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -TRUNCATE ONLY trunc_f; -SELECT * FROM trunc_f; - col1 ------- - 3 - 4 - 5 -(3 rows) - -ROLLBACK; -BEGIN; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -SELECT * FROM trunc_fa; - col1 | col2a -------+------- - 3 | three - 5 | five -(2 rows) - -SELECT * FROM trunc_faa; - col1 | col2a | col3 -------+-------+------ - 5 | five | FIVE -(1 row) - -TRUNCATE ONLY trunc_fb, ONLY trunc_fa; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 - 5 -(3 rows) - -SELECT * FROM trunc_fa; - col1 | col2a -------+------- - 5 | five -(1 row) - -SELECT * FROM trunc_faa; - col1 | col2a | col3 -------+-------+------ - 5 | five | FIVE -(1 row) - -ROLLBACK; -BEGIN; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -SELECT * FROM trunc_fa; - col1 | col2a -------+------- - 3 | three - 5 | five -(2 rows) - -SELECT * FROM trunc_faa; - col1 | col2a | col3 -------+-------+------ - 5 | five | FIVE -(1 row) - -TRUNCATE ONLY trunc_fb, trunc_fa; -SELECT * FROM trunc_f; - col1 ------- - 1 - 2 -(2 rows) - -SELECT * FROM trunc_fa; - col1 | col2a -------+------- -(0 rows) - -SELECT * FROM trunc_faa; - col1 | col2a | col3 -------+-------+------ -(0 rows) - -ROLLBACK; -DROP TABLE trunc_f CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table trunc_fa -drop cascades to table trunc_faa -drop cascades to table trunc_fb --- Test ON TRUNCATE triggers -CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); -CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, - tgargv text, tgtable name, rowcount bigint); -CREATE FUNCTION trunctrigger() RETURNS trigger as $$ -declare c bigint; -begin - execute 'select count(*) from ' || quote_ident(tg_table_name) into c; - insert into trunc_trigger_log values - (TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c); - return null; -end; -$$ LANGUAGE plpgsql; --- basic before trigger -INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); -CREATE TRIGGER t -BEFORE TRUNCATE ON trunc_trigger_test -FOR EACH STATEMENT -EXECUTE PROCEDURE trunctrigger('before trigger truncate'); -SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; - Row count in test table -------------------------- - 2 -(1 row) - -SELECT * FROM trunc_trigger_log; - tgop | tglevel | tgwhen | tgargv | tgtable | rowcount -------+---------+--------+--------+---------+---------- -(0 rows) - -TRUNCATE trunc_trigger_test; -SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; - Row count in test table -------------------------- - 0 -(1 row) - -SELECT * FROM trunc_trigger_log; - tgop | tglevel | tgwhen | tgargv | tgtable | rowcount -----------+-----------+--------+-------------------------+--------------------+---------- - TRUNCATE | STATEMENT | BEFORE | before trigger truncate | trunc_trigger_test | 2 -(1 row) - -DROP TRIGGER t ON trunc_trigger_test; -truncate trunc_trigger_log; --- same test with an after trigger -INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); -CREATE TRIGGER tt -AFTER TRUNCATE ON trunc_trigger_test -FOR EACH STATEMENT -EXECUTE PROCEDURE trunctrigger('after trigger truncate'); -SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; - Row count in test table -------------------------- - 2 -(1 row) - -SELECT * FROM trunc_trigger_log; - tgop | tglevel | tgwhen | tgargv | tgtable | rowcount -------+---------+--------+--------+---------+---------- -(0 rows) - -TRUNCATE trunc_trigger_test; -SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; - Row count in test table -------------------------- - 0 -(1 row) - -SELECT * FROM trunc_trigger_log; - tgop | tglevel | tgwhen | tgargv | tgtable | rowcount -----------+-----------+--------+------------------------+--------------------+---------- - TRUNCATE | STATEMENT | AFTER | after trigger truncate | trunc_trigger_test | 0 -(1 row) - -DROP TABLE trunc_trigger_test; -DROP TABLE trunc_trigger_log; -DROP FUNCTION trunctrigger(); --- test TRUNCATE ... RESTART IDENTITY -CREATE SEQUENCE truncate_a_id1 START WITH 33; -CREATE TABLE truncate_a (id serial, - id1 integer default nextval('truncate_a_id1')); -ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1; -INSERT INTO truncate_a DEFAULT VALUES; -INSERT INTO truncate_a DEFAULT VALUES; -SELECT * FROM truncate_a; - id | id1 -----+----- - 1 | 33 - 2 | 34 -(2 rows) - -TRUNCATE truncate_a; -INSERT INTO truncate_a DEFAULT VALUES; -INSERT INTO truncate_a DEFAULT VALUES; -SELECT * FROM truncate_a; - id | id1 -----+----- - 3 | 35 - 4 | 36 -(2 rows) - -TRUNCATE truncate_a RESTART IDENTITY; -INSERT INTO truncate_a DEFAULT VALUES; -INSERT INTO truncate_a DEFAULT VALUES; -SELECT * FROM truncate_a; - id | id1 -----+----- - 1 | 33 - 2 | 34 -(2 rows) - -CREATE TABLE truncate_b (id int GENERATED ALWAYS AS IDENTITY (START WITH 44)); -INSERT INTO truncate_b DEFAULT VALUES; -INSERT INTO truncate_b DEFAULT VALUES; -SELECT * FROM truncate_b; - id ----- - 44 - 45 -(2 rows) - -TRUNCATE truncate_b; -INSERT INTO truncate_b DEFAULT VALUES; -INSERT INTO truncate_b DEFAULT VALUES; -SELECT * FROM truncate_b; - id ----- - 46 - 47 -(2 rows) - -TRUNCATE truncate_b RESTART IDENTITY; -INSERT INTO truncate_b DEFAULT VALUES; -INSERT INTO truncate_b DEFAULT VALUES; -SELECT * FROM truncate_b; - id ----- - 44 - 45 -(2 rows) - --- check rollback of a RESTART IDENTITY operation -BEGIN; -TRUNCATE truncate_a RESTART IDENTITY; -INSERT INTO truncate_a DEFAULT VALUES; -SELECT * FROM truncate_a; - id | id1 -----+----- - 1 | 33 -(1 row) - -ROLLBACK; -INSERT INTO truncate_a DEFAULT VALUES; -INSERT INTO truncate_a DEFAULT VALUES; -SELECT * FROM truncate_a; - id | id1 -----+----- - 1 | 33 - 2 | 34 - 3 | 35 - 4 | 36 -(4 rows) - -DROP TABLE truncate_a; -SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped -ERROR: relation "truncate_a_id1" does not exist -LINE 1: SELECT nextval('truncate_a_id1'); - ^ --- partitioned table -CREATE TABLE truncparted (a int, b char) PARTITION BY LIST (a); --- error, can't truncate a partitioned table -TRUNCATE ONLY truncparted; -ERROR: cannot truncate only a partitioned table -HINT: Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly. -CREATE TABLE truncparted1 PARTITION OF truncparted FOR VALUES IN (1); -INSERT INTO truncparted VALUES (1, 'a'); --- error, must truncate partitions -TRUNCATE ONLY truncparted; -ERROR: cannot truncate only a partitioned table -HINT: Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly. -TRUNCATE truncparted; -DROP TABLE truncparted; --- foreign key on partitioned table: partition key is referencing column. --- Make sure truncate did execute on all tables -CREATE FUNCTION tp_ins_data() RETURNS void LANGUAGE plpgsql AS $$ - BEGIN - INSERT INTO truncprim VALUES (1), (100), (150); - INSERT INTO truncpart VALUES (1), (100), (150); - END -$$; -CREATE FUNCTION tp_chk_data(OUT pktb regclass, OUT pkval int, OUT fktb regclass, OUT fkval int) - RETURNS SETOF record LANGUAGE plpgsql AS $$ - BEGIN - RETURN QUERY SELECT - pk.tableoid::regclass, pk.a, fk.tableoid::regclass, fk.a - FROM truncprim pk FULL JOIN truncpart fk USING (a) - ORDER BY 2, 4; - END -$$; -CREATE TABLE truncprim (a int PRIMARY KEY); -CREATE TABLE truncpart (a int REFERENCES truncprim) - PARTITION BY RANGE (a); -CREATE TABLE truncpart_1 PARTITION OF truncpart FOR VALUES FROM (0) TO (100); -CREATE TABLE truncpart_2 PARTITION OF truncpart FOR VALUES FROM (100) TO (200) - PARTITION BY RANGE (a); -CREATE TABLE truncpart_2_1 PARTITION OF truncpart_2 FOR VALUES FROM (100) TO (150); -CREATE TABLE truncpart_2_d PARTITION OF truncpart_2 DEFAULT; -TRUNCATE TABLE truncprim; -- should fail -ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "truncpart" references "truncprim". -HINT: Truncate table "truncpart" at the same time, or use TRUNCATE ... CASCADE. -select tp_ins_data(); - tp_ins_data -------------- - -(1 row) - --- should truncate everything -TRUNCATE TABLE truncprim, truncpart; -select * from tp_chk_data(); - pktb | pkval | fktb | fkval -------+-------+------+------- -(0 rows) - -select tp_ins_data(); - tp_ins_data -------------- - -(1 row) - --- should truncate everything -TRUNCATE TABLE truncprim CASCADE; -NOTICE: truncate cascades to table "truncpart" -NOTICE: truncate cascades to table "truncpart_1" -NOTICE: truncate cascades to table "truncpart_2" -NOTICE: truncate cascades to table "truncpart_2_1" -NOTICE: truncate cascades to table "truncpart_2_d" -SELECT * FROM tp_chk_data(); - pktb | pkval | fktb | fkval -------+-------+------+------- -(0 rows) - -SELECT tp_ins_data(); - tp_ins_data -------------- - -(1 row) - --- should truncate all partitions -TRUNCATE TABLE truncpart; -SELECT * FROM tp_chk_data(); - pktb | pkval | fktb | fkval ------------+-------+------+------- - truncprim | 1 | | - truncprim | 100 | | - truncprim | 150 | | -(3 rows) - -DROP TABLE truncprim, truncpart; -DROP FUNCTION tp_ins_data(), tp_chk_data(); --- test cascade when referencing a partitioned table -CREATE TABLE trunc_a (a INT PRIMARY KEY) PARTITION BY RANGE (a); -CREATE TABLE trunc_a1 PARTITION OF trunc_a FOR VALUES FROM (0) TO (10); -CREATE TABLE trunc_a2 PARTITION OF trunc_a FOR VALUES FROM (10) TO (20) - PARTITION BY RANGE (a); -CREATE TABLE trunc_a21 PARTITION OF trunc_a2 FOR VALUES FROM (10) TO (12); -CREATE TABLE trunc_a22 PARTITION OF trunc_a2 FOR VALUES FROM (12) TO (16); -CREATE TABLE trunc_a2d PARTITION OF trunc_a2 DEFAULT; -CREATE TABLE trunc_a3 PARTITION OF trunc_a FOR VALUES FROM (20) TO (30); -INSERT INTO trunc_a VALUES (0), (5), (10), (15), (20), (25); --- truncate a partition cascading to a table -CREATE TABLE ref_b ( - b INT PRIMARY KEY, - a INT REFERENCES trunc_a(a) ON DELETE CASCADE -); -INSERT INTO ref_b VALUES (10, 0), (50, 5), (100, 10), (150, 15); -TRUNCATE TABLE trunc_a1 CASCADE; -NOTICE: truncate cascades to table "ref_b" -SELECT a FROM ref_b; - a ---- -(0 rows) - -DROP TABLE ref_b; --- truncate a partition cascading to a partitioned table -CREATE TABLE ref_c ( - c INT PRIMARY KEY, - a INT REFERENCES trunc_a(a) ON DELETE CASCADE -) PARTITION BY RANGE (c); -CREATE TABLE ref_c1 PARTITION OF ref_c FOR VALUES FROM (100) TO (200); -CREATE TABLE ref_c2 PARTITION OF ref_c FOR VALUES FROM (200) TO (300); -INSERT INTO ref_c VALUES (100, 10), (150, 15), (200, 20), (250, 25); -TRUNCATE TABLE trunc_a21 CASCADE; -NOTICE: truncate cascades to table "ref_c" -NOTICE: truncate cascades to table "ref_c1" -NOTICE: truncate cascades to table "ref_c2" -SELECT a as "from table ref_c" FROM ref_c; - from table ref_c ------------------- -(0 rows) - -SELECT a as "from table trunc_a" FROM trunc_a ORDER BY a; - from table trunc_a --------------------- - 15 - 20 - 25 -(3 rows) - -DROP TABLE trunc_a, ref_c; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_table.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_table.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/alter_table.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/alter_table.out 2021-10-30 22:09:24 -0600 @@ -19,4541 +19,7 @@ ALTER TABLE attmp ADD COLUMN a int4 default 3; ALTER TABLE attmp ADD COLUMN b name; ALTER TABLE attmp ADD COLUMN c text; -ALTER TABLE attmp ADD COLUMN d float8; -ALTER TABLE attmp ADD COLUMN e float4; -ALTER TABLE attmp ADD COLUMN f int2; -ALTER TABLE attmp ADD COLUMN g polygon; -ALTER TABLE attmp ADD COLUMN i char; -ALTER TABLE attmp ADD COLUMN k int4; -ALTER TABLE attmp ADD COLUMN l tid; -ALTER TABLE attmp ADD COLUMN m xid; -ALTER TABLE attmp ADD COLUMN n oidvector; ---ALTER TABLE attmp ADD COLUMN o lock; -ALTER TABLE attmp ADD COLUMN p boolean; -ALTER TABLE attmp ADD COLUMN q point; -ALTER TABLE attmp ADD COLUMN r lseg; -ALTER TABLE attmp ADD COLUMN s path; -ALTER TABLE attmp ADD COLUMN t box; -ALTER TABLE attmp ADD COLUMN v timestamp; -ALTER TABLE attmp ADD COLUMN w interval; -ALTER TABLE attmp ADD COLUMN x float8[]; -ALTER TABLE attmp ADD COLUMN y float4[]; -ALTER TABLE attmp ADD COLUMN z int2[]; -INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, - v, w, x, y, z) - VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', - 'c', - 314159, '(1,1)', '512', - '1 2 3 4 5 6 7 8', true, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', - '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', - 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); -SELECT * FROM attmp; - initial | a | b | c | d | e | f | g | i | k | l | m | n | p | q | r | s | t | v | w | x | y | z ----------+---+------+------+-----+-----+---+-----------------------+---+--------+-------+-----+-----------------+---+-----------+-----------------------+-----------------------------+---------------------+--------------------------+------------------+-----------+-----------+----------- - | 4 | name | text | 4.1 | 4.1 | 2 | ((4.1,4.1),(3.1,3.1)) | c | 314159 | (1,1) | 512 | 1 2 3 4 5 6 7 8 | t | (1.1,1.1) | [(4.1,4.1),(3.1,3.1)] | ((0,2),(4.1,4.1),(3.1,3.1)) | (4.1,4.1),(3.1,3.1) | Thu Jan 01 00:00:00 1970 | @ 1 hour 10 secs | {1,2,3,4} | {1,2,3,4} | {1,2,3,4} -(1 row) - -DROP TABLE attmp; --- the wolf bug - schema mods caused inconsistent row descriptors -CREATE TABLE attmp ( - initial int4 -); -ALTER TABLE attmp ADD COLUMN a int4; -ALTER TABLE attmp ADD COLUMN b name; -ALTER TABLE attmp ADD COLUMN c text; -ALTER TABLE attmp ADD COLUMN d float8; -ALTER TABLE attmp ADD COLUMN e float4; -ALTER TABLE attmp ADD COLUMN f int2; -ALTER TABLE attmp ADD COLUMN g polygon; -ALTER TABLE attmp ADD COLUMN i char; -ALTER TABLE attmp ADD COLUMN k int4; -ALTER TABLE attmp ADD COLUMN l tid; -ALTER TABLE attmp ADD COLUMN m xid; -ALTER TABLE attmp ADD COLUMN n oidvector; ---ALTER TABLE attmp ADD COLUMN o lock; -ALTER TABLE attmp ADD COLUMN p boolean; -ALTER TABLE attmp ADD COLUMN q point; -ALTER TABLE attmp ADD COLUMN r lseg; -ALTER TABLE attmp ADD COLUMN s path; -ALTER TABLE attmp ADD COLUMN t box; -ALTER TABLE attmp ADD COLUMN v timestamp; -ALTER TABLE attmp ADD COLUMN w interval; -ALTER TABLE attmp ADD COLUMN x float8[]; -ALTER TABLE attmp ADD COLUMN y float4[]; -ALTER TABLE attmp ADD COLUMN z int2[]; -INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, - v, w, x, y, z) - VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', - 'c', - 314159, '(1,1)', '512', - '1 2 3 4 5 6 7 8', true, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', - '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', - 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); -SELECT * FROM attmp; - initial | a | b | c | d | e | f | g | i | k | l | m | n | p | q | r | s | t | v | w | x | y | z ----------+---+------+------+-----+-----+---+-----------------------+---+--------+-------+-----+-----------------+---+-----------+-----------------------+-----------------------------+---------------------+--------------------------+------------------+-----------+-----------+----------- - | 4 | name | text | 4.1 | 4.1 | 2 | ((4.1,4.1),(3.1,3.1)) | c | 314159 | (1,1) | 512 | 1 2 3 4 5 6 7 8 | t | (1.1,1.1) | [(4.1,4.1),(3.1,3.1)] | ((0,2),(4.1,4.1),(3.1,3.1)) | (4.1,4.1),(3.1,3.1) | Thu Jan 01 00:00:00 1970 | @ 1 hour 10 secs | {1,2,3,4} | {1,2,3,4} | {1,2,3,4} -(1 row) - -CREATE INDEX attmp_idx ON attmp (a, (d + e), b); -ALTER INDEX attmp_idx ALTER COLUMN 0 SET STATISTICS 1000; -ERROR: column number must be in range from 1 to 32767 -LINE 1: ALTER INDEX attmp_idx ALTER COLUMN 0 SET STATISTICS 1000; - ^ -ALTER INDEX attmp_idx ALTER COLUMN 1 SET STATISTICS 1000; -ERROR: cannot alter statistics on non-expression column "a" of index "attmp_idx" -HINT: Alter statistics on table column instead. -ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS 1000; -\d+ attmp_idx - Index "public.attmp_idx" - Column | Type | Key? | Definition | Storage | Stats target ---------+------------------+------+------------+---------+-------------- - a | integer | yes | a | plain | - expr | double precision | yes | (d + e) | plain | 1000 - b | cstring | yes | b | plain | -btree, for table "public.attmp" - -ALTER INDEX attmp_idx ALTER COLUMN 3 SET STATISTICS 1000; -ERROR: cannot alter statistics on non-expression column "b" of index "attmp_idx" -HINT: Alter statistics on table column instead. -ALTER INDEX attmp_idx ALTER COLUMN 4 SET STATISTICS 1000; -ERROR: column number 4 of relation "attmp_idx" does not exist -ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS -1; -DROP TABLE attmp; --- --- rename - check on both non-temp and temp tables --- -CREATE TABLE attmp (regtable int); -CREATE TEMP TABLE attmp (attmptable int); -ALTER TABLE attmp RENAME TO attmp_new; -SELECT * FROM attmp; - regtable ----------- -(0 rows) - -SELECT * FROM attmp_new; - attmptable ------------- -(0 rows) - -ALTER TABLE attmp RENAME TO attmp_new2; -SELECT * FROM attmp; -- should fail -ERROR: relation "attmp" does not exist -LINE 1: SELECT * FROM attmp; - ^ -SELECT * FROM attmp_new; - attmptable ------------- -(0 rows) - -SELECT * FROM attmp_new2; - regtable ----------- -(0 rows) - -DROP TABLE attmp_new; -DROP TABLE attmp_new2; --- check rename of partitioned tables and indexes also -CREATE TABLE part_attmp (a int primary key) partition by range (a); -CREATE TABLE part_attmp1 PARTITION OF part_attmp FOR VALUES FROM (0) TO (100); -ALTER INDEX part_attmp_pkey RENAME TO part_attmp_index; -ALTER INDEX part_attmp1_pkey RENAME TO part_attmp1_index; -ALTER TABLE part_attmp RENAME TO part_at2tmp; -ALTER TABLE part_attmp1 RENAME TO part_at2tmp1; -SET ROLE regress_alter_table_user1; -ALTER INDEX part_attmp_index RENAME TO fail; -ERROR: must be owner of index part_attmp_index -ALTER INDEX part_attmp1_index RENAME TO fail; -ERROR: must be owner of index part_attmp1_index -ALTER TABLE part_at2tmp RENAME TO fail; -ERROR: must be owner of table part_at2tmp -ALTER TABLE part_at2tmp1 RENAME TO fail; -ERROR: must be owner of table part_at2tmp1 -RESET ROLE; -DROP TABLE part_at2tmp; --- --- check renaming to a table's array type's autogenerated name --- (the array type's name should get out of the way) --- -CREATE TABLE attmp_array (id int); -CREATE TABLE attmp_array2 (id int); -SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; - typname --------------- - _attmp_array -(1 row) - -SELECT typname FROM pg_type WHERE oid = 'attmp_array2[]'::regtype; - typname ---------------- - _attmp_array2 -(1 row) - -ALTER TABLE attmp_array2 RENAME TO _attmp_array; -SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; - typname ---------------- - __attmp_array -(1 row) - -SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; - typname ----------------- - ___attmp_array -(1 row) - -DROP TABLE _attmp_array; -DROP TABLE attmp_array; --- renaming to table's own array type's name is an interesting corner case -CREATE TABLE attmp_array (id int); -SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; - typname --------------- - _attmp_array -(1 row) - -ALTER TABLE attmp_array RENAME TO _attmp_array; -SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; - typname ---------------- - __attmp_array -(1 row) - -DROP TABLE _attmp_array; --- ALTER TABLE ... RENAME on non-table relations --- renaming indexes (FIXME: this should probably test the index's functionality) -ALTER INDEX IF EXISTS __onek_unique1 RENAME TO attmp_onek_unique1; -NOTICE: relation "__onek_unique1" does not exist, skipping -ALTER INDEX IF EXISTS __attmp_onek_unique1 RENAME TO onek_unique1; -NOTICE: relation "__attmp_onek_unique1" does not exist, skipping -ALTER INDEX onek_unique1 RENAME TO attmp_onek_unique1; -ALTER INDEX attmp_onek_unique1 RENAME TO onek_unique1; -SET ROLE regress_alter_table_user1; -ALTER INDEX onek_unique1 RENAME TO fail; -- permission denied -ERROR: must be owner of index onek_unique1 -RESET ROLE; --- rename statements with mismatching statement and object types -CREATE TABLE alter_idx_rename_test (a INT); -CREATE INDEX alter_idx_rename_test_idx ON alter_idx_rename_test (a); -CREATE TABLE alter_idx_rename_test_parted (a INT) PARTITION BY LIST (a); -CREATE INDEX alter_idx_rename_test_parted_idx ON alter_idx_rename_test_parted (a); -BEGIN; -ALTER INDEX alter_idx_rename_test RENAME TO alter_idx_rename_test_2; -ALTER INDEX alter_idx_rename_test_parted RENAME TO alter_idx_rename_test_parted_2; -SELECT relation::regclass, mode FROM pg_locks -WHERE pid = pg_backend_pid() AND locktype = 'relation' - AND relation::regclass::text LIKE 'alter\_idx%' -ORDER BY relation::regclass::text COLLATE "C"; - relation | mode ---------------------------------+--------------------- - alter_idx_rename_test_2 | AccessExclusiveLock - alter_idx_rename_test_parted_2 | AccessExclusiveLock -(2 rows) - -COMMIT; -BEGIN; -ALTER INDEX alter_idx_rename_test_idx RENAME TO alter_idx_rename_test_idx_2; -ALTER INDEX alter_idx_rename_test_parted_idx RENAME TO alter_idx_rename_test_parted_idx_2; -SELECT relation::regclass, mode FROM pg_locks -WHERE pid = pg_backend_pid() AND locktype = 'relation' - AND relation::regclass::text LIKE 'alter\_idx%' -ORDER BY relation::regclass::text COLLATE "C"; - relation | mode -------------------------------------+-------------------------- - alter_idx_rename_test_idx_2 | ShareUpdateExclusiveLock - alter_idx_rename_test_parted_idx_2 | ShareUpdateExclusiveLock -(2 rows) - -COMMIT; -BEGIN; -ALTER TABLE alter_idx_rename_test_idx_2 RENAME TO alter_idx_rename_test_idx_3; -ALTER TABLE alter_idx_rename_test_parted_idx_2 RENAME TO alter_idx_rename_test_parted_idx_3; -SELECT relation::regclass, mode FROM pg_locks -WHERE pid = pg_backend_pid() AND locktype = 'relation' - AND relation::regclass::text LIKE 'alter\_idx%' -ORDER BY relation::regclass::text COLLATE "C"; - relation | mode -------------------------------------+--------------------- - alter_idx_rename_test_idx_3 | AccessExclusiveLock - alter_idx_rename_test_parted_idx_3 | AccessExclusiveLock -(2 rows) - -COMMIT; -DROP TABLE alter_idx_rename_test_2; --- renaming views -CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1; -ALTER TABLE attmp_view RENAME TO attmp_view_new; -SET ROLE regress_alter_table_user1; -ALTER VIEW attmp_view_new RENAME TO fail; -- permission denied -ERROR: must be owner of view attmp_view_new -RESET ROLE; --- hack to ensure we get an indexscan here -set enable_seqscan to off; -set enable_bitmapscan to off; --- 5 values, sorted -SELECT unique1 FROM tenk1 WHERE unique1 < 5; - unique1 ---------- - 0 - 1 - 2 - 3 - 4 -(5 rows) - -reset enable_seqscan; -reset enable_bitmapscan; -DROP VIEW attmp_view_new; --- toast-like relation name -alter table stud_emp rename to pg_toast_stud_emp; -alter table pg_toast_stud_emp rename to stud_emp; --- renaming index should rename constraint as well -ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); -ALTER INDEX onek_unique1_constraint RENAME TO onek_unique1_constraint_foo; -ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; --- renaming constraint -ALTER TABLE onek ADD CONSTRAINT onek_check_constraint CHECK (unique1 >= 0); -ALTER TABLE onek RENAME CONSTRAINT onek_check_constraint TO onek_check_constraint_foo; -ALTER TABLE onek DROP CONSTRAINT onek_check_constraint_foo; --- renaming constraint should rename index as well -ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); -DROP INDEX onek_unique1_constraint; -- to see whether it's there -ERROR: cannot drop index onek_unique1_constraint because constraint onek_unique1_constraint on table onek requires it -HINT: You can drop constraint onek_unique1_constraint on table onek instead. -ALTER TABLE onek RENAME CONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo; -DROP INDEX onek_unique1_constraint_foo; -- to see whether it's there -ERROR: cannot drop index onek_unique1_constraint_foo because constraint onek_unique1_constraint_foo on table onek requires it -HINT: You can drop constraint onek_unique1_constraint_foo on table onek instead. -ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; --- renaming constraints vs. inheritance -CREATE TABLE constraint_rename_test (a int CONSTRAINT con1 CHECK (a > 0), b int, c int); -\d constraint_rename_test - Table "public.constraint_rename_test" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | -Check constraints: - "con1" CHECK (a > 0) - -CREATE TABLE constraint_rename_test2 (a int CONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test); -NOTICE: merging column "a" with inherited definition -NOTICE: merging constraint "con1" with inherited definition -\d constraint_rename_test2 - Table "public.constraint_rename_test2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | - d | integer | | | -Check constraints: - "con1" CHECK (a > 0) -Inherits: constraint_rename_test - -ALTER TABLE constraint_rename_test2 RENAME CONSTRAINT con1 TO con1foo; -- fail -ERROR: cannot rename inherited constraint "con1" -ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fail -ERROR: inherited constraint "con1" must be renamed in child tables too -ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok -\d constraint_rename_test - Table "public.constraint_rename_test" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | -Check constraints: - "con1foo" CHECK (a > 0) -Number of child tables: 1 (Use \d+ to list them.) - -\d constraint_rename_test2 - Table "public.constraint_rename_test2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | - d | integer | | | -Check constraints: - "con1foo" CHECK (a > 0) -Inherits: constraint_rename_test - -ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0) NO INHERIT; -ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok -ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok -\d constraint_rename_test - Table "public.constraint_rename_test" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | -Check constraints: - "con1foo" CHECK (a > 0) - "con2bar" CHECK (b > 0) NO INHERIT -Number of child tables: 1 (Use \d+ to list them.) - -\d constraint_rename_test2 - Table "public.constraint_rename_test2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | - d | integer | | | -Check constraints: - "con1foo" CHECK (a > 0) -Inherits: constraint_rename_test - -ALTER TABLE constraint_rename_test ADD CONSTRAINT con3 PRIMARY KEY (a); -ALTER TABLE constraint_rename_test RENAME CONSTRAINT con3 TO con3foo; -- ok -\d constraint_rename_test - Table "public.constraint_rename_test" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | - b | integer | | | - c | integer | | | -Indexes: - "con3foo" PRIMARY KEY, btree (a) -Check constraints: - "con1foo" CHECK (a > 0) - "con2bar" CHECK (b > 0) NO INHERIT -Number of child tables: 1 (Use \d+ to list them.) - -\d constraint_rename_test2 - Table "public.constraint_rename_test2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | - b | integer | | | - c | integer | | | - d | integer | | | -Check constraints: - "con1foo" CHECK (a > 0) -Inherits: constraint_rename_test - -DROP TABLE constraint_rename_test2; -DROP TABLE constraint_rename_test; -ALTER TABLE IF EXISTS constraint_not_exist RENAME CONSTRAINT con3 TO con3foo; -- ok -NOTICE: relation "constraint_not_exist" does not exist, skipping -ALTER TABLE IF EXISTS constraint_rename_test ADD CONSTRAINT con4 UNIQUE (a); -NOTICE: relation "constraint_rename_test" does not exist, skipping --- renaming constraints with cache reset of target relation -CREATE TABLE constraint_rename_cache (a int, - CONSTRAINT chk_a CHECK (a > 0), - PRIMARY KEY (a)); -ALTER TABLE constraint_rename_cache - RENAME CONSTRAINT chk_a TO chk_a_new; -ALTER TABLE constraint_rename_cache - RENAME CONSTRAINT constraint_rename_cache_pkey TO constraint_rename_pkey_new; -CREATE TABLE like_constraint_rename_cache - (LIKE constraint_rename_cache INCLUDING ALL); -\d like_constraint_rename_cache - Table "public.like_constraint_rename_cache" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | -Indexes: - "like_constraint_rename_cache_pkey" PRIMARY KEY, btree (a) -Check constraints: - "chk_a_new" CHECK (a > 0) - -DROP TABLE constraint_rename_cache; -DROP TABLE like_constraint_rename_cache; --- FOREIGN KEY CONSTRAINT adding TEST -CREATE TABLE attmp2 (a int primary key); -CREATE TABLE attmp3 (a int, b int); -CREATE TABLE attmp4 (a int, b int, unique(a,b)); -CREATE TABLE attmp5 (a int, b int); --- Insert rows into attmp2 (pktable) -INSERT INTO attmp2 values (1); -INSERT INTO attmp2 values (2); -INSERT INTO attmp2 values (3); -INSERT INTO attmp2 values (4); --- Insert rows into attmp3 -INSERT INTO attmp3 values (1,10); -INSERT INTO attmp3 values (1,20); -INSERT INTO attmp3 values (5,50); --- Try (and fail) to add constraint due to invalid source columns -ALTER TABLE attmp3 add constraint attmpconstr foreign key(c) references attmp2 match full; -ERROR: column "c" referenced in foreign key constraint does not exist --- Try (and fail) to add constraint due to invalid destination columns explicitly given -ALTER TABLE attmp3 add constraint attmpconstr foreign key(a) references attmp2(b) match full; -ERROR: column "b" referenced in foreign key constraint does not exist --- Try (and fail) to add constraint due to invalid data -ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full; -ERROR: insert or update on table "attmp3" violates foreign key constraint "attmpconstr" -DETAIL: Key (a)=(5) is not present in table "attmp2". --- Delete failing row -DELETE FROM attmp3 where a=5; --- Try (and succeed) -ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full; -ALTER TABLE attmp3 drop constraint attmpconstr; -INSERT INTO attmp3 values (5,50); --- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate -ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full NOT VALID; -ALTER TABLE attmp3 validate constraint attmpconstr; -ERROR: insert or update on table "attmp3" violates foreign key constraint "attmpconstr" -DETAIL: Key (a)=(5) is not present in table "attmp2". --- Delete failing row -DELETE FROM attmp3 where a=5; --- Try (and succeed) and repeat to show it works on already valid constraint -ALTER TABLE attmp3 validate constraint attmpconstr; -ALTER TABLE attmp3 validate constraint attmpconstr; --- Try a non-verified CHECK constraint -ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10); -- fail -ERROR: check constraint "b_greater_than_ten" of relation "attmp3" is violated by some row -ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10) NOT VALID; -- succeeds -ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- fails -ERROR: check constraint "b_greater_than_ten" of relation "attmp3" is violated by some row -DELETE FROM attmp3 WHERE NOT b > 10; -ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds -ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds --- Test inherited NOT VALID CHECK constraints -select * from attmp3; - a | b ----+---- - 1 | 20 -(1 row) - -CREATE TABLE attmp6 () INHERITS (attmp3); -CREATE TABLE attmp7 () INHERITS (attmp3); -INSERT INTO attmp6 VALUES (6, 30), (7, 16); -ALTER TABLE attmp3 ADD CONSTRAINT b_le_20 CHECK (b <= 20) NOT VALID; -ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- fails -ERROR: check constraint "b_le_20" of relation "attmp6" is violated by some row -DELETE FROM attmp6 WHERE b > 20; -ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- succeeds --- An already validated constraint must not be revalidated -CREATE FUNCTION boo(int) RETURNS int IMMUTABLE STRICT LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'boo: %', $1; RETURN $1; END; $$; -INSERT INTO attmp7 VALUES (8, 18); -ALTER TABLE attmp7 ADD CONSTRAINT identity CHECK (b = boo(b)); -NOTICE: boo: 18 -ALTER TABLE attmp3 ADD CONSTRAINT IDENTITY check (b = boo(b)) NOT VALID; -NOTICE: merging constraint "identity" with inherited definition -ALTER TABLE attmp3 VALIDATE CONSTRAINT identity; -NOTICE: boo: 20 -NOTICE: boo: 16 --- A NO INHERIT constraint should not be looked for in children during VALIDATE CONSTRAINT -create table parent_noinh_convalid (a int); -create table child_noinh_convalid () inherits (parent_noinh_convalid); -insert into parent_noinh_convalid values (1); -insert into child_noinh_convalid values (1); -alter table parent_noinh_convalid add constraint check_a_is_2 check (a = 2) no inherit not valid; --- fail, because of the row in parent -alter table parent_noinh_convalid validate constraint check_a_is_2; -ERROR: check constraint "check_a_is_2" of relation "parent_noinh_convalid" is violated by some row -delete from only parent_noinh_convalid; --- ok (parent itself contains no violating rows) -alter table parent_noinh_convalid validate constraint check_a_is_2; -select convalidated from pg_constraint where conrelid = 'parent_noinh_convalid'::regclass and conname = 'check_a_is_2'; - convalidated --------------- - t -(1 row) - --- cleanup -drop table parent_noinh_convalid, child_noinh_convalid; --- Try (and fail) to create constraint from attmp5(a) to attmp4(a) - unique constraint on --- attmp4 is a,b -ALTER TABLE attmp5 add constraint attmpconstr foreign key(a) references attmp4(a) match full; -ERROR: there is no unique constraint matching given keys for referenced table "attmp4" -DROP TABLE attmp7; -DROP TABLE attmp6; -DROP TABLE attmp5; -DROP TABLE attmp4; -DROP TABLE attmp3; -DROP TABLE attmp2; --- NOT VALID with plan invalidation -- ensure we don't use a constraint for --- exclusion until validated -set constraint_exclusion TO 'partition'; -create table nv_parent (d date, check (false) no inherit not valid); --- not valid constraint added at creation time should automatically become valid -\d nv_parent - Table "public.nv_parent" - Column | Type | Collation | Nullable | Default ---------+------+-----------+----------+--------- - d | date | | | -Check constraints: - "nv_parent_check" CHECK (false) NO INHERIT - -create table nv_child_2010 () inherits (nv_parent); -create table nv_child_2011 () inherits (nv_parent); -alter table nv_child_2010 add check (d between '2010-01-01'::date and '2010-12-31'::date) not valid; -alter table nv_child_2011 add check (d between '2011-01-01'::date and '2011-12-31'::date) not valid; -explain (costs off) select * from nv_parent where d between '2011-08-01' and '2011-08-31'; - QUERY PLAN ---------------------------------------------------------------------------- - Append - -> Seq Scan on nv_parent nv_parent_1 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) - -> Seq Scan on nv_child_2010 nv_parent_2 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) - -> Seq Scan on nv_child_2011 nv_parent_3 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) -(7 rows) - -create table nv_child_2009 (check (d between '2009-01-01'::date and '2009-12-31'::date)) inherits (nv_parent); -explain (costs off) select * from nv_parent where d between '2011-08-01'::date and '2011-08-31'::date; - QUERY PLAN ---------------------------------------------------------------------------- - Append - -> Seq Scan on nv_parent nv_parent_1 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) - -> Seq Scan on nv_child_2010 nv_parent_2 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) - -> Seq Scan on nv_child_2011 nv_parent_3 - Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date)) -(7 rows) - -explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date; - QUERY PLAN ---------------------------------------------------------------------------- - Append - -> Seq Scan on nv_parent nv_parent_1 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) - -> Seq Scan on nv_child_2010 nv_parent_2 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) - -> Seq Scan on nv_child_2011 nv_parent_3 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) - -> Seq Scan on nv_child_2009 nv_parent_4 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) -(9 rows) - --- after validation, the constraint should be used -alter table nv_child_2011 VALIDATE CONSTRAINT nv_child_2011_d_check; -explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date; - QUERY PLAN ---------------------------------------------------------------------------- - Append - -> Seq Scan on nv_parent nv_parent_1 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) - -> Seq Scan on nv_child_2010 nv_parent_2 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) - -> Seq Scan on nv_child_2009 nv_parent_3 - Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date)) -(7 rows) - --- add an inherited NOT VALID constraint -alter table nv_parent add check (d between '2001-01-01'::date and '2099-12-31'::date) not valid; -\d nv_child_2009 - Table "public.nv_child_2009" - Column | Type | Collation | Nullable | Default ---------+------+-----------+----------+--------- - d | date | | | -Check constraints: - "nv_child_2009_d_check" CHECK (d >= '01-01-2009'::date AND d <= '12-31-2009'::date) - "nv_parent_d_check" CHECK (d >= '01-01-2001'::date AND d <= '12-31-2099'::date) NOT VALID -Inherits: nv_parent - --- we leave nv_parent and children around to help test pg_dump logic --- Foreign key adding test with mixed types --- Note: these tables are TEMP to avoid name conflicts when this test --- is run in parallel with foreign_key.sql. -CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY); -INSERT INTO PKTABLE VALUES(42); -CREATE TEMP TABLE FKTABLE (ftest1 inet); --- This next should fail, because int=inet does not exist -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer. --- This should also fail for the same reason, but here we --- give the column name -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); -ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer. -DROP TABLE FKTABLE; --- This should succeed, even though they are different types, --- because int=int8 exists and is a member of the integer opfamily -CREATE TEMP TABLE FKTABLE (ftest1 int8); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; --- Check it actually works -INSERT INTO FKTABLE VALUES(42); -- should succeed -INSERT INTO FKTABLE VALUES(43); -- should fail -ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey" -DETAIL: Key (ftest1)=(43) is not present in table "pktable". -DROP TABLE FKTABLE; --- This should fail, because we'd have to cast numeric to int which is --- not an implicit coercion (or use numeric=numeric, but that's not part --- of the integer opfamily) -CREATE TEMP TABLE FKTABLE (ftest1 numeric); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: numeric and integer. -DROP TABLE FKTABLE; -DROP TABLE PKTABLE; --- On the other hand, this should work because int implicitly promotes to --- numeric, and we allow promotion on the FK side -CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY); -INSERT INTO PKTABLE VALUES(42); -CREATE TEMP TABLE FKTABLE (ftest1 int); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; --- Check it actually works -INSERT INTO FKTABLE VALUES(42); -- should succeed -INSERT INTO FKTABLE VALUES(43); -- should fail -ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey" -DETAIL: Key (ftest1)=(43) is not present in table "pktable". -DROP TABLE FKTABLE; -DROP TABLE PKTABLE; -CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, - PRIMARY KEY(ptest1, ptest2)); --- This should fail, because we just chose really odd types -CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; -ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer. -DROP TABLE FKTABLE; --- Again, so should this... -CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) - references pktable(ptest1, ptest2); -ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer. -DROP TABLE FKTABLE; --- This fails because we mixed up the column ordering -CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet); -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) - references pktable(ptest2, ptest1); -ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented -DETAIL: Key columns "ftest1" and "ptest2" are of incompatible types: integer and inet. --- As does this... -ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1) - references pktable(ptest1, ptest2); -ERROR: foreign key constraint "fktable_ftest2_ftest1_fkey" cannot be implemented -DETAIL: Key columns "ftest2" and "ptest1" are of incompatible types: inet and integer. -DROP TABLE FKTABLE; -DROP TABLE PKTABLE; --- Test that ALTER CONSTRAINT updates trigger deferrability properly -CREATE TEMP TABLE PKTABLE (ptest1 int primary key); -CREATE TEMP TABLE FKTABLE (ftest1 int); -ALTER TABLE FKTABLE ADD CONSTRAINT fknd FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; -ALTER TABLE FKTABLE ADD CONSTRAINT fkdd FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; -ALTER TABLE FKTABLE ADD CONSTRAINT fkdi FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE; -ALTER TABLE FKTABLE ADD CONSTRAINT fknd2 FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; -ALTER TABLE FKTABLE ALTER CONSTRAINT fknd2 NOT DEFERRABLE; -ALTER TABLE FKTABLE ADD CONSTRAINT fkdd2 FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; -ALTER TABLE FKTABLE ALTER CONSTRAINT fkdd2 DEFERRABLE INITIALLY DEFERRED; -ALTER TABLE FKTABLE ADD CONSTRAINT fkdi2 FOREIGN KEY(ftest1) REFERENCES pktable - ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; -ALTER TABLE FKTABLE ALTER CONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE; -SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred -FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint -WHERE tgrelid = 'pktable'::regclass -ORDER BY 1,2,3; - conname | tgfoid | tgtype | tgdeferrable | tginitdeferred ----------+------------------------+--------+--------------+---------------- - fkdd | "RI_FKey_cascade_del" | 9 | f | f - fkdd | "RI_FKey_noaction_upd" | 17 | t | t - fkdd2 | "RI_FKey_cascade_del" | 9 | f | f - fkdd2 | "RI_FKey_noaction_upd" | 17 | t | t - fkdi | "RI_FKey_cascade_del" | 9 | f | f - fkdi | "RI_FKey_noaction_upd" | 17 | t | f - fkdi2 | "RI_FKey_cascade_del" | 9 | f | f - fkdi2 | "RI_FKey_noaction_upd" | 17 | t | f - fknd | "RI_FKey_cascade_del" | 9 | f | f - fknd | "RI_FKey_noaction_upd" | 17 | f | f - fknd2 | "RI_FKey_cascade_del" | 9 | f | f - fknd2 | "RI_FKey_noaction_upd" | 17 | f | f -(12 rows) - -SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred -FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint -WHERE tgrelid = 'fktable'::regclass -ORDER BY 1,2,3; - conname | tgfoid | tgtype | tgdeferrable | tginitdeferred ----------+---------------------+--------+--------------+---------------- - fkdd | "RI_FKey_check_ins" | 5 | t | t - fkdd | "RI_FKey_check_upd" | 17 | t | t - fkdd2 | "RI_FKey_check_ins" | 5 | t | t - fkdd2 | "RI_FKey_check_upd" | 17 | t | t - fkdi | "RI_FKey_check_ins" | 5 | t | f - fkdi | "RI_FKey_check_upd" | 17 | t | f - fkdi2 | "RI_FKey_check_ins" | 5 | t | f - fkdi2 | "RI_FKey_check_upd" | 17 | t | f - fknd | "RI_FKey_check_ins" | 5 | f | f - fknd | "RI_FKey_check_upd" | 17 | f | f - fknd2 | "RI_FKey_check_ins" | 5 | f | f - fknd2 | "RI_FKey_check_upd" | 17 | f | f -(12 rows) - --- temp tables should go away by themselves, need not drop them. --- test check constraint adding -create table atacc1 ( test int ); --- add a check constraint -alter table atacc1 add constraint atacc_test1 check (test>3); --- should fail -insert into atacc1 (test) values (2); -ERROR: new row for relation "atacc1" violates check constraint "atacc_test1" -DETAIL: Failing row contains (2). --- should succeed -insert into atacc1 (test) values (4); -drop table atacc1; --- let's do one where the check fails when added -create table atacc1 ( test int ); --- insert a soon to be failing row -insert into atacc1 (test) values (2); --- add a check constraint (fails) -alter table atacc1 add constraint atacc_test1 check (test>3); -ERROR: check constraint "atacc_test1" of relation "atacc1" is violated by some row -insert into atacc1 (test) values (4); -drop table atacc1; --- let's do one where the check fails because the column doesn't exist -create table atacc1 ( test int ); --- add a check constraint (fails) -alter table atacc1 add constraint atacc_test1 check (test1>3); -ERROR: column "test1" does not exist -HINT: Perhaps you meant to reference the column "atacc1.test". -drop table atacc1; --- something a little more complicated -create table atacc1 ( test int, test2 int, test3 int); --- add a check constraint (fails) -alter table atacc1 add constraint atacc_test1 check (test+test23), test2 int); -alter table atacc1 add check (test2>test); --- should fail for $2 -insert into atacc1 (test2, test) values (3, 4); -ERROR: new row for relation "atacc1" violates check constraint "atacc1_check" -DETAIL: Failing row contains (4, 3). -drop table atacc1; --- inheritance related tests -create table atacc1 (test int); -create table atacc2 (test2 int); -create table atacc3 (test3 int) inherits (atacc1, atacc2); -alter table atacc2 add constraint foo check (test2>0); --- fail and then succeed on atacc2 -insert into atacc2 (test2) values (-3); -ERROR: new row for relation "atacc2" violates check constraint "foo" -DETAIL: Failing row contains (-3). -insert into atacc2 (test2) values (3); --- fail and then succeed on atacc3 -insert into atacc3 (test2) values (-3); -ERROR: new row for relation "atacc3" violates check constraint "foo" -DETAIL: Failing row contains (null, -3, null). -insert into atacc3 (test2) values (3); -drop table atacc3; -drop table atacc2; -drop table atacc1; --- same things with one created with INHERIT -create table atacc1 (test int); -create table atacc2 (test2 int); -create table atacc3 (test3 int) inherits (atacc1, atacc2); -alter table atacc3 no inherit atacc2; --- fail -alter table atacc3 no inherit atacc2; -ERROR: relation "atacc2" is not a parent of relation "atacc3" --- make sure it really isn't a child -insert into atacc3 (test2) values (3); -select test2 from atacc2; - test2 -------- -(0 rows) - --- fail due to missing constraint -alter table atacc2 add constraint foo check (test2>0); -alter table atacc3 inherit atacc2; -ERROR: child table is missing constraint "foo" --- fail due to missing column -alter table atacc3 rename test2 to testx; -alter table atacc3 inherit atacc2; -ERROR: child table is missing column "test2" --- fail due to mismatched data type -alter table atacc3 add test2 bool; -alter table atacc3 inherit atacc2; -ERROR: child table "atacc3" has different type for column "test2" -alter table atacc3 drop test2; --- succeed -alter table atacc3 add test2 int; -update atacc3 set test2 = 4 where test2 is null; -alter table atacc3 add constraint foo check (test2>0); -alter table atacc3 inherit atacc2; --- fail due to duplicates and circular inheritance -alter table atacc3 inherit atacc2; -ERROR: relation "atacc2" would be inherited from more than once -alter table atacc2 inherit atacc3; -ERROR: circular inheritance not allowed -DETAIL: "atacc3" is already a child of "atacc2". -alter table atacc2 inherit atacc2; -ERROR: circular inheritance not allowed -DETAIL: "atacc2" is already a child of "atacc2". --- test that we really are a child now (should see 4 not 3 and cascade should go through) -select test2 from atacc2; - test2 -------- - 4 -(1 row) - -drop table atacc2 cascade; -NOTICE: drop cascades to table atacc3 -drop table atacc1; --- adding only to a parent is allowed as of 9.2 -create table atacc1 (test int); -create table atacc2 (test2 int) inherits (atacc1); --- ok: -alter table atacc1 add constraint foo check (test>0) no inherit; --- check constraint is not there on child -insert into atacc2 (test) values (-3); --- check constraint is there on parent -insert into atacc1 (test) values (-3); -ERROR: new row for relation "atacc1" violates check constraint "foo" -DETAIL: Failing row contains (-3). -insert into atacc1 (test) values (3); --- fail, violating row: -alter table atacc2 add constraint foo check (test>0) no inherit; -ERROR: check constraint "foo" of relation "atacc2" is violated by some row -drop table atacc2; -drop table atacc1; --- test unique constraint adding -create table atacc1 ( test int ) ; --- add a unique constraint -alter table atacc1 add constraint atacc_test1 unique (test); --- insert first value -insert into atacc1 (test) values (2); --- should fail -insert into atacc1 (test) values (2); -ERROR: duplicate key value violates unique constraint "atacc_test1" -DETAIL: Key (test)=(2) already exists. --- should succeed -insert into atacc1 (test) values (4); --- try to create duplicates via alter table using - should fail -alter table atacc1 alter column test type integer using 0; -ERROR: could not create unique index "atacc_test1" -DETAIL: Key (test)=(0) is duplicated. -drop table atacc1; --- let's do one where the unique constraint fails when added -create table atacc1 ( test int ); --- insert soon to be failing rows -insert into atacc1 (test) values (2); -insert into atacc1 (test) values (2); --- add a unique constraint (fails) -alter table atacc1 add constraint atacc_test1 unique (test); -ERROR: could not create unique index "atacc_test1" -DETAIL: Key (test)=(2) is duplicated. -insert into atacc1 (test) values (3); -drop table atacc1; --- let's do one where the unique constraint fails --- because the column doesn't exist -create table atacc1 ( test int ); --- add a unique constraint (fails) -alter table atacc1 add constraint atacc_test1 unique (test1); -ERROR: column "test1" named in key does not exist -drop table atacc1; --- something a little more complicated -create table atacc1 ( test int, test2 int); --- add a unique constraint -alter table atacc1 add constraint atacc_test1 unique (test, test2); --- insert initial value -insert into atacc1 (test,test2) values (4,4); --- should fail -insert into atacc1 (test,test2) values (4,4); -ERROR: duplicate key value violates unique constraint "atacc_test1" -DETAIL: Key (test, test2)=(4, 4) already exists. --- should all succeed -insert into atacc1 (test,test2) values (4,5); -insert into atacc1 (test,test2) values (5,4); -insert into atacc1 (test,test2) values (5,5); -drop table atacc1; --- lets do some naming tests -create table atacc1 (test int, test2 int, unique(test)); -alter table atacc1 add unique (test2); --- should fail for @@ second one @@ -insert into atacc1 (test2, test) values (3, 3); -insert into atacc1 (test2, test) values (2, 3); -ERROR: duplicate key value violates unique constraint "atacc1_test_key" -DETAIL: Key (test)=(3) already exists. -drop table atacc1; --- test primary key constraint adding -create table atacc1 ( id serial, test int) ; --- add a primary key constraint -alter table atacc1 add constraint atacc_test1 primary key (test); --- insert first value -insert into atacc1 (test) values (2); --- should fail -insert into atacc1 (test) values (2); -ERROR: duplicate key value violates unique constraint "atacc_test1" -DETAIL: Key (test)=(2) already exists. --- should succeed -insert into atacc1 (test) values (4); --- inserting NULL should fail -insert into atacc1 (test) values(NULL); -ERROR: null value in column "test" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (4, null). --- try adding a second primary key (should fail) -alter table atacc1 add constraint atacc_oid1 primary key(id); -ERROR: multiple primary keys for table "atacc1" are not allowed --- drop first primary key constraint -alter table atacc1 drop constraint atacc_test1 restrict; --- try adding a primary key on oid (should succeed) -alter table atacc1 add constraint atacc_oid1 primary key(id); -drop table atacc1; --- let's do one where the primary key constraint fails when added -create table atacc1 ( test int ); --- insert soon to be failing rows -insert into atacc1 (test) values (2); -insert into atacc1 (test) values (2); --- add a primary key (fails) -alter table atacc1 add constraint atacc_test1 primary key (test); -ERROR: could not create unique index "atacc_test1" -DETAIL: Key (test)=(2) is duplicated. -insert into atacc1 (test) values (3); -drop table atacc1; --- let's do another one where the primary key constraint fails when added -create table atacc1 ( test int ); --- insert soon to be failing row -insert into atacc1 (test) values (NULL); --- add a primary key (fails) -alter table atacc1 add constraint atacc_test1 primary key (test); -ERROR: column "test" of relation "atacc1" contains null values -insert into atacc1 (test) values (3); -drop table atacc1; --- let's do one where the primary key constraint fails --- because the column doesn't exist -create table atacc1 ( test int ); --- add a primary key constraint (fails) -alter table atacc1 add constraint atacc_test1 primary key (test1); -ERROR: column "test1" of relation "atacc1" does not exist -drop table atacc1; --- adding a new column as primary key to a non-empty table. --- should fail unless the column has a non-null default value. -create table atacc1 ( test int ); -insert into atacc1 (test) values (0); --- add a primary key column without a default (fails). -alter table atacc1 add column test2 int primary key; -ERROR: column "test2" of relation "atacc1" contains null values --- now add a primary key column with a default (succeeds). -alter table atacc1 add column test2 int default 0 primary key; -drop table atacc1; --- this combination used to have order-of-execution problems (bug #15580) -create table atacc1 (a int); -insert into atacc1 values(1); -alter table atacc1 - add column b float8 not null default random(), - add primary key(a); -drop table atacc1; --- additionally, we've seen issues with foreign key validation not being --- properly delayed until after a table rewrite. Check that works ok. -create table atacc1 (a int primary key); -alter table atacc1 add constraint atacc1_fkey foreign key (a) references atacc1 (a) not valid; -alter table atacc1 validate constraint atacc1_fkey, alter a type bigint; -drop table atacc1; --- we've also seen issues with check constraints being validated at the wrong --- time when there's a pending table rewrite. -create table atacc1 (a bigint, b int); -insert into atacc1 values(1,1); -alter table atacc1 add constraint atacc1_chk check(b = 1) not valid; -alter table atacc1 validate constraint atacc1_chk, alter a type int; -drop table atacc1; --- same as above, but ensure the constraint violation is detected -create table atacc1 (a bigint, b int); -insert into atacc1 values(1,2); -alter table atacc1 add constraint atacc1_chk check(b = 1) not valid; -alter table atacc1 validate constraint atacc1_chk, alter a type int; -ERROR: check constraint "atacc1_chk" of relation "atacc1" is violated by some row -drop table atacc1; --- something a little more complicated -create table atacc1 ( test int, test2 int); --- add a primary key constraint -alter table atacc1 add constraint atacc_test1 primary key (test, test2); --- try adding a second primary key - should fail -alter table atacc1 add constraint atacc_test2 primary key (test); -ERROR: multiple primary keys for table "atacc1" are not allowed --- insert initial value -insert into atacc1 (test,test2) values (4,4); --- should fail -insert into atacc1 (test,test2) values (4,4); -ERROR: duplicate key value violates unique constraint "atacc_test1" -DETAIL: Key (test, test2)=(4, 4) already exists. -insert into atacc1 (test,test2) values (NULL,3); -ERROR: null value in column "test" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (null, 3). -insert into atacc1 (test,test2) values (3, NULL); -ERROR: null value in column "test2" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (3, null). -insert into atacc1 (test,test2) values (NULL,NULL); -ERROR: null value in column "test" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (null, null). --- should all succeed -insert into atacc1 (test,test2) values (4,5); -insert into atacc1 (test,test2) values (5,4); -insert into atacc1 (test,test2) values (5,5); -drop table atacc1; --- lets do some naming tests -create table atacc1 (test int, test2 int, primary key(test)); --- only first should succeed -insert into atacc1 (test2, test) values (3, 3); -insert into atacc1 (test2, test) values (2, 3); -ERROR: duplicate key value violates unique constraint "atacc1_pkey" -DETAIL: Key (test)=(3) already exists. -insert into atacc1 (test2, test) values (1, NULL); -ERROR: null value in column "test" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (null, 1). -drop table atacc1; --- alter table / alter column [set/drop] not null tests --- try altering system catalogs, should fail -alter table pg_class alter column relname drop not null; -ERROR: permission denied: "pg_class" is a system catalog -alter table pg_class alter relname set not null; -ERROR: permission denied: "pg_class" is a system catalog --- try altering non-existent table, should fail -alter table non_existent alter column bar set not null; -ERROR: relation "non_existent" does not exist -alter table non_existent alter column bar drop not null; -ERROR: relation "non_existent" does not exist --- test setting columns to null and not null and vice versa --- test checking for null values and primary key -create table atacc1 (test int not null); -alter table atacc1 add constraint "atacc1_pkey" primary key (test); -alter table atacc1 alter column test drop not null; -ERROR: column "test" is in a primary key -alter table atacc1 drop constraint "atacc1_pkey"; -alter table atacc1 alter column test drop not null; -insert into atacc1 values (null); -alter table atacc1 alter test set not null; -ERROR: column "test" of relation "atacc1" contains null values -delete from atacc1; -alter table atacc1 alter test set not null; --- try altering a non-existent column, should fail -alter table atacc1 alter bar set not null; -ERROR: column "bar" of relation "atacc1" does not exist -alter table atacc1 alter bar drop not null; -ERROR: column "bar" of relation "atacc1" does not exist --- try creating a view and altering that, should fail -create view myview as select * from atacc1; -alter table myview alter column test drop not null; -ERROR: ALTER action ALTER COLUMN ... DROP NOT NULL cannot be performed on relation "myview" -DETAIL: This operation is not supported for views. -alter table myview alter column test set not null; -ERROR: ALTER action ALTER COLUMN ... SET NOT NULL cannot be performed on relation "myview" -DETAIL: This operation is not supported for views. -drop view myview; -drop table atacc1; --- set not null verified by constraints -create table atacc1 (test_a int, test_b int); -insert into atacc1 values (null, 1); --- constraint not cover all values, should fail -alter table atacc1 add constraint atacc1_constr_or check(test_a is not null or test_b < 10); -alter table atacc1 alter test_a set not null; -ERROR: column "test_a" of relation "atacc1" contains null values -alter table atacc1 drop constraint atacc1_constr_or; --- not valid constraint, should fail -alter table atacc1 add constraint atacc1_constr_invalid check(test_a is not null) not valid; -alter table atacc1 alter test_a set not null; -ERROR: column "test_a" of relation "atacc1" contains null values -alter table atacc1 drop constraint atacc1_constr_invalid; --- with valid constraint -update atacc1 set test_a = 1; -alter table atacc1 add constraint atacc1_constr_a_valid check(test_a is not null); -alter table atacc1 alter test_a set not null; -delete from atacc1; -insert into atacc1 values (2, null); -alter table atacc1 alter test_a drop not null; --- test multiple set not null at same time --- test_a checked by atacc1_constr_a_valid, test_b should fail by table scan -alter table atacc1 alter test_a set not null, alter test_b set not null; -ERROR: column "test_b" of relation "atacc1" contains null values --- commands order has no importance -alter table atacc1 alter test_b set not null, alter test_a set not null; -ERROR: column "test_b" of relation "atacc1" contains null values --- valid one by table scan, one by check constraints -update atacc1 set test_b = 1; -alter table atacc1 alter test_b set not null, alter test_a set not null; -alter table atacc1 alter test_a drop not null, alter test_b drop not null; --- both column has check constraints -alter table atacc1 add constraint atacc1_constr_b_valid check(test_b is not null); -alter table atacc1 alter test_b set not null, alter test_a set not null; -drop table atacc1; --- test inheritance -create table parent (a int); -create table child (b varchar(255)) inherits (parent); -alter table parent alter a set not null; -insert into parent values (NULL); -ERROR: null value in column "a" of relation "parent" violates not-null constraint -DETAIL: Failing row contains (null). -insert into child (a, b) values (NULL, 'foo'); -ERROR: null value in column "a" of relation "child" violates not-null constraint -DETAIL: Failing row contains (null, foo). -alter table parent alter a drop not null; -insert into parent values (NULL); -insert into child (a, b) values (NULL, 'foo'); -alter table only parent alter a set not null; -ERROR: column "a" of relation "parent" contains null values -alter table child alter a set not null; -ERROR: column "a" of relation "child" contains null values -delete from parent; -alter table only parent alter a set not null; -insert into parent values (NULL); -ERROR: null value in column "a" of relation "parent" violates not-null constraint -DETAIL: Failing row contains (null). -alter table child alter a set not null; -insert into child (a, b) values (NULL, 'foo'); -ERROR: null value in column "a" of relation "child" violates not-null constraint -DETAIL: Failing row contains (null, foo). -delete from child; -alter table child alter a set not null; -insert into child (a, b) values (NULL, 'foo'); -ERROR: null value in column "a" of relation "child" violates not-null constraint -DETAIL: Failing row contains (null, foo). -drop table child; -drop table parent; --- test setting and removing default values -create table def_test ( - c1 int4 default 5, - c2 text default 'initial_default' -); -insert into def_test default values; -alter table def_test alter column c1 drop default; -insert into def_test default values; -alter table def_test alter column c2 drop default; -insert into def_test default values; -alter table def_test alter column c1 set default 10; -alter table def_test alter column c2 set default 'new_default'; -insert into def_test default values; -select * from def_test; - c1 | c2 -----+----------------- - 5 | initial_default - | initial_default - | - 10 | new_default -(4 rows) - --- set defaults to an incorrect type: this should fail -alter table def_test alter column c1 set default 'wrong_datatype'; -ERROR: invalid input syntax for type integer: "wrong_datatype" -alter table def_test alter column c2 set default 20; --- set defaults on a non-existent column: this should fail -alter table def_test alter column c3 set default 30; -ERROR: column "c3" of relation "def_test" does not exist --- set defaults on views: we need to create a view, add a rule --- to allow insertions into it, and then alter the view to add --- a default -create view def_view_test as select * from def_test; -create rule def_view_test_ins as - on insert to def_view_test - do instead insert into def_test select new.*; -insert into def_view_test default values; -alter table def_view_test alter column c1 set default 45; -insert into def_view_test default values; -alter table def_view_test alter column c2 set default 'view_default'; -insert into def_view_test default values; -select * from def_view_test; - c1 | c2 -----+----------------- - 5 | initial_default - | initial_default - | - 10 | new_default - | - 45 | - 45 | view_default -(7 rows) - -drop rule def_view_test_ins on def_view_test; -drop view def_view_test; -drop table def_test; --- alter table / drop column tests --- try altering system catalogs, should fail -alter table pg_class drop column relname; -ERROR: permission denied: "pg_class" is a system catalog --- try altering non-existent table, should fail -alter table nosuchtable drop column bar; -ERROR: relation "nosuchtable" does not exist --- test dropping columns -create table atacc1 (a int4 not null, b int4, c int4 not null, d int4); -insert into atacc1 values (1, 2, 3, 4); -alter table atacc1 drop a; -alter table atacc1 drop a; -ERROR: column "a" of relation "atacc1" does not exist --- SELECTs -select * from atacc1; - b | c | d ----+---+--- - 2 | 3 | 4 -(1 row) - -select * from atacc1 order by a; -ERROR: column "a" does not exist -LINE 1: select * from atacc1 order by a; - ^ -select * from atacc1 order by "........pg.dropped.1........"; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: select * from atacc1 order by "........pg.dropped.1........"... - ^ -select * from atacc1 group by a; -ERROR: column "a" does not exist -LINE 1: select * from atacc1 group by a; - ^ -select * from atacc1 group by "........pg.dropped.1........"; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: select * from atacc1 group by "........pg.dropped.1........"... - ^ -select atacc1.* from atacc1; - b | c | d ----+---+--- - 2 | 3 | 4 -(1 row) - -select a from atacc1; -ERROR: column "a" does not exist -LINE 1: select a from atacc1; - ^ -select atacc1.a from atacc1; -ERROR: column atacc1.a does not exist -LINE 1: select atacc1.a from atacc1; - ^ -select b,c,d from atacc1; - b | c | d ----+---+--- - 2 | 3 | 4 -(1 row) - -select a,b,c,d from atacc1; -ERROR: column "a" does not exist -LINE 1: select a,b,c,d from atacc1; - ^ -select * from atacc1 where a = 1; -ERROR: column "a" does not exist -LINE 1: select * from atacc1 where a = 1; - ^ -select "........pg.dropped.1........" from atacc1; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: select "........pg.dropped.1........" from atacc1; - ^ -select atacc1."........pg.dropped.1........" from atacc1; -ERROR: column atacc1.........pg.dropped.1........ does not exist -LINE 1: select atacc1."........pg.dropped.1........" from atacc1; - ^ -select "........pg.dropped.1........",b,c,d from atacc1; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: select "........pg.dropped.1........",b,c,d from atacc1; - ^ -select * from atacc1 where "........pg.dropped.1........" = 1; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: select * from atacc1 where "........pg.dropped.1........" = ... - ^ --- UPDATEs -update atacc1 set a = 3; -ERROR: column "a" of relation "atacc1" does not exist -LINE 1: update atacc1 set a = 3; - ^ -update atacc1 set b = 2 where a = 3; -ERROR: column "a" does not exist -LINE 1: update atacc1 set b = 2 where a = 3; - ^ -update atacc1 set "........pg.dropped.1........" = 3; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -LINE 1: update atacc1 set "........pg.dropped.1........" = 3; - ^ -update atacc1 set b = 2 where "........pg.dropped.1........" = 3; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: update atacc1 set b = 2 where "........pg.dropped.1........"... - ^ --- INSERTs -insert into atacc1 values (10, 11, 12, 13); -ERROR: INSERT has more expressions than target columns -LINE 1: insert into atacc1 values (10, 11, 12, 13); - ^ -insert into atacc1 values (default, 11, 12, 13); -ERROR: INSERT has more expressions than target columns -LINE 1: insert into atacc1 values (default, 11, 12, 13); - ^ -insert into atacc1 values (11, 12, 13); -insert into atacc1 (a) values (10); -ERROR: column "a" of relation "atacc1" does not exist -LINE 1: insert into atacc1 (a) values (10); - ^ -insert into atacc1 (a) values (default); -ERROR: column "a" of relation "atacc1" does not exist -LINE 1: insert into atacc1 (a) values (default); - ^ -insert into atacc1 (a,b,c,d) values (10,11,12,13); -ERROR: column "a" of relation "atacc1" does not exist -LINE 1: insert into atacc1 (a,b,c,d) values (10,11,12,13); - ^ -insert into atacc1 (a,b,c,d) values (default,11,12,13); -ERROR: column "a" of relation "atacc1" does not exist -LINE 1: insert into atacc1 (a,b,c,d) values (default,11,12,13); - ^ -insert into atacc1 (b,c,d) values (11,12,13); -insert into atacc1 ("........pg.dropped.1........") values (10); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -LINE 1: insert into atacc1 ("........pg.dropped.1........") values (... - ^ -insert into atacc1 ("........pg.dropped.1........") values (default); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -LINE 1: insert into atacc1 ("........pg.dropped.1........") values (... - ^ -insert into atacc1 ("........pg.dropped.1........",b,c,d) values (10,11,12,13); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -LINE 1: insert into atacc1 ("........pg.dropped.1........",b,c,d) va... - ^ -insert into atacc1 ("........pg.dropped.1........",b,c,d) values (default,11,12,13); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -LINE 1: insert into atacc1 ("........pg.dropped.1........",b,c,d) va... - ^ --- DELETEs -delete from atacc1 where a = 3; -ERROR: column "a" does not exist -LINE 1: delete from atacc1 where a = 3; - ^ -delete from atacc1 where "........pg.dropped.1........" = 3; -ERROR: column "........pg.dropped.1........" does not exist -LINE 1: delete from atacc1 where "........pg.dropped.1........" = 3; - ^ -delete from atacc1; --- try dropping a non-existent column, should fail -alter table atacc1 drop bar; -ERROR: column "bar" of relation "atacc1" does not exist --- try removing an oid column, should succeed (as it's nonexistent) -alter table atacc1 SET WITHOUT OIDS; --- try adding an oid column, should fail (not supported) -alter table atacc1 SET WITH OIDS; -ERROR: syntax error at or near "WITH" -LINE 1: alter table atacc1 SET WITH OIDS; - ^ --- try dropping the xmin column, should fail -alter table atacc1 drop xmin; -ERROR: cannot drop system column "xmin" --- try creating a view and altering that, should fail -create view myview as select * from atacc1; -select * from myview; - b | c | d ----+---+--- -(0 rows) - -alter table myview drop d; -ERROR: ALTER action DROP COLUMN cannot be performed on relation "myview" -DETAIL: This operation is not supported for views. -drop view myview; --- test some commands to make sure they fail on the dropped column -analyze atacc1(a); -ERROR: column "a" of relation "atacc1" does not exist -analyze atacc1("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -vacuum analyze atacc1(a); -ERROR: column "a" of relation "atacc1" does not exist -vacuum analyze atacc1("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -comment on column atacc1.a is 'testing'; -ERROR: column "a" of relation "atacc1" does not exist -comment on column atacc1."........pg.dropped.1........" is 'testing'; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a set storage plain; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" set storage plain; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a set statistics 0; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" set statistics 0; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a set default 3; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" set default 3; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a drop default; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" drop default; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a set not null; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" set not null; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 alter a drop not null; -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 alter "........pg.dropped.1........" drop not null; -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 rename a to x; -ERROR: column "a" does not exist -alter table atacc1 rename "........pg.dropped.1........" to x; -ERROR: column "........pg.dropped.1........" does not exist -alter table atacc1 add primary key(a); -ERROR: column "a" of relation "atacc1" does not exist -alter table atacc1 add primary key("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" of relation "atacc1" does not exist -alter table atacc1 add unique(a); -ERROR: column "a" named in key does not exist -alter table atacc1 add unique("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" named in key does not exist -alter table atacc1 add check (a > 3); -ERROR: column "a" does not exist -alter table atacc1 add check ("........pg.dropped.1........" > 3); -ERROR: column "........pg.dropped.1........" does not exist -create table atacc2 (id int4 unique); -alter table atacc1 add foreign key (a) references atacc2(id); -ERROR: column "a" referenced in foreign key constraint does not exist -alter table atacc1 add foreign key ("........pg.dropped.1........") references atacc2(id); -ERROR: column "........pg.dropped.1........" referenced in foreign key constraint does not exist -alter table atacc2 add foreign key (id) references atacc1(a); -ERROR: column "a" referenced in foreign key constraint does not exist -alter table atacc2 add foreign key (id) references atacc1("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" referenced in foreign key constraint does not exist -drop table atacc2; -create index "testing_idx" on atacc1(a); -ERROR: column "a" does not exist -create index "testing_idx" on atacc1("........pg.dropped.1........"); -ERROR: column "........pg.dropped.1........" does not exist --- test create as and select into -insert into atacc1 values (21, 22, 23); -create table attest1 as select * from atacc1; -select * from attest1; - b | c | d -----+----+---- - 21 | 22 | 23 -(1 row) - -drop table attest1; -select * into attest2 from atacc1; -select * from attest2; - b | c | d -----+----+---- - 21 | 22 | 23 -(1 row) - -drop table attest2; --- try dropping all columns -alter table atacc1 drop c; -alter table atacc1 drop d; -alter table atacc1 drop b; -select * from atacc1; --- -(1 row) - -drop table atacc1; --- test constraint error reporting in presence of dropped columns -create table atacc1 (id serial primary key, value int check (value < 10)); -insert into atacc1(value) values (100); -ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check" -DETAIL: Failing row contains (1, 100). -alter table atacc1 drop column value; -alter table atacc1 add column value int check (value < 10); -insert into atacc1(value) values (100); -ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check" -DETAIL: Failing row contains (2, 100). -insert into atacc1(id, value) values (null, 0); -ERROR: null value in column "id" of relation "atacc1" violates not-null constraint -DETAIL: Failing row contains (null, 0). -drop table atacc1; --- test inheritance -create table parent (a int, b int, c int); -insert into parent values (1, 2, 3); -alter table parent drop a; -create table child (d varchar(255)) inherits (parent); -insert into child values (12, 13, 'testing'); -select * from parent; - b | c -----+---- - 2 | 3 - 12 | 13 -(2 rows) - -select * from child; - b | c | d -----+----+--------- - 12 | 13 | testing -(1 row) - -alter table parent drop c; -select * from parent; - b ----- - 2 - 12 -(2 rows) - -select * from child; - b | d -----+--------- - 12 | testing -(1 row) - -drop table child; -drop table parent; --- check error cases for inheritance column merging -create table parent (a float8, b numeric(10,4), c text collate "C"); -create table child (a float4) inherits (parent); -- fail -NOTICE: merging column "a" with inherited definition -ERROR: column "a" has a type conflict -DETAIL: double precision versus real -create table child (b decimal(10,7)) inherits (parent); -- fail -NOTICE: moving and merging column "b" with inherited definition -DETAIL: User-specified column moved to the position of the inherited column. -ERROR: column "b" has a type conflict -DETAIL: numeric(10,4) versus numeric(10,7) -create table child (c text collate "POSIX") inherits (parent); -- fail -NOTICE: moving and merging column "c" with inherited definition -DETAIL: User-specified column moved to the position of the inherited column. -ERROR: column "c" has a collation conflict -DETAIL: "C" versus "POSIX" -create table child (a double precision, b decimal(10,4)) inherits (parent); -NOTICE: merging column "a" with inherited definition -NOTICE: merging column "b" with inherited definition -drop table child; -drop table parent; --- test copy in/out -create table attest (a int4, b int4, c int4); -insert into attest values (1,2,3); -alter table attest drop a; -copy attest to stdout; -2 3 -copy attest(a) to stdout; -ERROR: column "a" of relation "attest" does not exist -copy attest("........pg.dropped.1........") to stdout; -ERROR: column "........pg.dropped.1........" of relation "attest" does not exist -copy attest from stdin; -ERROR: extra data after last expected column -CONTEXT: COPY attest, line 1: "10 11 12" -select * from attest; - b | c ----+--- - 2 | 3 -(1 row) - -copy attest from stdin; -select * from attest; - b | c -----+---- - 2 | 3 - 21 | 22 -(2 rows) - -copy attest(a) from stdin; -ERROR: column "a" of relation "attest" does not exist -copy attest("........pg.dropped.1........") from stdin; -ERROR: column "........pg.dropped.1........" of relation "attest" does not exist -copy attest(b,c) from stdin; -select * from attest; - b | c -----+---- - 2 | 3 - 21 | 22 - 31 | 32 -(3 rows) - -drop table attest; --- test inheritance -create table dropColumn (a int, b int, e int); -create table dropColumnChild (c int) inherits (dropColumn); -create table dropColumnAnother (d int) inherits (dropColumnChild); --- these two should fail -alter table dropColumnchild drop column a; -ERROR: cannot drop inherited column "a" -alter table only dropColumnChild drop column b; -ERROR: cannot drop inherited column "b" --- these three should work -alter table only dropColumn drop column e; -alter table dropColumnChild drop column c; -alter table dropColumn drop column a; -create table renameColumn (a int); -create table renameColumnChild (b int) inherits (renameColumn); -create table renameColumnAnother (c int) inherits (renameColumnChild); --- these three should fail -alter table renameColumnChild rename column a to d; -ERROR: cannot rename inherited column "a" -alter table only renameColumnChild rename column a to d; -ERROR: inherited column "a" must be renamed in child tables too -alter table only renameColumn rename column a to d; -ERROR: inherited column "a" must be renamed in child tables too --- these should work -alter table renameColumn rename column a to d; -alter table renameColumnChild rename column b to a; --- these should work -alter table if exists doesnt_exist_tab rename column a to d; -NOTICE: relation "doesnt_exist_tab" does not exist, skipping -alter table if exists doesnt_exist_tab rename column b to a; -NOTICE: relation "doesnt_exist_tab" does not exist, skipping --- this should work -alter table renameColumn add column w int; --- this should fail -alter table only renameColumn add column x int; -ERROR: column must be added to child tables too --- Test corner cases in dropping of inherited columns -create table p1 (f1 int, f2 int); -create table c1 (f1 int not null) inherits(p1); -NOTICE: merging column "f1" with inherited definition --- should be rejected since c1.f1 is inherited -alter table c1 drop column f1; -ERROR: cannot drop inherited column "f1" --- should work -alter table p1 drop column f1; --- c1.f1 is still there, but no longer inherited -select f1 from c1; - f1 ----- -(0 rows) - -alter table c1 drop column f1; -select f1 from c1; -ERROR: column "f1" does not exist -LINE 1: select f1 from c1; - ^ -HINT: Perhaps you meant to reference the column "c1.f2". -drop table p1 cascade; -NOTICE: drop cascades to table c1 -create table p1 (f1 int, f2 int); -create table c1 () inherits(p1); --- should be rejected since c1.f1 is inherited -alter table c1 drop column f1; -ERROR: cannot drop inherited column "f1" -alter table p1 drop column f1; --- c1.f1 is dropped now, since there is no local definition for it -select f1 from c1; -ERROR: column "f1" does not exist -LINE 1: select f1 from c1; - ^ -HINT: Perhaps you meant to reference the column "c1.f2". -drop table p1 cascade; -NOTICE: drop cascades to table c1 -create table p1 (f1 int, f2 int); -create table c1 () inherits(p1); --- should be rejected since c1.f1 is inherited -alter table c1 drop column f1; -ERROR: cannot drop inherited column "f1" -alter table only p1 drop column f1; --- c1.f1 is NOT dropped, but must now be considered non-inherited -alter table c1 drop column f1; -drop table p1 cascade; -NOTICE: drop cascades to table c1 -create table p1 (f1 int, f2 int); -create table c1 (f1 int not null) inherits(p1); -NOTICE: merging column "f1" with inherited definition --- should be rejected since c1.f1 is inherited -alter table c1 drop column f1; -ERROR: cannot drop inherited column "f1" -alter table only p1 drop column f1; --- c1.f1 is still there, but no longer inherited -alter table c1 drop column f1; -drop table p1 cascade; -NOTICE: drop cascades to table c1 -create table p1(id int, name text); -create table p2(id2 int, name text, height int); -create table c1(age int) inherits(p1,p2); -NOTICE: merging multiple inherited definitions of column "name" -create table gc1() inherits (c1); -select relname, attname, attinhcount, attislocal -from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) -where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped -order by relname, attnum; - relname | attname | attinhcount | attislocal ----------+---------+-------------+------------ - c1 | id | 1 | f - c1 | name | 2 | f - c1 | id2 | 1 | f - c1 | height | 1 | f - c1 | age | 0 | t - gc1 | id | 1 | f - gc1 | name | 1 | f - gc1 | id2 | 1 | f - gc1 | height | 1 | f - gc1 | age | 1 | f - p1 | id | 0 | t - p1 | name | 0 | t - p2 | id2 | 0 | t - p2 | name | 0 | t - p2 | height | 0 | t -(15 rows) - --- should work -alter table only p1 drop column name; --- should work. Now c1.name is local and inhcount is 0. -alter table p2 drop column name; --- should be rejected since its inherited -alter table gc1 drop column name; -ERROR: cannot drop inherited column "name" --- should work, and drop gc1.name along -alter table c1 drop column name; --- should fail: column does not exist -alter table gc1 drop column name; -ERROR: column "name" of relation "gc1" does not exist --- should work and drop the attribute in all tables -alter table p2 drop column height; --- IF EXISTS test -create table dropColumnExists (); -alter table dropColumnExists drop column non_existing; --fail -ERROR: column "non_existing" of relation "dropcolumnexists" does not exist -alter table dropColumnExists drop column if exists non_existing; --succeed -NOTICE: column "non_existing" of relation "dropcolumnexists" does not exist, skipping -select relname, attname, attinhcount, attislocal -from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) -where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped -order by relname, attnum; - relname | attname | attinhcount | attislocal ----------+---------+-------------+------------ - c1 | id | 1 | f - c1 | id2 | 1 | f - c1 | age | 0 | t - gc1 | id | 1 | f - gc1 | id2 | 1 | f - gc1 | age | 1 | f - p1 | id | 0 | t - p2 | id2 | 0 | t -(8 rows) - -drop table p1, p2 cascade; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table c1 -drop cascades to table gc1 --- test attinhcount tracking with merged columns -create table depth0(); -create table depth1(c text) inherits (depth0); -create table depth2() inherits (depth1); -alter table depth0 add c text; -NOTICE: merging definition of column "c" for child "depth1" -select attrelid::regclass, attname, attinhcount, attislocal -from pg_attribute -where attnum > 0 and attrelid::regclass in ('depth0', 'depth1', 'depth2') -order by attrelid::regclass::text, attnum; - attrelid | attname | attinhcount | attislocal -----------+---------+-------------+------------ - depth0 | c | 0 | t - depth1 | c | 1 | t - depth2 | c | 1 | f -(3 rows) - --- test renumbering of child-table columns in inherited operations -create table p1 (f1 int); -create table c1 (f2 text, f3 int) inherits (p1); -alter table p1 add column a1 int check (a1 > 0); -alter table p1 add column f2 text; -NOTICE: merging definition of column "f2" for child "c1" -insert into p1 values (1,2,'abc'); -insert into c1 values(11,'xyz',33,0); -- should fail -ERROR: new row for relation "c1" violates check constraint "p1_a1_check" -DETAIL: Failing row contains (11, xyz, 33, 0). -insert into c1 values(11,'xyz',33,22); -select * from p1; - f1 | a1 | f2 -----+----+----- - 1 | 2 | abc - 11 | 22 | xyz -(2 rows) - -update p1 set a1 = a1 + 1, f2 = upper(f2); -select * from p1; - f1 | a1 | f2 -----+----+----- - 1 | 3 | ABC - 11 | 23 | XYZ -(2 rows) - -drop table p1 cascade; -NOTICE: drop cascades to table c1 --- test that operations with a dropped column do not try to reference --- its datatype -create domain mytype as text; -create temp table foo (f1 text, f2 mytype, f3 text); -insert into foo values('bb','cc','dd'); -select * from foo; - f1 | f2 | f3 -----+----+---- - bb | cc | dd -(1 row) - -drop domain mytype cascade; -NOTICE: drop cascades to column f2 of table foo -select * from foo; - f1 | f3 -----+---- - bb | dd -(1 row) - -insert into foo values('qq','rr'); -select * from foo; - f1 | f3 -----+---- - bb | dd - qq | rr -(2 rows) - -update foo set f3 = 'zz'; -select * from foo; - f1 | f3 -----+---- - bb | zz - qq | zz -(2 rows) - -select f3,max(f1) from foo group by f3; - f3 | max -----+----- - zz | qq -(1 row) - --- Simple tests for alter table column type -alter table foo alter f1 TYPE integer; -- fails -ERROR: column "f1" cannot be cast automatically to type integer -HINT: You might need to specify "USING f1::integer". -alter table foo alter f1 TYPE varchar(10); -create table anothertab (atcol1 serial8, atcol2 boolean, - constraint anothertab_chk check (atcol1 <= 3)); -insert into anothertab (atcol1, atcol2) values (default, true); -insert into anothertab (atcol1, atcol2) values (default, false); -select * from anothertab; - atcol1 | atcol2 ---------+-------- - 1 | t - 2 | f -(2 rows) - -alter table anothertab alter column atcol1 type boolean; -- fails -ERROR: column "atcol1" cannot be cast automatically to type boolean -HINT: You might need to specify "USING atcol1::boolean". -alter table anothertab alter column atcol1 type boolean using atcol1::int; -- fails -ERROR: result of USING clause for column "atcol1" cannot be cast automatically to type boolean -HINT: You might need to add an explicit cast. -alter table anothertab alter column atcol1 type integer; -select * from anothertab; - atcol1 | atcol2 ---------+-------- - 1 | t - 2 | f -(2 rows) - -insert into anothertab (atcol1, atcol2) values (45, null); -- fails -ERROR: new row for relation "anothertab" violates check constraint "anothertab_chk" -DETAIL: Failing row contains (45, null). -insert into anothertab (atcol1, atcol2) values (default, null); -select * from anothertab; - atcol1 | atcol2 ---------+-------- - 1 | t - 2 | f - 3 | -(3 rows) - -alter table anothertab alter column atcol2 type text - using case when atcol2 is true then 'IT WAS TRUE' - when atcol2 is false then 'IT WAS FALSE' - else 'IT WAS NULL!' end; -select * from anothertab; - atcol1 | atcol2 ---------+-------------- - 1 | IT WAS TRUE - 2 | IT WAS FALSE - 3 | IT WAS NULL! -(3 rows) - -alter table anothertab alter column atcol1 type boolean - using case when atcol1 % 2 = 0 then true else false end; -- fails -ERROR: default for column "atcol1" cannot be cast automatically to type boolean -alter table anothertab alter column atcol1 drop default; -alter table anothertab alter column atcol1 type boolean - using case when atcol1 % 2 = 0 then true else false end; -- fails -ERROR: operator does not exist: boolean <= integer -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -alter table anothertab drop constraint anothertab_chk; -alter table anothertab drop constraint anothertab_chk; -- fails -ERROR: constraint "anothertab_chk" of relation "anothertab" does not exist -alter table anothertab drop constraint IF EXISTS anothertab_chk; -- succeeds -NOTICE: constraint "anothertab_chk" of relation "anothertab" does not exist, skipping -alter table anothertab alter column atcol1 type boolean - using case when atcol1 % 2 = 0 then true else false end; -select * from anothertab; - atcol1 | atcol2 ---------+-------------- - f | IT WAS TRUE - t | IT WAS FALSE - f | IT WAS NULL! -(3 rows) - -drop table anothertab; --- Test index handling in alter table column type (cf. bugs #15835, #15865) -create table anothertab(f1 int primary key, f2 int unique, - f3 int, f4 int, f5 int); -alter table anothertab - add exclude using btree (f3 with =); -alter table anothertab - add exclude using btree (f4 with =) where (f4 is not null); -alter table anothertab - add exclude using btree (f4 with =) where (f5 > 0); -alter table anothertab - add unique(f1,f4); -create index on anothertab(f2,f3); -create unique index on anothertab(f4); -\d anothertab - Table "public.anothertab" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - f1 | integer | | not null | - f2 | integer | | | - f3 | integer | | | - f4 | integer | | | - f5 | integer | | | -Indexes: - "anothertab_pkey" PRIMARY KEY, btree (f1) - "anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4) - "anothertab_f2_f3_idx" btree (f2, f3) - "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) - "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) - "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) - "anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0) - "anothertab_f4_idx" UNIQUE, btree (f4) - -alter table anothertab alter column f1 type bigint; -alter table anothertab - alter column f2 type bigint, - alter column f3 type bigint, - alter column f4 type bigint; -alter table anothertab alter column f5 type bigint; -\d anothertab - Table "public.anothertab" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+--------- - f1 | bigint | | not null | - f2 | bigint | | | - f3 | bigint | | | - f4 | bigint | | | - f5 | bigint | | | -Indexes: - "anothertab_pkey" PRIMARY KEY, btree (f1) - "anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4) - "anothertab_f2_f3_idx" btree (f2, f3) - "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) - "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) - "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) - "anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0) - "anothertab_f4_idx" UNIQUE, btree (f4) - -drop table anothertab; --- test that USING expressions are parsed before column alter type / drop steps -create table another (f1 int, f2 text, f3 text); -insert into another values(1, 'one', 'uno'); -insert into another values(2, 'two', 'due'); -insert into another values(3, 'three', 'tre'); -select * from another; - f1 | f2 | f3 -----+-------+----- - 1 | one | uno - 2 | two | due - 3 | three | tre -(3 rows) - -alter table another - alter f1 type text using f2 || ' and ' || f3 || ' more', - alter f2 type bigint using f1 * 10, - drop column f3; -select * from another; - f1 | f2 ---------------------+---- - one and uno more | 10 - two and due more | 20 - three and tre more | 30 -(3 rows) - -drop table another; --- Create an index that skips WAL, then perform a SET DATA TYPE that skips --- rewriting the index. -begin; -create table skip_wal_skip_rewrite_index (c varchar(10) primary key); -alter table skip_wal_skip_rewrite_index alter c type varchar(20); -commit; --- table's row type -create table tab1 (a int, b text); -create table tab2 (x int, y tab1); -alter table tab1 alter column b type varchar; -- fails -ERROR: cannot alter table "tab1" because column "tab2.y" uses its row type --- Alter column type that's part of a partitioned index -create table at_partitioned (a int, b text) partition by range (a); -create table at_part_1 partition of at_partitioned for values from (0) to (1000); -insert into at_partitioned values (512, '0.123'); -create table at_part_2 (b text, a int); -insert into at_part_2 values ('1.234', 1024); -create index on at_partitioned (b); -create index on at_partitioned (a); -\d at_part_1 - Table "public.at_part_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | -Partition of: at_partitioned FOR VALUES FROM (0) TO (1000) -Indexes: - "at_part_1_a_idx" btree (a) - "at_part_1_b_idx" btree (b) - -\d at_part_2 - Table "public.at_part_2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | text | | | - a | integer | | | - -alter table at_partitioned attach partition at_part_2 for values from (1000) to (2000); -\d at_part_2 - Table "public.at_part_2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | text | | | - a | integer | | | -Partition of: at_partitioned FOR VALUES FROM (1000) TO (2000) -Indexes: - "at_part_2_a_idx" btree (a) - "at_part_2_b_idx" btree (b) - -alter table at_partitioned alter column b type numeric using b::numeric; -\d at_part_1 - Table "public.at_part_1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | numeric | | | -Partition of: at_partitioned FOR VALUES FROM (0) TO (1000) -Indexes: - "at_part_1_a_idx" btree (a) - "at_part_1_b_idx" btree (b) - -\d at_part_2 - Table "public.at_part_2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - b | numeric | | | - a | integer | | | -Partition of: at_partitioned FOR VALUES FROM (1000) TO (2000) -Indexes: - "at_part_2_a_idx" btree (a) - "at_part_2_b_idx" btree (b) - -drop table at_partitioned; --- Alter column type when no table rewrite is required --- Also check that comments are preserved -create table at_partitioned(id int, name varchar(64), unique (id, name)) - partition by hash(id); -comment on constraint at_partitioned_id_name_key on at_partitioned is 'parent constraint'; -comment on index at_partitioned_id_name_key is 'parent index'; -create table at_partitioned_0 partition of at_partitioned - for values with (modulus 2, remainder 0); -comment on constraint at_partitioned_0_id_name_key on at_partitioned_0 is 'child 0 constraint'; -comment on index at_partitioned_0_id_name_key is 'child 0 index'; -create table at_partitioned_1 partition of at_partitioned - for values with (modulus 2, remainder 1); -comment on constraint at_partitioned_1_id_name_key on at_partitioned_1 is 'child 1 constraint'; -comment on index at_partitioned_1_id_name_key is 'child 1 index'; -insert into at_partitioned values(1, 'foo'); -insert into at_partitioned values(3, 'bar'); -create temp table old_oids as - select relname, oid as oldoid, relfilenode as oldfilenode - from pg_class where relname like 'at_partitioned%'; -select relname, - c.oid = oldoid as orig_oid, - case relfilenode - when 0 then 'none' - when c.oid then 'own' - when oldfilenode then 'orig' - else 'OTHER' - end as storage, - obj_description(c.oid, 'pg_class') as desc - from pg_class c left join old_oids using (relname) - where relname like 'at_partitioned%' - order by relname; - relname | orig_oid | storage | desc -------------------------------+----------+---------+--------------- - at_partitioned | t | none | - at_partitioned_0 | t | own | - at_partitioned_0_id_name_key | t | own | child 0 index - at_partitioned_1 | t | own | - at_partitioned_1_id_name_key | t | own | child 1 index - at_partitioned_id_name_key | t | none | parent index -(6 rows) - -select conname, obj_description(oid, 'pg_constraint') as desc - from pg_constraint where conname like 'at_partitioned%' - order by conname; - conname | desc -------------------------------+-------------------- - at_partitioned_0_id_name_key | child 0 constraint - at_partitioned_1_id_name_key | child 1 constraint - at_partitioned_id_name_key | parent constraint -(3 rows) - -alter table at_partitioned alter column name type varchar(127); --- Note: these tests currently show the wrong behavior for comments :-( -select relname, - c.oid = oldoid as orig_oid, - case relfilenode - when 0 then 'none' - when c.oid then 'own' - when oldfilenode then 'orig' - else 'OTHER' - end as storage, - obj_description(c.oid, 'pg_class') as desc - from pg_class c left join old_oids using (relname) - where relname like 'at_partitioned%' - order by relname; - relname | orig_oid | storage | desc -------------------------------+----------+---------+-------------- - at_partitioned | t | none | - at_partitioned_0 | t | own | - at_partitioned_0_id_name_key | f | own | parent index - at_partitioned_1 | t | own | - at_partitioned_1_id_name_key | f | own | parent index - at_partitioned_id_name_key | f | none | parent index -(6 rows) - -select conname, obj_description(oid, 'pg_constraint') as desc - from pg_constraint where conname like 'at_partitioned%' - order by conname; - conname | desc -------------------------------+------------------- - at_partitioned_0_id_name_key | - at_partitioned_1_id_name_key | - at_partitioned_id_name_key | parent constraint -(3 rows) - --- Don't remove this DROP, it exposes bug #15672 -drop table at_partitioned; --- disallow recursive containment of row types -create temp table recur1 (f1 int); -alter table recur1 add column f2 recur1; -- fails -ERROR: composite type recur1 cannot be made a member of itself -alter table recur1 add column f2 recur1[]; -- fails -ERROR: composite type recur1 cannot be made a member of itself -create domain array_of_recur1 as recur1[]; -alter table recur1 add column f2 array_of_recur1; -- fails -ERROR: composite type recur1 cannot be made a member of itself -create temp table recur2 (f1 int, f2 recur1); -alter table recur1 add column f2 recur2; -- fails -ERROR: composite type recur1 cannot be made a member of itself -alter table recur1 add column f2 int; -alter table recur1 alter column f2 type recur2; -- fails -ERROR: composite type recur1 cannot be made a member of itself --- SET STORAGE may need to add a TOAST table -create table test_storage (a text); -alter table test_storage alter a set storage plain; -alter table test_storage add b int default 0; -- rewrite table to remove its TOAST table -alter table test_storage alter a set storage extended; -- re-add TOAST table -select reltoastrelid <> 0 as has_toast_table -from pg_class -where oid = 'test_storage'::regclass; - has_toast_table ------------------ - t -(1 row) - --- test that SET STORAGE propagates to index correctly -create index test_storage_idx on test_storage (b, a); -alter table test_storage alter column a set storage external; -\d+ test_storage - Table "public.test_storage" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+----------+--------------+------------- - a | text | | | | external | | - b | integer | | | 0 | plain | | -Indexes: - "test_storage_idx" btree (b, a) - -\d+ test_storage_idx - Index "public.test_storage_idx" - Column | Type | Key? | Definition | Storage | Stats target ---------+---------+------+------------+----------+-------------- - b | integer | yes | b | plain | - a | text | yes | a | external | -btree, for table "public.test_storage" - --- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779) -CREATE TABLE test_inh_check (a float check (a > 10.2), b float); -CREATE TABLE test_inh_check_child() INHERITS(test_inh_check); -\d test_inh_check - Table "public.test_inh_check" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | double precision | | | - b | double precision | | | -Check constraints: - "test_inh_check_a_check" CHECK (a > 10.2::double precision) -Number of child tables: 1 (Use \d+ to list them.) - -\d test_inh_check_child - Table "public.test_inh_check_child" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | double precision | | | - b | double precision | | | -Check constraints: - "test_inh_check_a_check" CHECK (a > 10.2::double precision) -Inherits: test_inh_check - -select relname, conname, coninhcount, conislocal, connoinherit - from pg_constraint c, pg_class r - where relname like 'test_inh_check%' and c.conrelid = r.oid - order by 1, 2; - relname | conname | coninhcount | conislocal | connoinherit -----------------------+------------------------+-------------+------------+-------------- - test_inh_check | test_inh_check_a_check | 0 | t | f - test_inh_check_child | test_inh_check_a_check | 1 | f | f -(2 rows) - -ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric; -\d test_inh_check - Table "public.test_inh_check" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | numeric | | | - b | double precision | | | -Check constraints: - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Number of child tables: 1 (Use \d+ to list them.) - -\d test_inh_check_child - Table "public.test_inh_check_child" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | numeric | | | - b | double precision | | | -Check constraints: - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Inherits: test_inh_check - -select relname, conname, coninhcount, conislocal, connoinherit - from pg_constraint c, pg_class r - where relname like 'test_inh_check%' and c.conrelid = r.oid - order by 1, 2; - relname | conname | coninhcount | conislocal | connoinherit -----------------------+------------------------+-------------+------------+-------------- - test_inh_check | test_inh_check_a_check | 0 | t | f - test_inh_check_child | test_inh_check_a_check | 1 | f | f -(2 rows) - --- also try noinherit, local, and local+inherited cases -ALTER TABLE test_inh_check ADD CONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT; -ALTER TABLE test_inh_check_child ADD CONSTRAINT blocal CHECK (b < 1000); -ALTER TABLE test_inh_check_child ADD CONSTRAINT bmerged CHECK (b > 1); -ALTER TABLE test_inh_check ADD CONSTRAINT bmerged CHECK (b > 1); -NOTICE: merging constraint "bmerged" with inherited definition -\d test_inh_check - Table "public.test_inh_check" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | numeric | | | - b | double precision | | | -Check constraints: - "bmerged" CHECK (b > 1::double precision) - "bnoinherit" CHECK (b > 100::double precision) NO INHERIT - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Number of child tables: 1 (Use \d+ to list them.) - -\d test_inh_check_child - Table "public.test_inh_check_child" - Column | Type | Collation | Nullable | Default ---------+------------------+-----------+----------+--------- - a | numeric | | | - b | double precision | | | -Check constraints: - "blocal" CHECK (b < 1000::double precision) - "bmerged" CHECK (b > 1::double precision) - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Inherits: test_inh_check - -select relname, conname, coninhcount, conislocal, connoinherit - from pg_constraint c, pg_class r - where relname like 'test_inh_check%' and c.conrelid = r.oid - order by 1, 2; - relname | conname | coninhcount | conislocal | connoinherit -----------------------+------------------------+-------------+------------+-------------- - test_inh_check | bmerged | 0 | t | f - test_inh_check | bnoinherit | 0 | t | t - test_inh_check | test_inh_check_a_check | 0 | t | f - test_inh_check_child | blocal | 0 | t | f - test_inh_check_child | bmerged | 1 | t | f - test_inh_check_child | test_inh_check_a_check | 1 | f | f -(6 rows) - -ALTER TABLE test_inh_check ALTER COLUMN b TYPE numeric; -NOTICE: merging constraint "bmerged" with inherited definition -\d test_inh_check - Table "public.test_inh_check" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | numeric | | | - b | numeric | | | -Check constraints: - "bmerged" CHECK (b::double precision > 1::double precision) - "bnoinherit" CHECK (b::double precision > 100::double precision) NO INHERIT - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Number of child tables: 1 (Use \d+ to list them.) - -\d test_inh_check_child - Table "public.test_inh_check_child" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | numeric | | | - b | numeric | | | -Check constraints: - "blocal" CHECK (b::double precision < 1000::double precision) - "bmerged" CHECK (b::double precision > 1::double precision) - "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision) -Inherits: test_inh_check - -select relname, conname, coninhcount, conislocal, connoinherit - from pg_constraint c, pg_class r - where relname like 'test_inh_check%' and c.conrelid = r.oid - order by 1, 2; - relname | conname | coninhcount | conislocal | connoinherit -----------------------+------------------------+-------------+------------+-------------- - test_inh_check | bmerged | 0 | t | f - test_inh_check | bnoinherit | 0 | t | t - test_inh_check | test_inh_check_a_check | 0 | t | f - test_inh_check_child | blocal | 0 | t | f - test_inh_check_child | bmerged | 1 | t | f - test_inh_check_child | test_inh_check_a_check | 1 | f | f -(6 rows) - --- ALTER COLUMN TYPE with different schema in children --- Bug at https://postgr.es/m/20170102225618.GA10071@telsasoft.com -CREATE TABLE test_type_diff (f1 int); -CREATE TABLE test_type_diff_c (extra smallint) INHERITS (test_type_diff); -ALTER TABLE test_type_diff ADD COLUMN f2 int; -INSERT INTO test_type_diff_c VALUES (1, 2, 3); -ALTER TABLE test_type_diff ALTER COLUMN f2 TYPE bigint USING f2::bigint; -CREATE TABLE test_type_diff2 (int_two int2, int_four int4, int_eight int8); -CREATE TABLE test_type_diff2_c1 (int_four int4, int_eight int8, int_two int2); -CREATE TABLE test_type_diff2_c2 (int_eight int8, int_two int2, int_four int4); -CREATE TABLE test_type_diff2_c3 (int_two int2, int_four int4, int_eight int8); -ALTER TABLE test_type_diff2_c1 INHERIT test_type_diff2; -ALTER TABLE test_type_diff2_c2 INHERIT test_type_diff2; -ALTER TABLE test_type_diff2_c3 INHERIT test_type_diff2; -INSERT INTO test_type_diff2_c1 VALUES (1, 2, 3); -INSERT INTO test_type_diff2_c2 VALUES (4, 5, 6); -INSERT INTO test_type_diff2_c3 VALUES (7, 8, 9); -ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int8 USING int_four::int8; --- whole-row references are disallowed -ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int4 USING (pg_column_size(test_type_diff2)); -ERROR: cannot convert whole-row table reference -DETAIL: USING expression contains a whole-row table reference. --- check for rollback of ANALYZE corrupting table property flags (bug #11638) -CREATE TABLE check_fk_presence_1 (id int PRIMARY KEY, t text); -CREATE TABLE check_fk_presence_2 (id int REFERENCES check_fk_presence_1, t text); -BEGIN; -ALTER TABLE check_fk_presence_2 DROP CONSTRAINT check_fk_presence_2_id_fkey; -ANALYZE check_fk_presence_2; -ROLLBACK; -\d check_fk_presence_2 - Table "public.check_fk_presence_2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - id | integer | | | - t | text | | | -Foreign-key constraints: - "check_fk_presence_2_id_fkey" FOREIGN KEY (id) REFERENCES check_fk_presence_1(id) - -DROP TABLE check_fk_presence_1, check_fk_presence_2; --- check column addition within a view (bug #14876) -create table at_base_table(id int, stuff text); -insert into at_base_table values (23, 'skidoo'); -create view at_view_1 as select * from at_base_table bt; -create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; -\d+ at_view_1 - View "public.at_view_1" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+----------+------------- - id | integer | | | | plain | - stuff | text | | | | extended | -View definition: - SELECT bt.id, - bt.stuff - FROM at_base_table bt; - -\d+ at_view_2 - View "public.at_view_2" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+----------+------------- - id | integer | | | | plain | - stuff | text | | | | extended | - j | json | | | | extended | -View definition: - SELECT v1.id, - v1.stuff, - to_json(v1.*) AS j - FROM at_view_1 v1; - -explain (verbose, costs off) select * from at_view_2; - QUERY PLAN ----------------------------------------------------------- - Seq Scan on public.at_base_table bt - Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff)) -(2 rows) - -select * from at_view_2; - id | stuff | j -----+--------+---------------------------- - 23 | skidoo | {"id":23,"stuff":"skidoo"} -(1 row) - -create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; -\d+ at_view_1 - View "public.at_view_1" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+----------+------------- - id | integer | | | | plain | - stuff | text | | | | extended | - more | integer | | | | plain | -View definition: - SELECT bt.id, - bt.stuff, - 2 + 2 AS more - FROM at_base_table bt; - -\d+ at_view_2 - View "public.at_view_2" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+---------+-----------+----------+---------+----------+------------- - id | integer | | | | plain | - stuff | text | | | | extended | - j | json | | | | extended | -View definition: - SELECT v1.id, - v1.stuff, - to_json(v1.*) AS j - FROM at_view_1 v1; - -explain (verbose, costs off) select * from at_view_2; - QUERY PLAN ----------------------------------------------------------------- - Seq Scan on public.at_base_table bt - Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff, NULL)) -(2 rows) - -select * from at_view_2; - id | stuff | j -----+--------+---------------------------------------- - 23 | skidoo | {"id":23,"stuff":"skidoo","more":null} -(1 row) - -drop view at_view_2; -drop view at_view_1; -drop table at_base_table; --- check adding a column not iself requiring a rewrite, together with --- a column requiring a default (bug #16038) --- ensure that rewrites aren't silently optimized away, removing the --- value of the test -CREATE FUNCTION check_ddl_rewrite(p_tablename regclass, p_ddl text) -RETURNS boolean -LANGUAGE plpgsql AS $$ -DECLARE - v_relfilenode oid; -BEGIN - v_relfilenode := relfilenode FROM pg_class WHERE oid = p_tablename; - - EXECUTE p_ddl; - - RETURN v_relfilenode <> (SELECT relfilenode FROM pg_class WHERE oid = p_tablename); -END; -$$; -CREATE TABLE rewrite_test(col text); -INSERT INTO rewrite_test VALUES ('something'); -INSERT INTO rewrite_test VALUES (NULL); --- empty[12] don't need rewrite, but notempty[12]_rewrite will force one -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN empty1 text, - ADD COLUMN notempty1_rewrite serial; -$$); - check_ddl_rewrite -------------------- - t -(1 row) - -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN notempty2_rewrite serial, - ADD COLUMN empty2 text; -$$); - check_ddl_rewrite -------------------- - t -(1 row) - --- also check that fast defaults cause no problem, first without rewrite -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN empty3 text, - ADD COLUMN notempty3_norewrite int default 42; -$$); - check_ddl_rewrite -------------------- - f -(1 row) - -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN notempty4_norewrite int default 42, - ADD COLUMN empty4 text; -$$); - check_ddl_rewrite -------------------- - f -(1 row) - --- then with rewrite -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN empty5 text, - ADD COLUMN notempty5_norewrite int default 42, - ADD COLUMN notempty5_rewrite serial; -$$); - check_ddl_rewrite -------------------- - t -(1 row) - -SELECT check_ddl_rewrite('rewrite_test', $$ - ALTER TABLE rewrite_test - ADD COLUMN notempty6_rewrite serial, - ADD COLUMN empty6 text, - ADD COLUMN notempty6_norewrite int default 42; -$$); - check_ddl_rewrite -------------------- - t -(1 row) - --- cleanup -DROP FUNCTION check_ddl_rewrite(regclass, text); -DROP TABLE rewrite_test; --- --- lock levels --- -drop type lockmodes; -ERROR: type "lockmodes" does not exist -create type lockmodes as enum ( - 'SIReadLock' -,'AccessShareLock' -,'RowShareLock' -,'RowExclusiveLock' -,'ShareUpdateExclusiveLock' -,'ShareLock' -,'ShareRowExclusiveLock' -,'ExclusiveLock' -,'AccessExclusiveLock' -); -drop view my_locks; -ERROR: view "my_locks" does not exist -create or replace view my_locks as -select case when c.relname like 'pg_toast%' then 'pg_toast' else c.relname end, max(mode::lockmodes) as max_lockmode -from pg_locks l join pg_class c on l.relation = c.oid -where virtualtransaction = ( - select virtualtransaction - from pg_locks - where transactionid = pg_current_xact_id()::xid) -and locktype = 'relation' -and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') -and c.relname != 'my_locks' -group by c.relname; -create table alterlock (f1 int primary key, f2 text); -insert into alterlock values (1, 'foo'); -create table alterlock2 (f3 int primary key, f1 int); -insert into alterlock2 values (1, 1); -begin; alter table alterlock alter column f2 set statistics 150; -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock -(1 row) - -rollback; -begin; alter table alterlock cluster on alterlock_pkey; -select * from my_locks order by 1; - relname | max_lockmode -----------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - alterlock_pkey | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock set without cluster; -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock -(1 row) - -commit; -begin; alter table alterlock set (fillfactor = 100); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - pg_toast | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock reset (fillfactor); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - pg_toast | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock set (toast.autovacuum_enabled = off); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - pg_toast | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock set (autovacuum_enabled = off); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - pg_toast | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock alter column f2 set (n_distinct = 1); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock -(1 row) - -rollback; --- test that mixing options with different lock levels works as expected -begin; alter table alterlock set (autovacuum_enabled = off, fillfactor = 80); -select * from my_locks order by 1; - relname | max_lockmode ------------+-------------------------- - alterlock | ShareUpdateExclusiveLock - pg_toast | ShareUpdateExclusiveLock -(2 rows) - -commit; -begin; alter table alterlock alter column f2 set storage extended; -select * from my_locks order by 1; - relname | max_lockmode ------------+--------------------- - alterlock | AccessExclusiveLock -(1 row) - -rollback; -begin; alter table alterlock alter column f2 set default 'x'; -select * from my_locks order by 1; - relname | max_lockmode ------------+--------------------- - alterlock | AccessExclusiveLock -(1 row) - -rollback; -begin; -create trigger ttdummy - before delete or update on alterlock - for each row - execute procedure - ttdummy (1, 1); -select * from my_locks order by 1; - relname | max_lockmode ------------+----------------------- - alterlock | ShareRowExclusiveLock -(1 row) - -rollback; -begin; -select * from my_locks order by 1; - relname | max_lockmode ----------+-------------- -(0 rows) - -alter table alterlock2 add foreign key (f1) references alterlock (f1); -select * from my_locks order by 1; - relname | max_lockmode ------------------+----------------------- - alterlock | ShareRowExclusiveLock - alterlock2 | ShareRowExclusiveLock - alterlock2_pkey | AccessShareLock - alterlock_pkey | AccessShareLock -(4 rows) - -rollback; -begin; -alter table alterlock2 -add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID; -select * from my_locks order by 1; - relname | max_lockmode -------------+----------------------- - alterlock | ShareRowExclusiveLock - alterlock2 | ShareRowExclusiveLock -(2 rows) - -commit; -begin; -alter table alterlock2 validate constraint alterlock2nv; -select * from my_locks order by 1; - relname | max_lockmode ------------------+-------------------------- - alterlock | RowShareLock - alterlock2 | ShareUpdateExclusiveLock - alterlock2_pkey | AccessShareLock - alterlock_pkey | AccessShareLock -(4 rows) - -rollback; -create or replace view my_locks as -select case when c.relname like 'pg_toast%' then 'pg_toast' else c.relname end, max(mode::lockmodes) as max_lockmode -from pg_locks l join pg_class c on l.relation = c.oid -where virtualtransaction = ( - select virtualtransaction - from pg_locks - where transactionid = pg_current_xact_id()::xid) -and locktype = 'relation' -and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') -and c.relname = 'my_locks' -group by c.relname; --- raise exception -alter table my_locks set (autovacuum_enabled = false); -ERROR: unrecognized parameter "autovacuum_enabled" -alter view my_locks set (autovacuum_enabled = false); -ERROR: unrecognized parameter "autovacuum_enabled" -alter table my_locks reset (autovacuum_enabled); -alter view my_locks reset (autovacuum_enabled); -begin; -alter view my_locks set (security_barrier=off); -select * from my_locks order by 1; - relname | max_lockmode -----------+--------------------- - my_locks | AccessExclusiveLock -(1 row) - -alter view my_locks reset (security_barrier); -rollback; --- this test intentionally applies the ALTER TABLE command against a view, but --- uses a view option so we expect this to succeed. This form of SQL is --- accepted for historical reasons, as shown in the docs for ALTER VIEW -begin; -alter table my_locks set (security_barrier=off); -select * from my_locks order by 1; - relname | max_lockmode -----------+--------------------- - my_locks | AccessExclusiveLock -(1 row) - -alter table my_locks reset (security_barrier); -rollback; --- cleanup -drop table alterlock2; -drop table alterlock; -drop view my_locks; -drop type lockmodes; --- --- alter function --- -create function test_strict(text) returns text as - 'select coalesce($1, ''got passed a null'');' - language sql returns null on null input; -select test_strict(NULL); - test_strict -------------- - -(1 row) - -alter function test_strict(text) called on null input; -select test_strict(NULL); - test_strict -------------------- - got passed a null -(1 row) - -create function non_strict(text) returns text as - 'select coalesce($1, ''got passed a null'');' - language sql called on null input; -select non_strict(NULL); - non_strict -------------------- - got passed a null -(1 row) - -alter function non_strict(text) returns null on null input; -select non_strict(NULL); - non_strict ------------- - -(1 row) - --- --- alter object set schema --- -create schema alter1; -create schema alter2; -create table alter1.t1(f1 serial primary key, f2 int check (f2 > 0)); -create view alter1.v1 as select * from alter1.t1; -create function alter1.plus1(int) returns int as 'select $1+1' language sql; -create domain alter1.posint integer check (value > 0); -create type alter1.ctype as (f1 int, f2 text); -create function alter1.same(alter1.ctype, alter1.ctype) returns boolean language sql -as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2'; -create operator alter1.=(procedure = alter1.same, leftarg = alter1.ctype, rightarg = alter1.ctype); -create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as - operator 1 alter1.=(alter1.ctype, alter1.ctype); -create conversion alter1.latin1_to_utf8 for 'latin1' to 'utf8' from iso8859_1_to_utf8; -create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); -create text search configuration alter1.cfg(parser = alter1.prs); -create text search template alter1.tmpl(init = dsimple_init, lexize = dsimple_lexize); -create text search dictionary alter1.dict(template = alter1.tmpl); -insert into alter1.t1(f2) values(11); -insert into alter1.t1(f2) values(12); -alter table alter1.t1 set schema alter1; -- no-op, same schema -alter table alter1.t1 set schema alter2; -alter table alter1.v1 set schema alter2; -alter function alter1.plus1(int) set schema alter2; -alter domain alter1.posint set schema alter2; -alter operator class alter1.ctype_hash_ops using hash set schema alter2; -alter operator family alter1.ctype_hash_ops using hash set schema alter2; -alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2; -alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2; -alter type alter1.ctype set schema alter1; -- no-op, same schema -alter type alter1.ctype set schema alter2; -alter conversion alter1.latin1_to_utf8 set schema alter2; -alter text search parser alter1.prs set schema alter2; -alter text search configuration alter1.cfg set schema alter2; -alter text search template alter1.tmpl set schema alter2; -alter text search dictionary alter1.dict set schema alter2; --- this should succeed because nothing is left in alter1 -drop schema alter1; -insert into alter2.t1(f2) values(13); -insert into alter2.t1(f2) values(14); -select * from alter2.t1; - f1 | f2 -----+---- - 1 | 11 - 2 | 12 - 3 | 13 - 4 | 14 -(4 rows) - -select * from alter2.v1; - f1 | f2 -----+---- - 1 | 11 - 2 | 12 - 3 | 13 - 4 | 14 -(4 rows) - -select alter2.plus1(41); - plus1 -------- - 42 -(1 row) - --- clean up -drop schema alter2 cascade; -NOTICE: drop cascades to 13 other objects -DETAIL: drop cascades to table alter2.t1 -drop cascades to view alter2.v1 -drop cascades to function alter2.plus1(integer) -drop cascades to type alter2.posint -drop cascades to type alter2.ctype -drop cascades to function alter2.same(alter2.ctype,alter2.ctype) -drop cascades to operator alter2.=(alter2.ctype,alter2.ctype) -drop cascades to operator family alter2.ctype_hash_ops for access method hash -drop cascades to conversion alter2.latin1_to_utf8 -drop cascades to text search parser alter2.prs -drop cascades to text search configuration alter2.cfg -drop cascades to text search template alter2.tmpl -drop cascades to text search dictionary alter2.dict --- --- composite types --- -CREATE TYPE test_type AS (a int); -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - -ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails -ERROR: relation "nosuchtype" does not exist -ALTER TYPE test_type ADD ATTRIBUTE b text; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | - -ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails -ERROR: column "b" of relation "test_type" already exists -ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+-------------------+-----------+----------+--------- - a | integer | | | - b | character varying | | | - -ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - -ALTER TYPE test_type DROP ATTRIBUTE b; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - -ALTER TYPE test_type DROP ATTRIBUTE c; -- fails -ERROR: column "c" of relation "test_type" does not exist -ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c; -NOTICE: column "c" of relation "test_type" does not exist, skipping -ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - d | boolean | | | - -ALTER TYPE test_type RENAME ATTRIBUTE a TO aa; -ERROR: column "a" does not exist -ALTER TYPE test_type RENAME ATTRIBUTE d TO dd; -\d test_type - Composite type "public.test_type" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - dd | boolean | | | - -DROP TYPE test_type; -CREATE TYPE test_type1 AS (a int, b text); -CREATE TABLE test_tbl1 (x int, y test_type1); -ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails -ERROR: cannot alter type "test_type1" because column "test_tbl1.y" uses it -CREATE TYPE test_type2 AS (a int, b text); -CREATE TABLE test_tbl2 OF test_type2; -CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2); -\d test_type2 - Composite type "public.test_type2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | - -\d test_tbl2 - Table "public.test_tbl2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | -Number of child tables: 1 (Use \d+ to list them.) -Typed table of type: test_type2 - -ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails -ERROR: cannot alter type "test_type2" because it is the type of a typed table -HINT: Use ALTER ... CASCADE to alter the typed tables too. -ALTER TYPE test_type2 ADD ATTRIBUTE c text CASCADE; -\d test_type2 - Composite type "public.test_type2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | - c | text | | | - -\d test_tbl2 - Table "public.test_tbl2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | text | | | - c | text | | | -Number of child tables: 1 (Use \d+ to list them.) -Typed table of type: test_type2 - -ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails -ERROR: cannot alter type "test_type2" because it is the type of a typed table -HINT: Use ALTER ... CASCADE to alter the typed tables too. -ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar CASCADE; -\d test_type2 - Composite type "public.test_type2" - Column | Type | Collation | Nullable | Default ---------+-------------------+-----------+----------+--------- - a | integer | | | - b | character varying | | | - c | text | | | - -\d test_tbl2 - Table "public.test_tbl2" - Column | Type | Collation | Nullable | Default ---------+-------------------+-----------+----------+--------- - a | integer | | | - b | character varying | | | - c | text | | | -Number of child tables: 1 (Use \d+ to list them.) -Typed table of type: test_type2 - -ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails -ERROR: cannot alter type "test_type2" because it is the type of a typed table -HINT: Use ALTER ... CASCADE to alter the typed tables too. -ALTER TYPE test_type2 DROP ATTRIBUTE b CASCADE; -\d test_type2 - Composite type "public.test_type2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - c | text | | | - -\d test_tbl2 - Table "public.test_tbl2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - c | text | | | -Number of child tables: 1 (Use \d+ to list them.) -Typed table of type: test_type2 - -ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa; -- fails -ERROR: cannot alter type "test_type2" because it is the type of a typed table -HINT: Use ALTER ... CASCADE to alter the typed tables too. -ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE; -\d test_type2 - Composite type "public.test_type2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - aa | integer | | | - c | text | | | - -\d test_tbl2 - Table "public.test_tbl2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - aa | integer | | | - c | text | | | -Number of child tables: 1 (Use \d+ to list them.) -Typed table of type: test_type2 - -\d test_tbl2_subclass - Table "public.test_tbl2_subclass" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - aa | integer | | | - c | text | | | -Inherits: test_tbl2 - -DROP TABLE test_tbl2_subclass; -CREATE TYPE test_typex AS (a int, b text); -CREATE TABLE test_tblx (x int, y test_typex check ((y).a > 0)); -ALTER TYPE test_typex DROP ATTRIBUTE a; -- fails -ERROR: cannot drop column a of composite type test_typex because other objects depend on it -DETAIL: constraint test_tblx_y_check on table test_tblx depends on column a of composite type test_typex -HINT: Use DROP ... CASCADE to drop the dependent objects too. -ALTER TYPE test_typex DROP ATTRIBUTE a CASCADE; -NOTICE: drop cascades to constraint test_tblx_y_check on table test_tblx -\d test_tblx - Table "public.test_tblx" - Column | Type | Collation | Nullable | Default ---------+------------+-----------+----------+--------- - x | integer | | | - y | test_typex | | | - -DROP TABLE test_tblx; -DROP TYPE test_typex; --- This test isn't that interesting on its own, but the purpose is to leave --- behind a table to test pg_upgrade with. The table has a composite type --- column in it, and the composite type has a dropped attribute. -CREATE TYPE test_type3 AS (a int); -CREATE TABLE test_tbl3 (c) AS SELECT '(1)'::test_type3; -ALTER TYPE test_type3 DROP ATTRIBUTE a, ADD ATTRIBUTE b int; -CREATE TYPE test_type_empty AS (); -DROP TYPE test_type_empty; --- --- typed tables: OF / NOT OF --- -CREATE TYPE tt_t0 AS (z inet, x int, y numeric(8,2)); -ALTER TYPE tt_t0 DROP ATTRIBUTE z; -CREATE TABLE tt0 (x int NOT NULL, y numeric(8,2)); -- OK -CREATE TABLE tt1 (x int, y bigint); -- wrong base type -CREATE TABLE tt2 (x int, y numeric(9,2)); -- wrong typmod -CREATE TABLE tt3 (y numeric(8,2), x int); -- wrong column order -CREATE TABLE tt4 (x int); -- too few columns -CREATE TABLE tt5 (x int, y numeric(8,2), z int); -- too few columns -CREATE TABLE tt6 () INHERITS (tt0); -- can't have a parent -CREATE TABLE tt7 (x int, q text, y numeric(8,2)); -ALTER TABLE tt7 DROP q; -- OK -ALTER TABLE tt0 OF tt_t0; -ALTER TABLE tt1 OF tt_t0; -ERROR: table "tt1" has different type for column "y" -ALTER TABLE tt2 OF tt_t0; -ERROR: table "tt2" has different type for column "y" -ALTER TABLE tt3 OF tt_t0; -ERROR: table has column "y" where type requires "x" -ALTER TABLE tt4 OF tt_t0; -ERROR: table is missing column "y" -ALTER TABLE tt5 OF tt_t0; -ERROR: table has extra column "z" -ALTER TABLE tt6 OF tt_t0; -ERROR: typed tables cannot inherit -ALTER TABLE tt7 OF tt_t0; -CREATE TYPE tt_t1 AS (x int, y numeric(8,2)); -ALTER TABLE tt7 OF tt_t1; -- reassign an already-typed table -ALTER TABLE tt7 NOT OF; -\d tt7 - Table "public.tt7" - Column | Type | Collation | Nullable | Default ---------+--------------+-----------+----------+--------- - x | integer | | | - y | numeric(8,2) | | | - --- make sure we can drop a constraint on the parent but it remains on the child -CREATE TABLE test_drop_constr_parent (c text CHECK (c IS NOT NULL)); -CREATE TABLE test_drop_constr_child () INHERITS (test_drop_constr_parent); -ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_parent_c_check"; --- should fail -INSERT INTO test_drop_constr_child (c) VALUES (NULL); -ERROR: new row for relation "test_drop_constr_child" violates check constraint "test_drop_constr_parent_c_check" -DETAIL: Failing row contains (null). -DROP TABLE test_drop_constr_parent CASCADE; -NOTICE: drop cascades to table test_drop_constr_child --- --- IF EXISTS test --- -ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; -NOTICE: relation "tt8" does not exist, skipping -ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY(f); -NOTICE: relation "tt8" does not exist, skipping -ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); -NOTICE: relation "tt8" does not exist, skipping -ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; -NOTICE: relation "tt8" does not exist, skipping -ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; -NOTICE: relation "tt8" does not exist, skipping -ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; -NOTICE: relation "tt8" does not exist, skipping -CREATE TABLE tt8(a int); -CREATE SCHEMA alter2; -ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; -ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY(f); -ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); -ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; -ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; -ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; -\d alter2.tt8 - Table "alter2.tt8" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - f1 | integer | | not null | 0 -Indexes: - "xxx" PRIMARY KEY, btree (f1) -Check constraints: - "tt8_f_check" CHECK (f1 >= 0 AND f1 <= 10) - -DROP TABLE alter2.tt8; -DROP SCHEMA alter2; --- --- Check conflicts between index and CHECK constraint names --- -CREATE TABLE tt9(c integer); -ALTER TABLE tt9 ADD CHECK(c > 1); -ALTER TABLE tt9 ADD CHECK(c > 2); -- picks nonconflicting name -ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 3); -ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 4); -- fail, dup name -ERROR: constraint "foo" for relation "tt9" already exists -ALTER TABLE tt9 ADD UNIQUE(c); -ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name -ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key UNIQUE(c); -- fail, dup name -ERROR: relation "tt9_c_key" already exists -ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE(c); -- fail, dup name -ERROR: constraint "foo" for relation "tt9" already exists -ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK(c > 5); -- fail, dup name -ERROR: constraint "tt9_c_key" for relation "tt9" already exists -ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK(c > 6); -ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name -\d tt9 - Table "public.tt9" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c | integer | | | -Indexes: - "tt9_c_key" UNIQUE CONSTRAINT, btree (c) - "tt9_c_key1" UNIQUE CONSTRAINT, btree (c) - "tt9_c_key3" UNIQUE CONSTRAINT, btree (c) -Check constraints: - "foo" CHECK (c > 3) - "tt9_c_check" CHECK (c > 1) - "tt9_c_check1" CHECK (c > 2) - "tt9_c_key2" CHECK (c > 6) - -DROP TABLE tt9; --- Check that comments on constraints and indexes are not lost at ALTER TABLE. -CREATE TABLE comment_test ( - id int, - positive_col int CHECK (positive_col > 0), - indexed_col int, - CONSTRAINT comment_test_pk PRIMARY KEY (id)); -CREATE INDEX comment_test_index ON comment_test(indexed_col); -COMMENT ON COLUMN comment_test.id IS 'Column ''id'' on comment_test'; -COMMENT ON INDEX comment_test_index IS 'Simple index on comment_test'; -COMMENT ON CONSTRAINT comment_test_positive_col_check ON comment_test IS 'CHECK constraint on comment_test.positive_col'; -COMMENT ON CONSTRAINT comment_test_pk ON comment_test IS 'PRIMARY KEY constraint of comment_test'; -COMMENT ON INDEX comment_test_pk IS 'Index backing the PRIMARY KEY of comment_test'; -SELECT col_description('comment_test'::regclass, 1) as comment; - comment ------------------------------ - Column 'id' on comment_test -(1 row) - -SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2; - index | comment ---------------------+----------------------------------------------- - comment_test_index | Simple index on comment_test - comment_test_pk | Index backing the PRIMARY KEY of comment_test -(2 rows) - -SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDER BY 1, 2; - constraint | comment ----------------------------------+----------------------------------------------- - comment_test_pk | PRIMARY KEY constraint of comment_test - comment_test_positive_col_check | CHECK constraint on comment_test.positive_col -(2 rows) - --- Change the datatype of all the columns. ALTER TABLE is optimized to not --- rebuild an index if the new data type is binary compatible with the old --- one. Check do a dummy ALTER TABLE that doesn't change the datatype --- first, to test that no-op codepath, and another one that does. -ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE int; -ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE text; -ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int; -ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; -ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int; -ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint; --- Check that the comments are intact. -SELECT col_description('comment_test'::regclass, 1) as comment; - comment ------------------------------ - Column 'id' on comment_test -(1 row) - -SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2; - index | comment ---------------------+----------------------------------------------- - comment_test_index | Simple index on comment_test - comment_test_pk | Index backing the PRIMARY KEY of comment_test -(2 rows) - -SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDER BY 1, 2; - constraint | comment ----------------------------------+----------------------------------------------- - comment_test_pk | PRIMARY KEY constraint of comment_test - comment_test_positive_col_check | CHECK constraint on comment_test.positive_col -(2 rows) - --- Check compatibility for foreign keys and comments. This is done --- separately as rebuilding the column type of the parent leads --- to an error and would reduce the test scope. -CREATE TABLE comment_test_child ( - id text CONSTRAINT comment_test_child_fk REFERENCES comment_test); -CREATE INDEX comment_test_child_fk ON comment_test_child(id); -COMMENT ON COLUMN comment_test_child.id IS 'Column ''id'' on comment_test_child'; -COMMENT ON INDEX comment_test_child_fk IS 'Index backing the FOREIGN KEY of comment_test_child'; -COMMENT ON CONSTRAINT comment_test_child_fk ON comment_test_child IS 'FOREIGN KEY constraint of comment_test_child'; --- Change column type of parent -ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; -ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int USING id::integer; -ERROR: foreign key constraint "comment_test_child_fk" cannot be implemented -DETAIL: Key columns "id" and "id" are of incompatible types: text and integer. --- Comments should be intact -SELECT col_description('comment_test_child'::regclass, 1) as comment; - comment ------------------------------------ - Column 'id' on comment_test_child -(1 row) - -SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test_child'::regclass ORDER BY 1, 2; - index | comment ------------------------+----------------------------------------------------- - comment_test_child_fk | Index backing the FOREIGN KEY of comment_test_child -(1 row) - -SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test_child'::regclass ORDER BY 1, 2; - constraint | comment ------------------------+---------------------------------------------- - comment_test_child_fk | FOREIGN KEY constraint of comment_test_child -(1 row) - --- Check that we map relation oids to filenodes and back correctly. Only --- display bad mappings so the test output doesn't change all the time. A --- filenode function call can return NULL for a relation dropped concurrently --- with the call's surrounding query, so ignore a NULL mapped_oid for --- relations that no longer exist after all calls finish. -CREATE TEMP TABLE filenode_mapping AS -SELECT - oid, mapped_oid, reltablespace, relfilenode, relname -FROM pg_class, - pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid -WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid; -SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid -WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL; - oid | mapped_oid | reltablespace | relfilenode | relname ------+------------+---------------+-------------+--------- -(0 rows) - --- Checks on creating and manipulation of user defined relations in --- pg_catalog. -SHOW allow_system_table_mods; - allow_system_table_mods -------------------------- - off -(1 row) - --- disallowed because of search_path issues with pg_dump -CREATE TABLE pg_catalog.new_system_table(); -ERROR: permission denied to create "pg_catalog.new_system_table" -DETAIL: System catalog modifications are currently disallowed. --- instead create in public first, move to catalog -CREATE TABLE new_system_table(id serial primary key, othercol text); -ALTER TABLE new_system_table SET SCHEMA pg_catalog; -ALTER TABLE new_system_table SET SCHEMA public; -ALTER TABLE new_system_table SET SCHEMA pg_catalog; --- will be ignored -- already there: -ALTER TABLE new_system_table SET SCHEMA pg_catalog; -ALTER TABLE new_system_table RENAME TO old_system_table; -CREATE INDEX old_system_table__othercol ON old_system_table (othercol); -INSERT INTO old_system_table(othercol) VALUES ('somedata'), ('otherdata'); -UPDATE old_system_table SET id = -id; -DELETE FROM old_system_table WHERE othercol = 'somedata'; -TRUNCATE old_system_table; -ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey; -ALTER TABLE old_system_table DROP COLUMN othercol; -DROP TABLE old_system_table; --- set logged -CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT); --- check relpersistence of an unlogged table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' -UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' -UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' -ORDER BY relname; - relname | relkind | relpersistence -------------------+---------+---------------- - toast index | i | u - toast table | t | u - unlogged1 | r | u - unlogged1_f1_seq | S | p - unlogged1_pkey | i | u -(5 rows) - -CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key -CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key -ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key -ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists -ERROR: could not change table "unlogged2" to logged because it references unlogged table "unlogged1" -ALTER TABLE unlogged1 SET LOGGED; --- check relpersistence of an unlogged table after changing to permanent -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' -UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' -UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' -ORDER BY relname; - relname | relkind | relpersistence -------------------+---------+---------------- - toast index | i | p - toast table | t | p - unlogged1 | r | p - unlogged1_f1_seq | S | p - unlogged1_pkey | i | p -(5 rows) - -ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing -DROP TABLE unlogged3; -DROP TABLE unlogged2; -DROP TABLE unlogged1; --- set unlogged -CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT); --- check relpersistence of a permanent table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' -UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' -UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' -ORDER BY relname; - relname | relkind | relpersistence -----------------+---------+---------------- - logged1 | r | p - logged1_f1_seq | S | p - logged1_pkey | i | p - toast index | i | p - toast table | t | p -(5 rows) - -CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key -CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key -ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists -ERROR: could not change table "logged1" to unlogged because it references logged table "logged2" -ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key -ALTER TABLE logged2 SET UNLOGGED; -ALTER TABLE logged1 SET UNLOGGED; --- check relpersistence of a permanent table after changing to unlogged -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' -UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' -UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' -ORDER BY relname; - relname | relkind | relpersistence -----------------+---------+---------------- - logged1 | r | u - logged1_f1_seq | S | p - logged1_pkey | i | u - toast index | i | u - toast table | t | u -(5 rows) - -ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing -DROP TABLE logged3; -DROP TABLE logged2; -DROP TABLE logged1; --- test ADD COLUMN IF NOT EXISTS -CREATE TABLE test_add_column(c1 integer); -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - -ALTER TABLE test_add_column - ADD COLUMN c2 integer; -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - -ALTER TABLE test_add_column - ADD COLUMN c2 integer; -- fail because c2 already exists -ERROR: column "c2" of relation "test_add_column" already exists -ALTER TABLE ONLY test_add_column - ADD COLUMN c2 integer; -- fail because c2 already exists -ERROR: column "c2" of relation "test_add_column" already exists -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists -NOTICE: column "c2" of relation "test_add_column" already exists, skipping -ALTER TABLE ONLY test_add_column - ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists -NOTICE: column "c2" of relation "test_add_column" already exists, skipping -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - -ALTER TABLE test_add_column - ADD COLUMN c2 integer, -- fail because c2 already exists - ADD COLUMN c3 integer primary key; -ERROR: column "c2" of relation "test_add_column" already exists -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists - ADD COLUMN c3 integer primary key; -NOTICE: column "c2" of relation "test_add_column" already exists, skipping -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists - ADD COLUMN IF NOT EXISTS c3 integer primary key; -- skipping because c3 already exists -NOTICE: column "c2" of relation "test_add_column" already exists, skipping -NOTICE: column "c3" of relation "test_add_column" already exists, skipping -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists - ADD COLUMN IF NOT EXISTS c3 integer, -- skipping because c3 already exists - ADD COLUMN c4 integer REFERENCES test_add_column; -NOTICE: column "c2" of relation "test_add_column" already exists, skipping -NOTICE: column "c3" of relation "test_add_column" already exists, skipping -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | - c4 | integer | | | -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) -Foreign-key constraints: - "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) -Referenced by: - TABLE "test_add_column" CONSTRAINT "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c4 integer REFERENCES test_add_column; -NOTICE: column "c4" of relation "test_add_column" already exists, skipping -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | - c4 | integer | | | -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) -Foreign-key constraints: - "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) -Referenced by: - TABLE "test_add_column" CONSTRAINT "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c5 SERIAL CHECK (c5 > 8); -\d test_add_column - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | - c4 | integer | | | - c5 | integer | | not null | nextval('test_add_column_c5_seq'::regclass) -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) -Check constraints: - "test_add_column_c5_check" CHECK (c5 > 8) -Foreign-key constraints: - "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) -Referenced by: - TABLE "test_add_column" CONSTRAINT "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) - -ALTER TABLE test_add_column - ADD COLUMN IF NOT EXISTS c5 SERIAL CHECK (c5 > 10); -NOTICE: column "c5" of relation "test_add_column" already exists, skipping -\d test_add_column* - Table "public.test_add_column" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------- - c1 | integer | | | - c2 | integer | | | - c3 | integer | | not null | - c4 | integer | | | - c5 | integer | | not null | nextval('test_add_column_c5_seq'::regclass) -Indexes: - "test_add_column_pkey" PRIMARY KEY, btree (c3) -Check constraints: - "test_add_column_c5_check" CHECK (c5 > 8) -Foreign-key constraints: - "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) -Referenced by: - TABLE "test_add_column" CONSTRAINT "test_add_column_c4_fkey" FOREIGN KEY (c4) REFERENCES test_add_column(c3) - - Sequence "public.test_add_column_c5_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Owned by: public.test_add_column.c5 - - Index "public.test_add_column_pkey" - Column | Type | Key? | Definition ---------+---------+------+------------ - c3 | integer | yes | c3 -primary key, btree, for table "public.test_add_column" - -DROP TABLE test_add_column; -\d test_add_column* --- assorted cases with multiple ALTER TABLE steps -CREATE TABLE ataddindex(f1 INT); -INSERT INTO ataddindex VALUES (42), (43); -CREATE UNIQUE INDEX ataddindexi0 ON ataddindex(f1); -ALTER TABLE ataddindex - ADD PRIMARY KEY USING INDEX ataddindexi0, - ALTER f1 TYPE BIGINT; -\d ataddindex - Table "public.ataddindex" - Column | Type | Collation | Nullable | Default ---------+--------+-----------+----------+--------- - f1 | bigint | | not null | -Indexes: - "ataddindexi0" PRIMARY KEY, btree (f1) - -DROP TABLE ataddindex; -CREATE TABLE ataddindex(f1 VARCHAR(10)); -INSERT INTO ataddindex(f1) VALUES ('foo'), ('a'); -ALTER TABLE ataddindex - ALTER f1 SET DATA TYPE TEXT, - ADD EXCLUDE ((f1 LIKE 'a') WITH =); -\d ataddindex - Table "public.ataddindex" - Column | Type | Collation | Nullable | Default ---------+------+-----------+----------+--------- - f1 | text | | | -Indexes: - "ataddindex_expr_excl" EXCLUDE USING btree ((f1 ~~ 'a'::text) WITH =) - -DROP TABLE ataddindex; -CREATE TABLE ataddindex(id int, ref_id int); -ALTER TABLE ataddindex - ADD PRIMARY KEY (id), - ADD FOREIGN KEY (ref_id) REFERENCES ataddindex; -\d ataddindex - Table "public.ataddindex" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - id | integer | | not null | - ref_id | integer | | | -Indexes: - "ataddindex_pkey" PRIMARY KEY, btree (id) -Foreign-key constraints: - "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id) -Referenced by: - TABLE "ataddindex" CONSTRAINT "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id) - -DROP TABLE ataddindex; -CREATE TABLE ataddindex(id int, ref_id int); -ALTER TABLE ataddindex - ADD UNIQUE (id), - ADD FOREIGN KEY (ref_id) REFERENCES ataddindex (id); -\d ataddindex - Table "public.ataddindex" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - id | integer | | | - ref_id | integer | | | -Indexes: - "ataddindex_id_key" UNIQUE CONSTRAINT, btree (id) -Foreign-key constraints: - "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id) -Referenced by: - TABLE "ataddindex" CONSTRAINT "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id) - -DROP TABLE ataddindex; --- unsupported constraint types for partitioned tables -CREATE TABLE partitioned ( - a int, - b int -) PARTITION BY RANGE (a, (a+b+1)); -ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&); -ERROR: exclusion constraints are not supported on partitioned tables -LINE 1: ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&); - ^ --- cannot drop column that is part of the partition key -ALTER TABLE partitioned DROP COLUMN a; -ERROR: cannot drop column "a" because it is part of the partition key of relation "partitioned" -ALTER TABLE partitioned ALTER COLUMN a TYPE char(5); -ERROR: cannot alter column "a" because it is part of the partition key of relation "partitioned" -ALTER TABLE partitioned DROP COLUMN b; -ERROR: cannot drop column "b" because it is part of the partition key of relation "partitioned" -ALTER TABLE partitioned ALTER COLUMN b TYPE char(5); -ERROR: cannot alter column "b" because it is part of the partition key of relation "partitioned" --- partitioned table cannot participate in regular inheritance -CREATE TABLE nonpartitioned ( - a int, - b int -); -ALTER TABLE partitioned INHERIT nonpartitioned; -ERROR: cannot change inheritance of partitioned table -ALTER TABLE nonpartitioned INHERIT partitioned; -ERROR: cannot inherit from partitioned table "partitioned" --- cannot add NO INHERIT constraint to partitioned tables -ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT; -ERROR: cannot add NO INHERIT constraint to partitioned table "partitioned" -DROP TABLE partitioned, nonpartitioned; --- --- ATTACH PARTITION --- --- check that target table is partitioned -CREATE TABLE unparted ( - a int -); -CREATE TABLE fail_part (like unparted); -ALTER TABLE unparted ATTACH PARTITION fail_part FOR VALUES IN ('a'); -ERROR: table "unparted" is not partitioned -DROP TABLE unparted, fail_part; --- check that partition bound is compatible -CREATE TABLE list_parted ( - a int NOT NULL, - b char(2) COLLATE "C", - CONSTRAINT check_a CHECK (a > 0) -) PARTITION BY LIST (a); -CREATE TABLE fail_part (LIKE list_parted); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES FROM (1) TO (10); -ERROR: invalid bound specification for a list partition -LINE 1: ...list_parted ATTACH PARTITION fail_part FOR VALUES FROM (1) T... - ^ -DROP TABLE fail_part; --- check that the table being attached exists -ALTER TABLE list_parted ATTACH PARTITION nonexistent FOR VALUES IN (1); -ERROR: relation "nonexistent" does not exist --- check ownership of the source table -CREATE ROLE regress_test_me; -CREATE ROLE regress_test_not_me; -CREATE TABLE not_owned_by_me (LIKE list_parted); -ALTER TABLE not_owned_by_me OWNER TO regress_test_not_me; -SET SESSION AUTHORIZATION regress_test_me; -CREATE TABLE owned_by_me ( - a int -) PARTITION BY LIST (a); -ALTER TABLE owned_by_me ATTACH PARTITION not_owned_by_me FOR VALUES IN (1); -ERROR: must be owner of table not_owned_by_me -RESET SESSION AUTHORIZATION; -DROP TABLE owned_by_me, not_owned_by_me; -DROP ROLE regress_test_not_me; -DROP ROLE regress_test_me; --- check that the table being attached is not part of regular inheritance -CREATE TABLE parent (LIKE list_parted); -CREATE TABLE child () INHERITS (parent); -ALTER TABLE list_parted ATTACH PARTITION child FOR VALUES IN (1); -ERROR: cannot attach inheritance child as partition -ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1); -ERROR: cannot attach inheritance parent as partition -DROP TABLE parent CASCADE; -NOTICE: drop cascades to table child --- check any TEMP-ness -CREATE TEMP TABLE temp_parted (a int) PARTITION BY LIST (a); -CREATE TABLE perm_part (a int); -ALTER TABLE temp_parted ATTACH PARTITION perm_part FOR VALUES IN (1); -ERROR: cannot attach a permanent relation as partition of temporary relation "temp_parted" -DROP TABLE temp_parted, perm_part; --- check that the table being attached is not a typed table -CREATE TYPE mytype AS (a int); -CREATE TABLE fail_part OF mytype; -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: cannot attach a typed table as partition -DROP TYPE mytype CASCADE; -NOTICE: drop cascades to table fail_part --- check that the table being attached has only columns present in the parent -CREATE TABLE fail_part (like list_parted, c int); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: table "fail_part" contains column "c" not found in parent "list_parted" -DETAIL: The new partition may contain only the columns present in parent. -DROP TABLE fail_part; --- check that the table being attached has every column of the parent -CREATE TABLE fail_part (a int NOT NULL); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: child table is missing column "b" -DROP TABLE fail_part; --- check that columns match in type, collation and NOT NULL status -CREATE TABLE fail_part ( - b char(3), - a int NOT NULL -); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: child table "fail_part" has different type for column "b" -ALTER TABLE fail_part ALTER b TYPE char (2) COLLATE "POSIX"; -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: child table "fail_part" has different collation for column "b" -DROP TABLE fail_part; --- check that the table being attached has all constraints of the parent -CREATE TABLE fail_part ( - b char(2) COLLATE "C", - a int NOT NULL -); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: child table is missing constraint "check_a" --- check that the constraint matches in definition with parent's constraint -ALTER TABLE fail_part ADD CONSTRAINT check_a CHECK (a >= 0); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: child table "fail_part" has different definition for check constraint "check_a" -DROP TABLE fail_part; --- check the attributes and constraints after partition is attached -CREATE TABLE part_1 ( - a int NOT NULL, - b char(2) COLLATE "C", - CONSTRAINT check_a CHECK (a > 0) -); -ALTER TABLE list_parted ATTACH PARTITION part_1 FOR VALUES IN (1); --- attislocal and conislocal are always false for merged attributes and constraints respectively. -SELECT attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_1'::regclass AND attnum > 0; - attislocal | attinhcount -------------+------------- - f | 1 - f | 1 -(2 rows) - -SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::regclass AND conname = 'check_a'; - conislocal | coninhcount -------------+------------- - f | 1 -(1 row) - --- check that the new partition won't overlap with an existing partition -CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS); -ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -ERROR: partition "fail_part" would overlap partition "part_1" -LINE 1: ...LE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); - ^ -DROP TABLE fail_part; --- check that an existing table can be attached as a default partition -CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS); -ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT; --- check attaching default partition fails if a default partition already --- exists -CREATE TABLE fail_def_part (LIKE part_1 INCLUDING CONSTRAINTS); -ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT; -ERROR: partition "fail_def_part" conflicts with existing default partition "def_part" -LINE 1: ...ER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT; - ^ --- check validation when attaching list partitions -CREATE TABLE list_parted2 ( - a int, - b char -) PARTITION BY LIST (a); --- check that violating rows are correctly reported -CREATE TABLE part_2 (LIKE list_parted2); -INSERT INTO part_2 VALUES (3, 'a'); -ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -ERROR: partition constraint of relation "part_2" is violated by some row --- should be ok after deleting the bad row -DELETE FROM part_2; -ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); --- check partition cannot be attached if default has some row for its values -CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT; -INSERT INTO list_parted2_def VALUES (11, 'z'); -CREATE TABLE part_3 (LIKE list_parted2); -ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); -ERROR: updated partition constraint for default partition "list_parted2_def" would be violated by some row --- should be ok after deleting the bad row -DELETE FROM list_parted2_def WHERE a = 11; -ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); --- adding constraints that describe the desired partition constraint --- (or more restrictive) will help skip the validation scan -CREATE TABLE part_3_4 ( - LIKE list_parted2, - CONSTRAINT check_a CHECK (a IN (3)) -); --- however, if a list partition does not accept nulls, there should be --- an explicit NOT NULL constraint on the partition key column for the --- validation scan to be skipped; -ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); --- adding a NOT NULL constraint will cause the scan to be skipped -ALTER TABLE list_parted2 DETACH PARTITION part_3_4; -ALTER TABLE part_3_4 ALTER a SET NOT NULL; -ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); --- check if default partition scan skipped -ALTER TABLE list_parted2_def ADD CONSTRAINT check_a CHECK (a IN (5, 6)); -CREATE TABLE part_55_66 PARTITION OF list_parted2 FOR VALUES IN (55, 66); --- check validation when attaching range partitions -CREATE TABLE range_parted ( - a int, - b int -) PARTITION BY RANGE (a, b); --- check that violating rows are correctly reported -CREATE TABLE part1 ( - a int NOT NULL CHECK (a = 1), - b int NOT NULL CHECK (b >= 1 AND b <= 10) -); -INSERT INTO part1 VALUES (1, 10); --- Remember the TO bound is exclusive -ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -ERROR: partition constraint of relation "part1" is violated by some row --- should be ok after deleting the bad row -DELETE FROM part1; -ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); --- adding constraints that describe the desired partition constraint --- (or more restrictive) will help skip the validation scan -CREATE TABLE part2 ( - a int NOT NULL CHECK (a = 1), - b int NOT NULL CHECK (b >= 10 AND b < 18) -); -ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20); --- Create default partition -CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT; --- Only one default partition is allowed, hence, following should give error -CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS); -ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT; -ERROR: partition "partr_def2" conflicts with existing default partition "partr_def1" -LINE 1: ...LTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT; - ^ --- Overlapping partitions cannot be attached, hence, following should give error -INSERT INTO partr_def1 VALUES (2, 10); -CREATE TABLE part3 (LIKE range_parted); -ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (2, 10) TO (2, 20); -ERROR: updated partition constraint for default partition "partr_def1" would be violated by some row --- Attaching partitions should be successful when there are no overlapping rows -ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20); --- check that leaf partitions are scanned when attaching a partitioned --- table -CREATE TABLE part_5 ( - LIKE list_parted2 -) PARTITION BY LIST (b); --- check that violating rows are correctly reported -CREATE TABLE part_5_a PARTITION OF part_5 FOR VALUES IN ('a'); -INSERT INTO part_5_a (a, b) VALUES (6, 'a'); -ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -ERROR: partition constraint of relation "part_5_a" is violated by some row --- delete the faulting row and also add a constraint to skip the scan -DELETE FROM part_5_a WHERE a NOT IN (3); -ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 5); -ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -ALTER TABLE list_parted2 DETACH PARTITION part_5; -ALTER TABLE part_5 DROP CONSTRAINT check_a; --- scan should again be skipped, even though NOT NULL is now a column property -ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IN (5)), ALTER a SET NOT NULL; -ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); --- Check the case where attnos of the partitioning columns in the table being --- attached differs from the parent. It should not affect the constraint- --- checking logic that allows to skip the scan. -CREATE TABLE part_6 ( - c int, - LIKE list_parted2, - CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 6) -); -ALTER TABLE part_6 DROP c; -ALTER TABLE list_parted2 ATTACH PARTITION part_6 FOR VALUES IN (6); --- Similar to above, but the table being attached is a partitioned table --- whose partition has still different attnos for the root partitioning --- columns. -CREATE TABLE part_7 ( - LIKE list_parted2, - CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) -) PARTITION BY LIST (b); -CREATE TABLE part_7_a_null ( - c int, - d int, - e int, - LIKE list_parted2, -- 'a' will have attnum = 4 - CONSTRAINT check_b CHECK (b IS NULL OR b = 'a'), - CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) -); -ALTER TABLE part_7_a_null DROP c, DROP d, DROP e; -ALTER TABLE part_7 ATTACH PARTITION part_7_a_null FOR VALUES IN ('a', null); -ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); --- Same example, but check this time that the constraint correctly detects --- violating rows -ALTER TABLE list_parted2 DETACH PARTITION part_7; -ALTER TABLE part_7 DROP CONSTRAINT check_a; -- thusly, scan won't be skipped -INSERT INTO part_7 (a, b) VALUES (8, null), (9, 'a'); -SELECT tableoid::regclass, a, b FROM part_7 order by a; - tableoid | a | b ----------------+---+--- - part_7_a_null | 8 | - part_7_a_null | 9 | a -(2 rows) - -ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); -ERROR: partition constraint of relation "part_7_a_null" is violated by some row --- check that leaf partitions of default partition are scanned when --- attaching a partitioned table. -ALTER TABLE part_5 DROP CONSTRAINT check_a; -CREATE TABLE part5_def PARTITION OF part_5 DEFAULT PARTITION BY LIST(a); -CREATE TABLE part5_def_p1 PARTITION OF part5_def FOR VALUES IN (5); -INSERT INTO part5_def_p1 VALUES (5, 'y'); -CREATE TABLE part5_p1 (LIKE part_5); -ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); -ERROR: updated partition constraint for default partition "part5_def_p1" would be violated by some row --- should be ok after deleting the bad row -DELETE FROM part5_def_p1 WHERE b = 'y'; -ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); --- check that the table being attached is not already a partition -ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -ERROR: "part_2" is already a partition --- check that circular inheritance is not allowed -ALTER TABLE part_5 ATTACH PARTITION list_parted2 FOR VALUES IN ('b'); -ERROR: circular inheritance not allowed -DETAIL: "part_5" is already a child of "list_parted2". -ALTER TABLE list_parted2 ATTACH PARTITION list_parted2 FOR VALUES IN (0); -ERROR: circular inheritance not allowed -DETAIL: "list_parted2" is already a child of "list_parted2". --- If a partitioned table being created or an existing table being attached --- as a partition does not have a constraint that would allow validation scan --- to be skipped, but an individual partition does, then the partition's --- validation scan is skipped. -CREATE TABLE quuux (a int, b text) PARTITION BY LIST (a); -CREATE TABLE quuux_default PARTITION OF quuux DEFAULT PARTITION BY LIST (b); -CREATE TABLE quuux_default1 PARTITION OF quuux_default ( - CONSTRAINT check_1 CHECK (a IS NOT NULL AND a = 1) -) FOR VALUES IN ('b'); -CREATE TABLE quuux1 (a int, b text); -ALTER TABLE quuux ATTACH PARTITION quuux1 FOR VALUES IN (1); -- validate! -CREATE TABLE quuux2 (a int, b text); -ALTER TABLE quuux ATTACH PARTITION quuux2 FOR VALUES IN (2); -- skip validation -DROP TABLE quuux1, quuux2; --- should validate for quuux1, but not for quuux2 -CREATE TABLE quuux1 PARTITION OF quuux FOR VALUES IN (1); -CREATE TABLE quuux2 PARTITION OF quuux FOR VALUES IN (2); -DROP TABLE quuux; --- check validation when attaching hash partitions --- Use hand-rolled hash functions and operator class to get predictable result --- on different machines. part_test_int4_ops is defined in insert.sql. --- check that the new partition won't overlap with an existing partition -CREATE TABLE hash_parted ( - a int, - b int -) PARTITION BY HASH (a part_test_int4_ops); -CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 0); -CREATE TABLE fail_part (LIKE hpart_1); -ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4); -ERROR: partition "fail_part" would overlap partition "hpart_1" -LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU... - ^ -ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0); -ERROR: partition "fail_part" would overlap partition "hpart_1" -LINE 1: ...hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODU... - ^ -DROP TABLE fail_part; --- check validation when attaching hash partitions --- check that violating rows are correctly reported -CREATE TABLE hpart_2 (LIKE hash_parted); -INSERT INTO hpart_2 VALUES (3, 0); -ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); -ERROR: partition constraint of relation "hpart_2" is violated by some row --- should be ok after deleting the bad row -DELETE FROM hpart_2; -ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); --- check that leaf partitions are scanned when attaching a partitioned --- table -CREATE TABLE hpart_5 ( - LIKE hash_parted -) PARTITION BY LIST (b); --- check that violating rows are correctly reported -CREATE TABLE hpart_5_a PARTITION OF hpart_5 FOR VALUES IN ('1', '2', '3'); -INSERT INTO hpart_5_a (a, b) VALUES (7, 1); -ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); -ERROR: partition constraint of relation "hpart_5_a" is violated by some row --- should be ok after deleting the bad row -DELETE FROM hpart_5_a; -ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); --- check that the table being attach is with valid modulus and remainder value -CREATE TABLE fail_part(LIKE hash_parted); -ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 0, REMAINDER 1); -ERROR: modulus for hash partition must be an integer value greater than zero -ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 8); -ERROR: remainder for hash partition must be less than modulus -ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 3, REMAINDER 2); -ERROR: every hash partition modulus must be a factor of the next larger modulus -DETAIL: The new modulus 3 is not a factor of 4, the modulus of existing partition "hpart_1". -DROP TABLE fail_part; --- --- DETACH PARTITION --- --- check that the table is partitioned at all -CREATE TABLE regular_table (a int); -ALTER TABLE regular_table DETACH PARTITION any_name; -ERROR: table "regular_table" is not partitioned -DROP TABLE regular_table; --- check that the partition being detached exists at all -ALTER TABLE list_parted2 DETACH PARTITION part_4; -ERROR: relation "part_4" does not exist -ALTER TABLE hash_parted DETACH PARTITION hpart_4; -ERROR: relation "hpart_4" does not exist --- check that the partition being detached is actually a partition of the parent -CREATE TABLE not_a_part (a int); -ALTER TABLE list_parted2 DETACH PARTITION not_a_part; -ERROR: relation "not_a_part" is not a partition of relation "list_parted2" -ALTER TABLE list_parted2 DETACH PARTITION part_1; -ERROR: relation "part_1" is not a partition of relation "list_parted2" -ALTER TABLE hash_parted DETACH PARTITION not_a_part; -ERROR: relation "not_a_part" is not a partition of relation "hash_parted" -DROP TABLE not_a_part; --- check that, after being detached, attinhcount/coninhcount is dropped to 0 and --- attislocal/conislocal is set to true -ALTER TABLE list_parted2 DETACH PARTITION part_3_4; -SELECT attinhcount, attislocal FROM pg_attribute WHERE attrelid = 'part_3_4'::regclass AND attnum > 0; - attinhcount | attislocal --------------+------------ - 0 | t - 0 | t -(2 rows) - -SELECT coninhcount, conislocal FROM pg_constraint WHERE conrelid = 'part_3_4'::regclass AND conname = 'check_a'; - coninhcount | conislocal --------------+------------ - 0 | t -(1 row) - -DROP TABLE part_3_4; --- check that a detached partition is not dropped on dropping a partitioned table -CREATE TABLE range_parted2 ( - a int -) PARTITION BY RANGE(a); -CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) to (100); -ALTER TABLE range_parted2 DETACH PARTITION part_rp; -DROP TABLE range_parted2; -SELECT * from part_rp; - a ---- -(0 rows) - -DROP TABLE part_rp; --- concurrent detach -CREATE TABLE range_parted2 ( - a int -) PARTITION BY RANGE(a); -CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) to (100); -BEGIN; --- doesn't work in a partition block -ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY; -ERROR: ALTER TABLE ... DETACH CONCURRENTLY cannot run inside a transaction block -COMMIT; -CREATE TABLE part_rpd PARTITION OF range_parted2 DEFAULT; --- doesn't work if there's a default partition -ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY; -ERROR: cannot detach partitions concurrently when a default partition exists --- doesn't work for the default partition -ALTER TABLE range_parted2 DETACH PARTITION part_rpd CONCURRENTLY; -ERROR: cannot detach partitions concurrently when a default partition exists -DROP TABLE part_rpd; --- works fine -ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY; -\d+ range_parted2 - Partitioned table "public.range_parted2" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - a | integer | | | | plain | | -Partition key: RANGE (a) -Number of partitions: 0 - --- constraint should be created -\d part_rp - Table "public.part_rp" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Check constraints: - "part_rp_a_check" CHECK (a IS NOT NULL AND a >= 0 AND a < 100) - -CREATE TABLE part_rp100 PARTITION OF range_parted2 (CHECK (a>=123 AND a<133 AND a IS NOT NULL)) FOR VALUES FROM (100) to (200); -ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY; --- redundant constraint should not be created -\d part_rp100 - Table "public.part_rp100" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Check constraints: - "part_rp100_a_check" CHECK (a >= 123 AND a < 133 AND a IS NOT NULL) - -DROP TABLE range_parted2; --- Check ALTER TABLE commands for partitioned tables and partitions --- cannot add/drop column to/from *only* the parent -ALTER TABLE ONLY list_parted2 ADD COLUMN c int; -ERROR: column must be added to child tables too -ALTER TABLE ONLY list_parted2 DROP COLUMN b; -ERROR: cannot drop column from only the partitioned table when partitions exist -HINT: Do not specify the ONLY keyword. --- cannot add a column to partition or drop an inherited one -ALTER TABLE part_2 ADD COLUMN c text; -ERROR: cannot add column to a partition -ALTER TABLE part_2 DROP COLUMN b; -ERROR: cannot drop inherited column "b" --- Nor rename, alter type -ALTER TABLE part_2 RENAME COLUMN b to c; -ERROR: cannot rename inherited column "b" -ALTER TABLE part_2 ALTER COLUMN b TYPE text; -ERROR: cannot alter inherited column "b" --- cannot add/drop NOT NULL or check constraints to *only* the parent, when --- partitions exist -ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL; -ERROR: constraint must be added to child tables too -DETAIL: Column "b" of relation "part_2" is not already NOT NULL. -HINT: Do not specify the ONLY keyword. -ALTER TABLE ONLY list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); -ERROR: constraint must be added to child tables too -ALTER TABLE list_parted2 ALTER b SET NOT NULL; -ALTER TABLE ONLY list_parted2 ALTER b DROP NOT NULL; -ERROR: cannot remove constraint from only the partitioned table when partitions exist -HINT: Do not specify the ONLY keyword. -ALTER TABLE list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); -ALTER TABLE ONLY list_parted2 DROP CONSTRAINT check_b; -ERROR: cannot remove constraint from only the partitioned table when partitions exist -HINT: Do not specify the ONLY keyword. --- It's alright though, if no partitions are yet created -CREATE TABLE parted_no_parts (a int) PARTITION BY LIST (a); -ALTER TABLE ONLY parted_no_parts ALTER a SET NOT NULL; -ALTER TABLE ONLY parted_no_parts ADD CONSTRAINT check_a CHECK (a > 0); -ALTER TABLE ONLY parted_no_parts ALTER a DROP NOT NULL; -ALTER TABLE ONLY parted_no_parts DROP CONSTRAINT check_a; -DROP TABLE parted_no_parts; --- cannot drop inherited NOT NULL or check constraints from partition -ALTER TABLE list_parted2 ALTER b SET NOT NULL, ADD CONSTRAINT check_a2 CHECK (a > 0); -ALTER TABLE part_2 ALTER b DROP NOT NULL; -ERROR: column "b" is marked NOT NULL in parent table -ALTER TABLE part_2 DROP CONSTRAINT check_a2; -ERROR: cannot drop inherited constraint "check_a2" of relation "part_2" --- Doesn't make sense to add NO INHERIT constraints on partitioned tables -ALTER TABLE list_parted2 add constraint check_b2 check (b <> 'zz') NO INHERIT; -ERROR: cannot add NO INHERIT constraint to partitioned table "list_parted2" --- check that a partition cannot participate in regular inheritance -CREATE TABLE inh_test () INHERITS (part_2); -ERROR: cannot inherit from partition "part_2" -CREATE TABLE inh_test (LIKE part_2); -ALTER TABLE inh_test INHERIT part_2; -ERROR: cannot inherit from a partition -ALTER TABLE part_2 INHERIT inh_test; -ERROR: cannot change inheritance of a partition --- cannot drop or alter type of partition key columns of lower level --- partitioned tables; for example, part_5, which is list_parted2's --- partition, is partitioned on b; -ALTER TABLE list_parted2 DROP COLUMN b; -ERROR: cannot drop column "b" because it is part of the partition key of relation "part_5" -ALTER TABLE list_parted2 ALTER COLUMN b TYPE text; -ERROR: cannot alter column "b" because it is part of the partition key of relation "part_5" --- dropping non-partition key columns should be allowed on the parent table. -ALTER TABLE list_parted DROP COLUMN b; -SELECT * FROM list_parted; - a ---- -(0 rows) - --- cleanup -DROP TABLE list_parted, list_parted2, range_parted; -DROP TABLE fail_def_part; -DROP TABLE hash_parted; --- more tests for certain multi-level partitioning scenarios -create table p (a int, b int) partition by range (a, b); -create table p1 (b int, a int not null) partition by range (b); -create table p11 (like p1); -alter table p11 drop a; -alter table p11 add a int; -alter table p11 drop a; -alter table p11 add a int not null; --- attnum for key attribute 'a' is different in p, p1, and p11 -select attrelid::regclass, attname, attnum -from pg_attribute -where attname = 'a' - and (attrelid = 'p'::regclass - or attrelid = 'p1'::regclass - or attrelid = 'p11'::regclass) -order by attrelid::regclass::text; - attrelid | attname | attnum -----------+---------+-------- - p | a | 1 - p1 | a | 2 - p11 | a | 4 -(3 rows) - -alter table p1 attach partition p11 for values from (2) to (5); -insert into p1 (a, b) values (2, 3); --- check that partition validation scan correctly detects violating rows -alter table p attach partition p1 for values from (1, 2) to (1, 10); -ERROR: partition constraint of relation "p11" is violated by some row --- cleanup -drop table p; -drop table p1; --- validate constraint on partitioned tables should only scan leaf partitions -create table parted_validate_test (a int) partition by list (a); -create table parted_validate_test_1 partition of parted_validate_test for values in (0, 1); -alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid; -alter table parted_validate_test validate constraint parted_validate_test_chka; -drop table parted_validate_test; --- test alter column options -CREATE TABLE attmp(i integer); -INSERT INTO attmp VALUES (1); -ALTER TABLE attmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2); -ALTER TABLE attmp ALTER COLUMN i RESET (n_distinct_inherited); -ANALYZE attmp; -DROP TABLE attmp; -DROP USER regress_alter_table_user1; --- check that violating rows are correctly reported when attaching as the --- default partition -create table defpart_attach_test (a int) partition by list (a); -create table defpart_attach_test1 partition of defpart_attach_test for values in (1); -create table defpart_attach_test_d (b int, a int); -alter table defpart_attach_test_d drop b; -insert into defpart_attach_test_d values (1), (2); --- error because its constraint as the default partition would be violated --- by the row containing 1 -alter table defpart_attach_test attach partition defpart_attach_test_d default; -ERROR: partition constraint of relation "defpart_attach_test_d" is violated by some row -delete from defpart_attach_test_d where a = 1; -alter table defpart_attach_test_d add check (a > 1); --- should be attached successfully and without needing to be scanned -alter table defpart_attach_test attach partition defpart_attach_test_d default; --- check that attaching a partition correctly reports any rows in the default --- partition that should not be there for the new partition to be attached --- successfully -create table defpart_attach_test_2 (like defpart_attach_test_d); -alter table defpart_attach_test attach partition defpart_attach_test_2 for values in (2); -ERROR: updated partition constraint for default partition "defpart_attach_test_d" would be violated by some row -drop table defpart_attach_test; --- check combinations of temporary and permanent relations when attaching --- partitions. -create table perm_part_parent (a int) partition by list (a); -create temp table temp_part_parent (a int) partition by list (a); -create table perm_part_child (a int); -create temp table temp_part_child (a int); -alter table temp_part_parent attach partition perm_part_child default; -- error -ERROR: cannot attach a permanent relation as partition of temporary relation "temp_part_parent" -alter table perm_part_parent attach partition temp_part_child default; -- error -ERROR: cannot attach a temporary relation as partition of permanent relation "perm_part_parent" -alter table temp_part_parent attach partition temp_part_child default; -- ok -drop table perm_part_parent cascade; -drop table temp_part_parent cascade; --- check that attaching partitions to a table while it is being used is --- prevented -create table tab_part_attach (a int) partition by list (a); -create or replace function func_part_attach() returns trigger - language plpgsql as $$ - begin - execute 'create table tab_part_attach_1 (a int)'; - execute 'alter table tab_part_attach attach partition tab_part_attach_1 for values in (1)'; - return null; - end $$; -create trigger trig_part_attach before insert on tab_part_attach - for each statement execute procedure func_part_attach(); -insert into tab_part_attach values (1); -ERROR: cannot ALTER TABLE "tab_part_attach" because it is being used by active queries in this session -CONTEXT: SQL statement "alter table tab_part_attach attach partition tab_part_attach_1 for values in (1)" -PL/pgSQL function func_part_attach() line 4 at EXECUTE -drop table tab_part_attach; -drop function func_part_attach(); --- test case where the partitioning operator is a SQL function whose --- evaluation results in the table's relcache being rebuilt partway through --- the execution of an ATTACH PARTITION command -create function at_test_sql_partop (int4, int4) returns int language sql -as $$ select case when $1 = $2 then 0 when $1 > $2 then 1 else -1 end; $$; -create operator class at_test_sql_partop for type int4 using btree as - operator 1 < (int4, int4), operator 2 <= (int4, int4), - operator 3 = (int4, int4), operator 4 >= (int4, int4), - operator 5 > (int4, int4), function 1 at_test_sql_partop(int4, int4); -create table at_test_sql_partop (a int) partition by range (a at_test_sql_partop); -create table at_test_sql_partop_1 (a int); -alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values from (0) to (10); -drop table at_test_sql_partop; -drop operator class at_test_sql_partop using btree; -drop function at_test_sql_partop; -/* Test case for bug #16242 */ --- We create a parent and child where the child has missing --- non-null attribute values, and arrange to pass them through --- tuple conversion from the child to the parent tupdesc -create table bar1 (a integer, b integer not null default 1) - partition by range (a); -create table bar2 (a integer); -insert into bar2 values (1); -alter table bar2 add column b integer not null default 1; --- (at this point bar2 contains tuple with natts=1) -alter table bar1 attach partition bar2 default; --- this works: -select * from bar1; - a | b ----+--- - 1 | 1 -(1 row) - --- this exercises tuple conversion: -create function xtrig() - returns trigger language plpgsql -as $$ - declare - r record; - begin - for r in select * from old loop - raise info 'a=%, b=%', r.a, r.b; - end loop; - return NULL; - end; -$$; -create trigger xtrig - after update on bar1 - referencing old table as old - for each statement execute procedure xtrig(); -update bar1 set a = a + 1; -INFO: a=1, b=1 -/* End test case for bug #16242 */ --- Test that ALTER TABLE rewrite preserves a clustered index --- for normal indexes and indexes on constraints. -create table alttype_cluster (a int); -alter table alttype_cluster add primary key (a); -create index alttype_cluster_ind on alttype_cluster (a); -alter table alttype_cluster cluster on alttype_cluster_ind; --- Normal index remains clustered. -select indexrelid::regclass, indisclustered from pg_index - where indrelid = 'alttype_cluster'::regclass - order by indexrelid::regclass::text; - indexrelid | indisclustered -----------------------+---------------- - alttype_cluster_ind | t - alttype_cluster_pkey | f -(2 rows) - -alter table alttype_cluster alter a type bigint; -select indexrelid::regclass, indisclustered from pg_index - where indrelid = 'alttype_cluster'::regclass - order by indexrelid::regclass::text; - indexrelid | indisclustered -----------------------+---------------- - alttype_cluster_ind | t - alttype_cluster_pkey | f -(2 rows) - --- Constraint index remains clustered. -alter table alttype_cluster cluster on alttype_cluster_pkey; -select indexrelid::regclass, indisclustered from pg_index - where indrelid = 'alttype_cluster'::regclass - order by indexrelid::regclass::text; - indexrelid | indisclustered -----------------------+---------------- - alttype_cluster_ind | f - alttype_cluster_pkey | t -(2 rows) - -alter table alttype_cluster alter a type int; -select indexrelid::regclass, indisclustered from pg_index - where indrelid = 'alttype_cluster'::regclass - order by indexrelid::regclass::text; - indexrelid | indisclustered -----------------------+---------------- - alttype_cluster_ind | f - alttype_cluster_pkey | t -(2 rows) - -drop table alttype_cluster; --- --- Check that attaching or detaching a partitioned partition correctly leads --- to its partitions' constraint being updated to reflect the parent's --- newly added/removed constraint -create table target_parted (a int, b int) partition by list (a); -create table attach_parted (a int, b int) partition by list (b); -create table attach_parted_part1 partition of attach_parted for values in (1); --- insert a row directly into the leaf partition so that its partition --- constraint is built and stored in the relcache -insert into attach_parted_part1 values (1, 1); --- the following better invalidate the partition constraint of the leaf --- partition too... -alter table target_parted attach partition attach_parted for values in (1); --- ...such that the following insert fails -insert into attach_parted_part1 values (2, 1); -ERROR: new row for relation "attach_parted_part1" violates partition constraint -DETAIL: Failing row contains (2, 1). --- ...and doesn't when the partition is detached along with its own partition -alter table target_parted detach partition attach_parted; -insert into attach_parted_part1 values (2, 1); +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sequence.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sequence.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/sequence.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/sequence.out 2021-10-30 22:09:24 -0600 @@ -331,495 +331,10 @@ (1 row) ALTER SEQUENCE sequence_test2 RESTART; -SELECT nextval('sequence_test2'); - nextval ---------- - 32 -(1 row) - -ALTER SEQUENCE sequence_test2 RESTART WITH 0; -- error -ERROR: RESTART value (0) cannot be less than MINVALUE (1) -ALTER SEQUENCE sequence_test4 RESTART WITH 40; -- error -ERROR: RESTART value (40) cannot be greater than MAXVALUE (-1) --- test CYCLE and NO CYCLE -ALTER SEQUENCE sequence_test2 RESTART WITH 24 - INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; -SELECT nextval('sequence_test2'); - nextval ---------- - 24 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 28 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 32 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 36 -(1 row) - -SELECT nextval('sequence_test2'); -- cycled - nextval ---------- - 5 -(1 row) - -ALTER SEQUENCE sequence_test2 RESTART WITH 24 - NO CYCLE; -SELECT nextval('sequence_test2'); - nextval ---------- - 24 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 28 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 32 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - 36 -(1 row) - -SELECT nextval('sequence_test2'); -- error -ERROR: nextval: reached maximum value of sequence "sequence_test2" (36) -ALTER SEQUENCE sequence_test2 RESTART WITH -24 START WITH -24 - INCREMENT BY -4 MINVALUE -36 MAXVALUE -5 CYCLE; -SELECT nextval('sequence_test2'); - nextval ---------- - -24 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -28 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -32 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -36 -(1 row) - -SELECT nextval('sequence_test2'); -- cycled - nextval ---------- - -5 -(1 row) - -ALTER SEQUENCE sequence_test2 RESTART WITH -24 - NO CYCLE; -SELECT nextval('sequence_test2'); - nextval ---------- - -24 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -28 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -32 -(1 row) - -SELECT nextval('sequence_test2'); - nextval ---------- - -36 -(1 row) - -SELECT nextval('sequence_test2'); -- error -ERROR: nextval: reached minimum value of sequence "sequence_test2" (-36) --- reset -ALTER SEQUENCE IF EXISTS sequence_test2 RESTART WITH 32 START WITH 32 - INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; -SELECT setval('sequence_test2', -100); -- error -ERROR: setval: value -100 is out of bounds for sequence "sequence_test2" (5..36) -SELECT setval('sequence_test2', 100); -- error -ERROR: setval: value 100 is out of bounds for sequence "sequence_test2" (5..36) -SELECT setval('sequence_test2', 5); - setval --------- - 5 -(1 row) - -CREATE SEQUENCE sequence_test3; -- not read from, to test is_called --- Information schema -SELECT * FROM information_schema.sequences - WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) - ORDER BY sequence_name ASC; - sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option -------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+----------------------+---------------------+-----------+-------------- - regression | public | sequence_test10 | smallint | 16 | 2 | 0 | 1 | -20000 | 32767 | 1 | NO - regression | public | sequence_test11 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO - regression | public | sequence_test12 | integer | 32 | 2 | 0 | -1 | -2147483648 | -1 | -1 | NO - regression | public | sequence_test13 | integer | 32 | 2 | 0 | -32768 | -2147483648 | 2147483647 | 1 | NO - regression | public | sequence_test14 | integer | 32 | 2 | 0 | 32767 | -2147483648 | 2147483647 | -1 | NO - regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO - regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO - regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | sequence_test8 | smallint | 16 | 2 | 0 | 1 | 1 | 20000 | 1 | NO - regression | public | sequence_test9 | smallint | 16 | 2 | 0 | -1 | -32768 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO - regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO - regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO - regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO - regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(19 rows) - -SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value -FROM pg_sequences -WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) - ORDER BY sequencename ASC; - schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value -------------+--------------------+-------------+----------------------+---------------------+--------------+-------+------------+------------ - public | sequence_test10 | 1 | -20000 | 32767 | 1 | f | 1 | - public | sequence_test11 | 1 | 1 | 2147483647 | 1 | f | 1 | - public | sequence_test12 | -1 | -2147483648 | -1 | -1 | f | 1 | - public | sequence_test13 | -32768 | -2147483648 | 2147483647 | 1 | f | 1 | - public | sequence_test14 | 32767 | -2147483648 | 2147483647 | -1 | f | 1 | - public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 - public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | - public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | - public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | sequence_test8 | 1 | 1 | 20000 | 1 | f | 1 | - public | sequence_test9 | -1 | -32768 | -1 | -1 | f | 1 | - public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 - public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(19 rows) - -SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type --------------+----------------------+---------------+-----------+--------------+------------+----------- - -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 -(1 row) - -\d sequence_test4 - Sequence "public.sequence_test4" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ---------+-------+----------------------+---------+-----------+---------+------- - bigint | -1 | -9223372036854775808 | -1 | -1 | no | 1 - -\d serialtest2_f2_seq - Sequence "public.serialtest2_f2_seq" - Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+------------+-----------+---------+------- - integer | 1 | 1 | 2147483647 | 1 | no | 1 -Owned by: public.serialtest2.f2 - --- Test comments -COMMENT ON SEQUENCE asdf IS 'won''t work'; -ERROR: relation "asdf" does not exist -COMMENT ON SEQUENCE sequence_test2 IS 'will work'; -COMMENT ON SEQUENCE sequence_test2 IS NULL; --- Test lastval() -CREATE SEQUENCE seq; -SELECT nextval('seq'); - nextval ---------- - 1 -(1 row) - -SELECT lastval(); - lastval ---------- - 1 -(1 row) - -SELECT setval('seq', 99); - setval --------- - 99 -(1 row) - -SELECT lastval(); - lastval ---------- - 99 -(1 row) - -DISCARD SEQUENCES; -SELECT lastval(); -ERROR: lastval is not yet defined in this session -CREATE SEQUENCE seq2; -SELECT nextval('seq2'); - nextval ---------- - 1 -(1 row) - -SELECT lastval(); - lastval ---------- - 1 -(1 row) - -DROP SEQUENCE seq2; --- should fail -SELECT lastval(); -ERROR: lastval is not yet defined in this session --- Test sequences in read-only transactions -CREATE TEMPORARY SEQUENCE sequence_test_temp1; -START TRANSACTION READ ONLY; -SELECT nextval('sequence_test_temp1'); -- ok - nextval ---------- - 1 -(1 row) - -SELECT nextval('sequence_test2'); -- error -ERROR: cannot execute nextval() in a read-only transaction -ROLLBACK; -START TRANSACTION READ ONLY; -SELECT setval('sequence_test_temp1', 1); -- ok - setval --------- - 1 -(1 row) - -SELECT setval('sequence_test2', 1); -- error -ERROR: cannot execute setval() in a read-only transaction -ROLLBACK; --- privileges tests -CREATE USER regress_seq_user; --- nextval -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT SELECT ON seq3 TO regress_seq_user; -SELECT nextval('seq3'); -ERROR: permission denied for sequence seq3 -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT UPDATE ON seq3 TO regress_seq_user; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT USAGE ON seq3 TO regress_seq_user; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -ROLLBACK; --- currval -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT SELECT ON seq3 TO regress_seq_user; -SELECT currval('seq3'); - currval ---------- - 1 -(1 row) - -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT UPDATE ON seq3 TO regress_seq_user; -SELECT currval('seq3'); -ERROR: permission denied for sequence seq3 -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT USAGE ON seq3 TO regress_seq_user; -SELECT currval('seq3'); - currval ---------- - 1 -(1 row) - -ROLLBACK; --- lastval -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT SELECT ON seq3 TO regress_seq_user; -SELECT lastval(); - lastval ---------- - 1 -(1 row) - -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT UPDATE ON seq3 TO regress_seq_user; -SELECT lastval(); -ERROR: permission denied for sequence seq3 -ROLLBACK; -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -SELECT nextval('seq3'); - nextval ---------- - 1 -(1 row) - -REVOKE ALL ON seq3 FROM regress_seq_user; -GRANT USAGE ON seq3 TO regress_seq_user; -SELECT lastval(); - lastval ---------- - 1 -(1 row) - -ROLLBACK; --- setval -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -CREATE SEQUENCE seq3; -REVOKE ALL ON seq3 FROM regress_seq_user; -SAVEPOINT save; -SELECT setval('seq3', 5); -ERROR: permission denied for sequence seq3 -ROLLBACK TO save; -GRANT UPDATE ON seq3 TO regress_seq_user; -SELECT setval('seq3', 5); - setval --------- - 5 -(1 row) - -SELECT nextval('seq3'); - nextval ---------- - 6 -(1 row) - -ROLLBACK; --- ALTER SEQUENCE -BEGIN; -SET LOCAL SESSION AUTHORIZATION regress_seq_user; -ALTER SEQUENCE sequence_test2 START WITH 1; -ERROR: must be owner of sequence sequence_test2 -ROLLBACK; --- Sequences should get wiped out as well: -DROP TABLE serialTest1, serialTest2; --- Make sure sequences are gone: -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') - ORDER BY sequence_name ASC; - sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option -------------------+-----------------+----------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------+-----------+-------------- - regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES -(1 row) - -DROP USER regress_seq_user; -DROP SEQUENCE seq; --- cache tests -CREATE SEQUENCE test_seq1 CACHE 10; -SELECT nextval('test_seq1'); - nextval ---------- - 1 -(1 row) - -SELECT nextval('test_seq1'); - nextval ---------- - 2 -(1 row) - -SELECT nextval('test_seq1'); - nextval ---------- - 3 -(1 row) - -DROP SEQUENCE test_seq1; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/polymorphism.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/polymorphism.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/polymorphism.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/polymorphism.out 2021-10-30 22:09:24 -0600 @@ -159,1940 +159,10 @@ select x, pg_typeof(x), y, pg_typeof(y) from polyf(11, array[1, 2], point(1,2), point(3,4)); - x | pg_typeof | y | pg_typeof -----------+-----------+-------------------+----------- - {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x), y, pg_typeof(y) - from polyf(11, '{1,2}', point(1,2), '(3,4)'); - x | pg_typeof | y | pg_typeof -----------+-----------+-------------------+----------- - {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x), y, pg_typeof(y) - from polyf(11, array[1, 2.2], 42, 34.5); -- fail -ERROR: function polyf(integer, numeric[], integer, numeric) does not exist -LINE 2: from polyf(11, array[1, 2.2], 42, 34.5); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function polyf(a anyelement, b anyarray, - c anycompatible, d anycompatible); -create function polyf(anyrange) returns anymultirange -as 'select multirange($1);' language sql; -select polyf(int4range(1,10)); - polyf ----------- - {[1,10)} -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type because input has type unknown -drop function polyf(anyrange); -create function polyf(anymultirange) returns anyelement -as 'select lower($1);' language sql; -select polyf(int4multirange(int4range(1,10), int4range(20,30))); - polyf -------- - 1 -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type because input has type unknown -drop function polyf(anymultirange); -create function polyf(anycompatiblerange) returns anycompatiblemultirange -as 'select multirange($1);' language sql; -select polyf(int4range(1,10)); - polyf ----------- - {[1,10)} -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type anycompatiblerange because input has type unknown -drop function polyf(anycompatiblerange); -create function polyf(anymultirange) returns anyrange -as 'select range_merge($1);' language sql; -select polyf(int4multirange(int4range(1,10), int4range(20,30))); - polyf --------- - [1,30) -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type because input has type unknown -drop function polyf(anymultirange); -create function polyf(anycompatiblemultirange) returns anycompatiblerange -as 'select range_merge($1);' language sql; -select polyf(int4multirange(int4range(1,10), int4range(20,30))); - polyf --------- - [1,30) -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type anycompatiblerange because input has type unknown -drop function polyf(anycompatiblemultirange); -create function polyf(anycompatiblemultirange) returns anycompatible -as 'select lower($1);' language sql; -select polyf(int4multirange(int4range(1,10), int4range(20,30))); - polyf -------- - 1 -(1 row) - -select polyf(null); -ERROR: could not determine polymorphic type anycompatiblemultirange because input has type unknown -drop function polyf(anycompatiblemultirange); --- --- Polymorphic aggregate tests --- --- Legend: ------------ --- A = type is ANY --- P = type is polymorphic --- N = type is non-polymorphic --- B = aggregate base type --- S = aggregate state type --- R = aggregate return type --- 1 = arg1 of a function --- 2 = arg2 of a function --- ag = aggregate --- tf = trans (state) function --- ff = final function --- rt = return type of a function --- -> = implies --- => = allowed --- !> = not allowed --- E = exists --- NE = not-exists --- --- Possible states: --- ---------------- --- B = (A || P || N) --- when (B = A) -> (tf2 = NE) --- S = (P || N) --- ff = (E || NE) --- tf1 = (P || N) --- tf2 = (NE || P || N) --- R = (P || N) --- create functions for use as tf and ff with the needed combinations of --- argument polymorphism, but within the constraints of valid aggregate --- functions, i.e. tf arg1 and tf return type must match --- polymorphic single arg transfn -CREATE FUNCTION stfp(anyarray) RETURNS anyarray AS -'select $1' LANGUAGE SQL; --- non-polymorphic single arg transfn -CREATE FUNCTION stfnp(int[]) RETURNS int[] AS -'select $1' LANGUAGE SQL; --- dual polymorphic transfn -CREATE FUNCTION tfp(anyarray,anyelement) RETURNS anyarray AS -'select $1 || $2' LANGUAGE SQL; --- dual non-polymorphic transfn -CREATE FUNCTION tfnp(int[],int) RETURNS int[] AS -'select $1 || $2' LANGUAGE SQL; --- arg1 only polymorphic transfn -CREATE FUNCTION tf1p(anyarray,int) RETURNS anyarray AS -'select $1' LANGUAGE SQL; --- arg2 only polymorphic transfn -CREATE FUNCTION tf2p(int[],anyelement) RETURNS int[] AS -'select $1' LANGUAGE SQL; --- multi-arg polymorphic -CREATE FUNCTION sum3(anyelement,anyelement,anyelement) returns anyelement AS -'select $1+$2+$3' language sql strict; --- finalfn polymorphic -CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS -'select $1' LANGUAGE SQL; --- finalfn non-polymorphic -CREATE FUNCTION ffnp(int[]) returns int[] as -'select $1' LANGUAGE SQL; --- Try to cover all the possible states: --- --- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn --- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp, --- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to --- return N. Therefore, if the transfn is stfp, tfp, or tf1p, we must use ffnp --- as finalfn, because stfp, tfp, and tf1p do not return N. --- --- Case1 (R = P) && (B = A) --- ------------------------ --- S tf1 --- ------- --- N N --- should CREATE -CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[], - FINALFUNC = ffp, INITCOND = '{}'); --- P N --- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) -CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- N P --- should CREATE -CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[], - FINALFUNC = ffp, INITCOND = '{}'); -CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[], - INITCOND = '{}'); --- P P --- should ERROR: we have no way to resolve S -CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- Case2 (R = P) && ((B = P) || (B = N)) --- ------------------------------------- --- S tf1 B tf2 --- ----------------------- --- N N N N --- should CREATE -CREATE AGGREGATE myaggp05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); --- N N N P --- should CREATE -CREATE AGGREGATE myaggp06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); --- N N P N --- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) -CREATE AGGREGATE myaggp07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tfnp(integer[], anyelement) does not exist --- N N P P --- should CREATE -CREATE AGGREGATE myaggp08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); --- N P N N --- should CREATE -CREATE AGGREGATE myaggp09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); -CREATE AGGREGATE myaggp09b(BASETYPE = int, SFUNC = tf1p, STYPE = int[], - INITCOND = '{}'); --- N P N P --- should CREATE -CREATE AGGREGATE myaggp10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); -CREATE AGGREGATE myaggp10b(BASETYPE = int, SFUNC = tfp, STYPE = int[], - INITCOND = '{}'); --- N P P N --- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) -CREATE AGGREGATE myaggp11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tf1p(integer[], anyelement) does not exist -CREATE AGGREGATE myaggp11b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], - INITCOND = '{}'); -ERROR: function tf1p(integer[], anyelement) does not exist --- N P P P --- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) -CREATE AGGREGATE myaggp12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tfp(integer[], anyelement) does not exist -CREATE AGGREGATE myaggp12b(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], - INITCOND = '{}'); -ERROR: function tfp(integer[], anyelement) does not exist --- P N N N --- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) -CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P N N P --- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) -CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P N P N --- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) -CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp, - STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tfnp(anyarray, anyelement) does not exist --- P N P P --- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) -CREATE AGGREGATE myaggp16a(BASETYPE = anyelement, SFUNC = tf2p, - STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tf2p(anyarray, anyelement) does not exist --- P P N N --- should ERROR: we have no way to resolve S -CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P P N P --- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) -CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, - FINALFUNC = ffp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P P P N --- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) -CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p, - STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -ERROR: function tf1p(anyarray, anyelement) does not exist -CREATE AGGREGATE myaggp19b(BASETYPE = anyelement, SFUNC = tf1p, - STYPE = anyarray, INITCOND = '{}'); -ERROR: function tf1p(anyarray, anyelement) does not exist --- P P P P --- should CREATE -CREATE AGGREGATE myaggp20a(BASETYPE = anyelement, SFUNC = tfp, - STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp, - STYPE = anyarray, INITCOND = '{}'); --- Case3 (R = N) && (B = A) --- ------------------------ --- S tf1 --- ------- --- N N --- should CREATE -CREATE AGGREGATE myaggn01a(*) (SFUNC = stfnp, STYPE = int4[], - FINALFUNC = ffnp, INITCOND = '{}'); -CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[], - INITCOND = '{}'); --- P N --- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) -CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- N P --- should CREATE -CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[], - FINALFUNC = ffnp, INITCOND = '{}'); --- P P --- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) -CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- Case4 (R = N) && ((B = P) || (B = N)) --- ------------------------------------- --- S tf1 B tf2 --- ----------------------- --- N N N N --- should CREATE -CREATE AGGREGATE myaggn05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -CREATE AGGREGATE myaggn05b(BASETYPE = int, SFUNC = tfnp, STYPE = int[], - INITCOND = '{}'); --- N N N P --- should CREATE -CREATE AGGREGATE myaggn06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -CREATE AGGREGATE myaggn06b(BASETYPE = int, SFUNC = tf2p, STYPE = int[], - INITCOND = '{}'); --- N N P N --- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) -CREATE AGGREGATE myaggn07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tfnp(integer[], anyelement) does not exist -CREATE AGGREGATE myaggn07b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], - INITCOND = '{}'); -ERROR: function tfnp(integer[], anyelement) does not exist --- N N P P --- should CREATE -CREATE AGGREGATE myaggn08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -CREATE AGGREGATE myaggn08b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], - INITCOND = '{}'); --- N P N N --- should CREATE -CREATE AGGREGATE myaggn09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); --- N P N P --- should CREATE -CREATE AGGREGATE myaggn10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); --- N P P N --- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) -CREATE AGGREGATE myaggn11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tf1p(integer[], anyelement) does not exist --- N P P P --- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) -CREATE AGGREGATE myaggn12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tfp(integer[], anyelement) does not exist --- P N N N --- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) -CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P N N P --- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) -CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. -CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, - INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P N P N --- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) -CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp, - STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tfnp(anyarray, anyelement) does not exist -CREATE AGGREGATE myaggn15b(BASETYPE = anyelement, SFUNC = tfnp, - STYPE = anyarray, INITCOND = '{}'); -ERROR: function tfnp(anyarray, anyelement) does not exist --- P N P P --- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) -CREATE AGGREGATE myaggn16a(BASETYPE = anyelement, SFUNC = tf2p, - STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tf2p(anyarray, anyelement) does not exist -CREATE AGGREGATE myaggn16b(BASETYPE = anyelement, SFUNC = tf2p, - STYPE = anyarray, INITCOND = '{}'); -ERROR: function tf2p(anyarray, anyelement) does not exist --- P P N N --- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) -CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P P N P --- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) -CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, - FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: cannot determine transition data type -DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange. --- P P P N --- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) -CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p, - STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function tf1p(anyarray, anyelement) does not exist --- P P P P --- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) -CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp, - STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -ERROR: function ffnp(anyarray) does not exist --- multi-arg polymorphic -CREATE AGGREGATE mysum2(anyelement,anyelement) (SFUNC = sum3, - STYPE = anyelement, INITCOND = '0'); --- create test data for polymorphic aggregates -create temp table t(f1 int, f2 int[], f3 text); -insert into t values(1,array[1],'a'); -insert into t values(1,array[11],'b'); -insert into t values(1,array[111],'c'); -insert into t values(2,array[2],'a'); -insert into t values(2,array[22],'b'); -insert into t values(2,array[222],'c'); -insert into t values(3,array[3],'a'); -insert into t values(3,array[3],'b'); --- test the successfully created polymorphic aggregates -select f3, myaggp01a(*) from t group by f3 order by f3; - f3 | myaggp01a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp03a(*) from t group by f3 order by f3; - f3 | myaggp03a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp03b(*) from t group by f3 order by f3; - f3 | myaggp03b -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp05a(f1) from t group by f3 order by f3; - f3 | myaggp05a -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggp06a(f1) from t group by f3 order by f3; - f3 | myaggp06a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp08a(f1) from t group by f3 order by f3; - f3 | myaggp08a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp09a(f1) from t group by f3 order by f3; - f3 | myaggp09a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp09b(f1) from t group by f3 order by f3; - f3 | myaggp09b -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggp10a(f1) from t group by f3 order by f3; - f3 | myaggp10a -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggp10b(f1) from t group by f3 order by f3; - f3 | myaggp10b -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggp20a(f1) from t group by f3 order by f3; - f3 | myaggp20a -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggp20b(f1) from t group by f3 order by f3; - f3 | myaggp20b -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggn01a(*) from t group by f3 order by f3; - f3 | myaggn01a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn01b(*) from t group by f3 order by f3; - f3 | myaggn01b -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn03a(*) from t group by f3 order by f3; - f3 | myaggn03a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn05a(f1) from t group by f3 order by f3; - f3 | myaggn05a -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggn05b(f1) from t group by f3 order by f3; - f3 | myaggn05b -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select f3, myaggn06a(f1) from t group by f3 order by f3; - f3 | myaggn06a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn06b(f1) from t group by f3 order by f3; - f3 | myaggn06b -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn08a(f1) from t group by f3 order by f3; - f3 | myaggn08a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn08b(f1) from t group by f3 order by f3; - f3 | myaggn08b -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn09a(f1) from t group by f3 order by f3; - f3 | myaggn09a -----+----------- - a | {} - b | {} - c | {} -(3 rows) - -select f3, myaggn10a(f1) from t group by f3 order by f3; - f3 | myaggn10a -----+----------- - a | {1,2,3} - b | {1,2,3} - c | {1,2} -(3 rows) - -select mysum2(f1, f1 + 1) from t; - mysum2 --------- - 38 -(1 row) - --- test inlining of polymorphic SQL functions -create function bleat(int) returns int as $$ -begin - raise notice 'bleat %', $1; - return $1; -end$$ language plpgsql; -create function sql_if(bool, anyelement, anyelement) returns anyelement as $$ -select case when $1 then $2 else $3 end $$ language sql; --- Note this would fail with integer overflow, never mind wrong bleat() output, --- if the CASE expression were not successfully inlined -select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl; -NOTICE: bleat 1 -NOTICE: bleat 123456 -NOTICE: bleat -123455 -NOTICE: bleat 2147483647 -NOTICE: bleat -2147483646 - f1 | sql_if --------------+------------- - 0 | 1 - 123456 | 123456 - -123456 | -123455 - 2147483647 | 2147483647 - -2147483647 | -2147483646 -(5 rows) - -select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl; - q2 | sql_if --------------------+------------------- - 456 | 456 - 4567890123456789 | 4567890123456789 - 123 | 123 - 4567890123456789 | 4567890123456789 - -4567890123456789 | -4567890123456788 -(5 rows) - --- another sort of polymorphic aggregate -CREATE AGGREGATE array_larger_accum (anyarray) -( - sfunc = array_larger, - stype = anyarray, - initcond = '{}' -); -SELECT array_larger_accum(i) -FROM (VALUES (ARRAY[1,2]), (ARRAY[3,4])) as t(i); - array_larger_accum --------------------- - {3,4} -(1 row) - -SELECT array_larger_accum(i) -FROM (VALUES (ARRAY[row(1,2),row(3,4)]), (ARRAY[row(5,6),row(7,8)])) as t(i); - array_larger_accum --------------------- - {"(5,6)","(7,8)"} -(1 row) - --- another kind of polymorphic aggregate -create function add_group(grp anyarray, ad anyelement, size integer) - returns anyarray - as $$ -begin - if grp is null then - return array[ad]; - end if; - if array_upper(grp, 1) < size then - return grp || ad; - end if; - return grp; -end; -$$ - language plpgsql immutable; -create aggregate build_group(anyelement, integer) ( - SFUNC = add_group, - STYPE = anyarray -); -select build_group(q1,3) from int8_tbl; - build_group ----------------------------- - {123,123,4567890123456789} -(1 row) - --- this should fail because stype isn't compatible with arg -create aggregate build_group(int8, integer) ( - SFUNC = add_group, - STYPE = int2[] -); -ERROR: function add_group(smallint[], bigint, integer) does not exist --- but we can make a non-poly agg from a poly sfunc if types are OK -create aggregate build_group(int8, integer) ( - SFUNC = add_group, - STYPE = int8[] -); --- check proper resolution of data types for polymorphic transfn/finalfn -create function first_el_transfn(anyarray, anyelement) returns anyarray as -'select $1 || $2' language sql immutable; -create function first_el(anyarray) returns anyelement as -'select $1[1]' language sql strict immutable; -create aggregate first_el_agg_f8(float8) ( - SFUNC = array_append, - STYPE = float8[], - FINALFUNC = first_el -); -create aggregate first_el_agg_any(anyelement) ( - SFUNC = first_el_transfn, - STYPE = anyarray, - FINALFUNC = first_el -); -select first_el_agg_f8(x::float8) from generate_series(1,10) x; - first_el_agg_f8 ------------------ - 1 -(1 row) - -select first_el_agg_any(x) from generate_series(1,10) x; - first_el_agg_any ------------------- - 1 -(1 row) - -select first_el_agg_f8(x::float8) over(order by x) from generate_series(1,10) x; - first_el_agg_f8 ------------------ - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 -(10 rows) - -select first_el_agg_any(x) over(order by x) from generate_series(1,10) x; - first_el_agg_any ------------------- - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 -(10 rows) - --- check that we can apply functions taking ANYARRAY to pg_stats -select distinct array_ndims(histogram_bounds) from pg_stats -where histogram_bounds is not null; - array_ndims -------------- - 1 -(1 row) - --- such functions must protect themselves if varying element type isn't OK --- (WHERE clause here is to avoid possibly getting a collation error instead) -select max(histogram_bounds) from pg_stats where tablename = 'pg_am'; -ERROR: cannot compare arrays of different element types --- another corner case is the input functions for polymorphic pseudotypes -select array_in('{1,2,3}','int4'::regtype,-1); -- this has historically worked - array_in ----------- - {1,2,3} -(1 row) - -select * from array_in('{1,2,3}','int4'::regtype,-1); -- this not -ERROR: function "array_in" in FROM has unsupported return type anyarray -LINE 1: select * from array_in('{1,2,3}','int4'::regtype,-1); - ^ -select anyrange_in('[10,20)','int4range'::regtype,-1); -ERROR: cannot accept a value of type anyrange --- test variadic polymorphic functions -create function myleast(variadic anyarray) returns anyelement as $$ - select min($1[i]) from generate_subscripts($1,1) g(i) -$$ language sql immutable strict; -select myleast(10, 1, 20, 33); - myleast ---------- - 1 -(1 row) - -select myleast(1.1, 0.22, 0.55); - myleast ---------- - 0.22 -(1 row) - -select myleast('z'::text); - myleast ---------- - z -(1 row) - -select myleast(); -- fail -ERROR: function myleast() does not exist -LINE 1: select myleast(); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. --- test with variadic call parameter -select myleast(variadic array[1,2,3,4,-1]); - myleast ---------- - -1 -(1 row) - -select myleast(variadic array[1.1, -5.5]); - myleast ---------- - -5.5 -(1 row) - ---test with empty variadic call parameter -select myleast(variadic array[]::int[]); - myleast ---------- - -(1 row) - --- an example with some ordinary arguments too -create function concat(text, variadic anyarray) returns text as $$ - select array_to_string($2, $1); -$$ language sql immutable strict; -select concat('%', 1, 2, 3, 4, 5); - concat ------------ - 1%2%3%4%5 -(1 row) - -select concat('|', 'a'::text, 'b', 'c'); - concat --------- - a|b|c -(1 row) - -select concat('|', variadic array[1,2,33]); - concat --------- - 1|2|33 -(1 row) - -select concat('|', variadic array[]::int[]); - concat --------- - -(1 row) - -drop function concat(text, anyarray); --- mix variadic with anyelement -create function formarray(anyelement, variadic anyarray) returns anyarray as $$ - select array_prepend($1, $2); -$$ language sql immutable strict; -select formarray(1,2,3,4,5); - formarray -------------- - {1,2,3,4,5} -(1 row) - -select formarray(1.1, variadic array[1.2,55.5]); - formarray ----------------- - {1.1,1.2,55.5} -(1 row) - -select formarray(1.1, array[1.2,55.5]); -- fail without variadic -ERROR: function formarray(numeric, numeric[]) does not exist -LINE 1: select formarray(1.1, array[1.2,55.5]); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select formarray(1, 'x'::text); -- fail, type mismatch -ERROR: function formarray(integer, text) does not exist -LINE 1: select formarray(1, 'x'::text); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select formarray(1, variadic array['x'::text]); -- fail, type mismatch -ERROR: function formarray(integer, text[]) does not exist -LINE 1: select formarray(1, variadic array['x'::text]); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function formarray(anyelement, variadic anyarray); --- test pg_typeof() function -select pg_typeof(null); -- unknown - pg_typeof ------------ - unknown -(1 row) - -select pg_typeof(0); -- integer - pg_typeof ------------ - integer -(1 row) - -select pg_typeof(0.0); -- numeric - pg_typeof ------------ - numeric -(1 row) - -select pg_typeof(1+1 = 2); -- boolean - pg_typeof ------------ - boolean -(1 row) - -select pg_typeof('x'); -- unknown - pg_typeof ------------ - unknown -(1 row) - -select pg_typeof('' || ''); -- text - pg_typeof ------------ - text -(1 row) - -select pg_typeof(pg_typeof(0)); -- regtype - pg_typeof ------------ - regtype -(1 row) - -select pg_typeof(array[1.2,55.5]); -- numeric[] - pg_typeof ------------ - numeric[] -(1 row) - -select pg_typeof(myleast(10, 1, 20, 33)); -- polymorphic input - pg_typeof ------------ - integer -(1 row) - --- test functions with default parameters --- test basic functionality -create function dfunc(a int = 1, int = 2) returns int as $$ - select $1 + $2; -$$ language sql; -select dfunc(); - dfunc -------- - 3 -(1 row) - -select dfunc(10); - dfunc -------- - 12 -(1 row) - -select dfunc(10, 20); - dfunc -------- - 30 -(1 row) - -select dfunc(10, 20, 30); -- fail -ERROR: function dfunc(integer, integer, integer) does not exist -LINE 1: select dfunc(10, 20, 30); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function dfunc(); -- fail -ERROR: function dfunc() does not exist -drop function dfunc(int); -- fail -ERROR: function dfunc(integer) does not exist -drop function dfunc(int, int); -- ok --- fail: defaults must be at end of argument list -create function dfunc(a int = 1, b int) returns int as $$ - select $1 + $2; -$$ language sql; -ERROR: input parameters after one with a default value must also have defaults --- however, this should work: -create function dfunc(a int = 1, out sum int, b int = 2) as $$ - select $1 + $2; -$$ language sql; -select dfunc(); - dfunc -------- - 3 -(1 row) - --- verify it lists properly -\df dfunc - List of functions - Schema | Name | Result data type | Argument data types | Type ---------+-------+------------------+-----------------------------------------------------------+------ - public | dfunc | integer | a integer DEFAULT 1, OUT sum integer, b integer DEFAULT 2 | func -(1 row) - -drop function dfunc(int, int); --- check implicit coercion -create function dfunc(a int DEFAULT 1.0, int DEFAULT '-1') returns int as $$ - select $1 + $2; -$$ language sql; -select dfunc(); - dfunc -------- - 0 -(1 row) - -create function dfunc(a text DEFAULT 'Hello', b text DEFAULT 'World') returns text as $$ - select $1 || ', ' || $2; -$$ language sql; -select dfunc(); -- fail: which dfunc should be called? int or text -ERROR: function dfunc() is not unique -LINE 1: select dfunc(); - ^ -HINT: Could not choose a best candidate function. You might need to add explicit type casts. -select dfunc('Hi'); -- ok - dfunc ------------ - Hi, World -(1 row) - -select dfunc('Hi', 'City'); -- ok - dfunc ----------- - Hi, City -(1 row) - -select dfunc(0); -- ok - dfunc -------- - -1 -(1 row) - -select dfunc(10, 20); -- ok - dfunc -------- - 30 -(1 row) - -drop function dfunc(int, int); -drop function dfunc(text, text); -create function dfunc(int = 1, int = 2) returns int as $$ - select 2; -$$ language sql; -create function dfunc(int = 1, int = 2, int = 3, int = 4) returns int as $$ - select 4; -$$ language sql; --- Now, dfunc(nargs = 2) and dfunc(nargs = 4) are ambiguous when called --- with 0 to 2 arguments. -select dfunc(); -- fail -ERROR: function dfunc() is not unique -LINE 1: select dfunc(); - ^ -HINT: Could not choose a best candidate function. You might need to add explicit type casts. -select dfunc(1); -- fail -ERROR: function dfunc(integer) is not unique -LINE 1: select dfunc(1); - ^ -HINT: Could not choose a best candidate function. You might need to add explicit type casts. -select dfunc(1, 2); -- fail -ERROR: function dfunc(integer, integer) is not unique -LINE 1: select dfunc(1, 2); - ^ -HINT: Could not choose a best candidate function. You might need to add explicit type casts. -select dfunc(1, 2, 3); -- ok - dfunc -------- - 4 -(1 row) - -select dfunc(1, 2, 3, 4); -- ok - dfunc -------- - 4 -(1 row) - -drop function dfunc(int, int); -drop function dfunc(int, int, int, int); --- default values are not allowed for output parameters -create function dfunc(out int = 20) returns int as $$ - select 1; -$$ language sql; -ERROR: only input parameters can have default values --- polymorphic parameter test -create function dfunc(anyelement = 'World'::text) returns text as $$ - select 'Hello, ' || $1::text; -$$ language sql; -select dfunc(); - dfunc --------------- - Hello, World -(1 row) - -select dfunc(0); - dfunc ----------- - Hello, 0 -(1 row) - -select dfunc(to_date('20081215','YYYYMMDD')); - dfunc -------------------- - Hello, 12-15-2008 -(1 row) - -select dfunc('City'::text); - dfunc -------------- - Hello, City -(1 row) - -drop function dfunc(anyelement); --- check defaults for variadics -create function dfunc(a variadic int[]) returns int as -$$ select array_upper($1, 1) $$ language sql; -select dfunc(); -- fail -ERROR: function dfunc() does not exist -LINE 1: select dfunc(); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select dfunc(10); - dfunc -------- - 1 -(1 row) - -select dfunc(10,20); - dfunc -------- - 2 -(1 row) - -create or replace function dfunc(a variadic int[] default array[]::int[]) returns int as -$$ select array_upper($1, 1) $$ language sql; -select dfunc(); -- now ok - dfunc -------- - -(1 row) - -select dfunc(10); - dfunc -------- - 1 -(1 row) - -select dfunc(10,20); - dfunc -------- - 2 -(1 row) - --- can't remove the default once it exists -create or replace function dfunc(a variadic int[]) returns int as -$$ select array_upper($1, 1) $$ language sql; -ERROR: cannot remove parameter defaults from existing function -HINT: Use DROP FUNCTION dfunc(integer[]) first. -\df dfunc - List of functions - Schema | Name | Result data type | Argument data types | Type ---------+-------+------------------+-------------------------------------------------+------ - public | dfunc | integer | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | func -(1 row) - -drop function dfunc(a variadic int[]); --- Ambiguity should be reported only if there's not a better match available -create function dfunc(int = 1, int = 2, int = 3) returns int as $$ - select 3; -$$ language sql; -create function dfunc(int = 1, int = 2) returns int as $$ - select 2; -$$ language sql; -create function dfunc(text) returns text as $$ - select $1; -$$ language sql; --- dfunc(narg=2) and dfunc(narg=3) are ambiguous -select dfunc(1); -- fail -ERROR: function dfunc(integer) is not unique -LINE 1: select dfunc(1); - ^ -HINT: Could not choose a best candidate function. You might need to add explicit type casts. --- but this works since the ambiguous functions aren't preferred anyway -select dfunc('Hi'); - dfunc -------- - Hi -(1 row) - -drop function dfunc(int, int, int); -drop function dfunc(int, int); -drop function dfunc(text); --- --- Tests for named- and mixed-notation function calling --- -create function dfunc(a int, b int, c int = 0, d int = 0) - returns table (a int, b int, c int, d int) as $$ - select $1, $2, $3, $4; -$$ language sql; -select (dfunc(10,20,30)).*; - a | b | c | d -----+----+----+--- - 10 | 20 | 30 | 0 -(1 row) - -select (dfunc(a := 10, b := 20, c := 30)).*; - a | b | c | d -----+----+----+--- - 10 | 20 | 30 | 0 -(1 row) - -select * from dfunc(a := 10, b := 20); - a | b | c | d -----+----+---+--- - 10 | 20 | 0 | 0 -(1 row) - -select * from dfunc(b := 10, a := 20); - a | b | c | d -----+----+---+--- - 20 | 10 | 0 | 0 -(1 row) - -select * from dfunc(0); -- fail -ERROR: function dfunc(integer) does not exist -LINE 1: select * from dfunc(0); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select * from dfunc(1,2); - a | b | c | d ----+---+---+--- - 1 | 2 | 0 | 0 -(1 row) - -select * from dfunc(1,2,c := 3); - a | b | c | d ----+---+---+--- - 1 | 2 | 3 | 0 -(1 row) - -select * from dfunc(1,2,d := 3); - a | b | c | d ----+---+---+--- - 1 | 2 | 0 | 3 -(1 row) - -select * from dfunc(x := 20, b := 10, x := 30); -- fail, duplicate name -ERROR: argument name "x" used more than once -LINE 1: select * from dfunc(x := 20, b := 10, x := 30); - ^ -select * from dfunc(10, b := 20, 30); -- fail, named args must be last -ERROR: positional argument cannot follow named argument -LINE 1: select * from dfunc(10, b := 20, 30); - ^ -select * from dfunc(x := 10, b := 20, c := 30); -- fail, unknown param -ERROR: function dfunc(x => integer, b => integer, c => integer) does not exist -LINE 1: select * from dfunc(x := 10, b := 20, c := 30); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select * from dfunc(10, 10, a := 20); -- fail, a overlaps positional parameter -ERROR: function dfunc(integer, integer, a => integer) does not exist -LINE 1: select * from dfunc(10, 10, a := 20); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select * from dfunc(1,c := 2,d := 3); -- fail, no value for b -ERROR: function dfunc(integer, c => integer, d => integer) does not exist -LINE 1: select * from dfunc(1,c := 2,d := 3); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function dfunc(int, int, int, int); --- test with different parameter types -create function dfunc(a varchar, b numeric, c date = current_date) - returns table (a varchar, b numeric, c date) as $$ - select $1, $2, $3; -$$ language sql; -select (dfunc('Hello World', 20, '2009-07-25'::date)).*; - a | b | c --------------+----+------------ - Hello World | 20 | 07-25-2009 -(1 row) - -select * from dfunc('Hello World', 20, '2009-07-25'::date); - a | b | c --------------+----+------------ - Hello World | 20 | 07-25-2009 -(1 row) - -select * from dfunc(c := '2009-07-25'::date, a := 'Hello World', b := 20); - a | b | c --------------+----+------------ - Hello World | 20 | 07-25-2009 -(1 row) - -select * from dfunc('Hello World', b := 20, c := '2009-07-25'::date); - a | b | c --------------+----+------------ - Hello World | 20 | 07-25-2009 -(1 row) - -select * from dfunc('Hello World', c := '2009-07-25'::date, b := 20); - a | b | c --------------+----+------------ - Hello World | 20 | 07-25-2009 -(1 row) - -select * from dfunc('Hello World', c := 20, b := '2009-07-25'::date); -- fail -ERROR: function dfunc(unknown, c => integer, b => date) does not exist -LINE 1: select * from dfunc('Hello World', c := 20, b := '2009-07-25... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function dfunc(varchar, numeric, date); --- test out parameters with named params -create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric) -returns record as $$ - select $1, $2; -$$ language sql; -select (dfunc()).*; - _a | _c --------+---- - def a | -(1 row) - -select * from dfunc(); - _a | _c --------+---- - def a | -(1 row) - -select * from dfunc('Hello', 100); - _a | _c --------+----- - Hello | 100 -(1 row) - -select * from dfunc(a := 'Hello', c := 100); - _a | _c --------+----- - Hello | 100 -(1 row) - -select * from dfunc(c := 100, a := 'Hello'); - _a | _c --------+----- - Hello | 100 -(1 row) - -select * from dfunc('Hello'); - _a | _c --------+---- - Hello | -(1 row) - -select * from dfunc('Hello', c := 100); - _a | _c --------+----- - Hello | 100 -(1 row) - -select * from dfunc(c := 100); - _a | _c --------+----- - def a | 100 -(1 row) - --- fail, can no longer change an input parameter's name -create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric) -returns record as $$ - select $1, $2; -$$ language sql; -ERROR: cannot change name of input parameter "c" -HINT: Use DROP FUNCTION dfunc(character varying,numeric) first. -create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric) -returns record as $$ - select $1, $2; -$$ language sql; -ERROR: cannot change name of input parameter "c" -HINT: Use DROP FUNCTION dfunc(character varying,numeric) first. -drop function dfunc(varchar, numeric); ---fail, named parameters are not unique -create function testpolym(a int, a int) returns int as $$ select 1;$$ language sql; -ERROR: parameter name "a" used more than once -create function testpolym(int, out a int, out a int) returns int as $$ select 1;$$ language sql; -ERROR: parameter name "a" used more than once -create function testpolym(out a int, inout a int) returns int as $$ select 1;$$ language sql; -ERROR: parameter name "a" used more than once -create function testpolym(a int, inout a int) returns int as $$ select 1;$$ language sql; -ERROR: parameter name "a" used more than once --- valid -create function testpolym(a int, out a int) returns int as $$ select $1;$$ language sql; -select testpolym(37); - testpolym ------------ - 37 -(1 row) - -drop function testpolym(int); -create function testpolym(a int) returns table(a int) as $$ select $1;$$ language sql; -select * from testpolym(37); - a ----- - 37 -(1 row) - -drop function testpolym(int); --- test polymorphic params and defaults -create function dfunc(a anyelement, b anyelement = null, flag bool = true) -returns anyelement as $$ - select case when $3 then $1 else $2 end; -$$ language sql; -select dfunc(1,2); - dfunc -------- - 1 -(1 row) - -select dfunc('a'::text, 'b'); -- positional notation with default - dfunc -------- - a -(1 row) - -select dfunc(a := 1, b := 2); - dfunc -------- - 1 -(1 row) - -select dfunc(a := 'a'::text, b := 'b'); - dfunc -------- - a -(1 row) - -select dfunc(a := 'a'::text, b := 'b', flag := false); -- named notation - dfunc -------- - b -(1 row) - -select dfunc(b := 'b'::text, a := 'a'); -- named notation with default - dfunc -------- - a -(1 row) - -select dfunc(a := 'a'::text, flag := true); -- named notation with default - dfunc -------- - a -(1 row) - -select dfunc(a := 'a'::text, flag := false); -- named notation with default - dfunc -------- - -(1 row) - -select dfunc(b := 'b'::text, a := 'a', flag := true); -- named notation - dfunc -------- - a -(1 row) - -select dfunc('a'::text, 'b', false); -- full positional notation - dfunc -------- - b -(1 row) - -select dfunc('a'::text, 'b', flag := false); -- mixed notation - dfunc -------- - b -(1 row) - -select dfunc('a'::text, 'b', true); -- full positional notation - dfunc -------- - a -(1 row) - -select dfunc('a'::text, 'b', flag := true); -- mixed notation - dfunc -------- - a -(1 row) - --- ansi/sql syntax -select dfunc(a => 1, b => 2); - dfunc -------- - 1 -(1 row) - -select dfunc(a => 'a'::text, b => 'b'); - dfunc -------- - a -(1 row) - -select dfunc(a => 'a'::text, b => 'b', flag => false); -- named notation - dfunc -------- - b -(1 row) - -select dfunc(b => 'b'::text, a => 'a'); -- named notation with default - dfunc -------- - a -(1 row) - -select dfunc(a => 'a'::text, flag => true); -- named notation with default - dfunc -------- - a -(1 row) - -select dfunc(a => 'a'::text, flag => false); -- named notation with default - dfunc -------- - -(1 row) - -select dfunc(b => 'b'::text, a => 'a', flag => true); -- named notation - dfunc -------- - a -(1 row) - -select dfunc('a'::text, 'b', false); -- full positional notation - dfunc -------- - b -(1 row) - -select dfunc('a'::text, 'b', flag => false); -- mixed notation - dfunc -------- - b -(1 row) - -select dfunc('a'::text, 'b', true); -- full positional notation - dfunc -------- - a -(1 row) - -select dfunc('a'::text, 'b', flag => true); -- mixed notation - dfunc -------- - a -(1 row) - --- this tests lexer edge cases around => -select dfunc(a =>-1); - dfunc -------- - -1 -(1 row) - -select dfunc(a =>+1); - dfunc -------- - 1 -(1 row) - -select dfunc(a =>/**/1); - dfunc -------- - 1 -(1 row) - -select dfunc(a =>--comment to be removed by psql - 1); - dfunc -------- - 1 -(1 row) - --- need DO to protect the -- from psql -do $$ - declare r integer; - begin - select dfunc(a=>-- comment - 1) into r; - raise info 'r = %', r; - end; -$$; -INFO: r = 1 --- check reverse-listing of named-arg calls -CREATE VIEW dfview AS - SELECT q1, q2, - dfunc(q1,q2, flag := q1>q2) as c3, - dfunc(q1, flag := q1 int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 - FROM int8_tbl; - -drop view dfview; -drop function dfunc(anyelement, anyelement, bool); --- --- Tests for ANYCOMPATIBLE polymorphism family --- -create function anyctest(anycompatible, anycompatible) -returns anycompatible as $$ - select greatest($1, $2) -$$ language sql; -select x, pg_typeof(x) from anyctest(11, 12) x; - x | pg_typeof -----+----------- - 12 | integer -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12.3) x; - x | pg_typeof -------+----------- - 12.3 | numeric -(1 row) - -select x, pg_typeof(x) from anyctest(11, point(1,2)) x; -- fail -ERROR: function anyctest(integer, point) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, point(1,2)) x; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest('11', '12.3') x; -- defaults to text - x | pg_typeof -------+----------- - 12.3 | text -(1 row) - -drop function anyctest(anycompatible, anycompatible); -create function anyctest(anycompatible, anycompatible) -returns anycompatiblearray as $$ - select array[$1, $2] -$$ language sql; -select x, pg_typeof(x) from anyctest(11, 12) x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12.3) x; - x | pg_typeof ------------+----------- - {11,12.3} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, array[1,2]) x; -- fail -ERROR: function anyctest(integer, integer[]) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, array[1,2]) x; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(anycompatible, anycompatible); -create function anyctest(anycompatible, anycompatiblearray) -returns anycompatiblearray as $$ - select array[$1] || $2 -$$ language sql; -select x, pg_typeof(x) from anyctest(11, array[12]) x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, array[12.3]) x; - x | pg_typeof ------------+----------- - {11,12.3} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(12.3, array[13]) x; - x | pg_typeof ------------+----------- - {12.3,13} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(12.3, '{13,14.4}') x; - x | pg_typeof -----------------+----------- - {12.3,13,14.4} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) x; -- fail -ERROR: function anyctest(integer, point[]) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, array[point(1,2)]) ... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest(11, 12) x; -- fail -ERROR: function anyctest(integer, integer) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(anycompatible, anycompatiblearray); -create function anyctest(anycompatible, anycompatiblerange) -returns anycompatiblerange as $$ - select $2 -$$ language sql; -select x, pg_typeof(x) from anyctest(11, int4range(4,7)) x; - x | pg_typeof --------+----------- - [4,7) | int4range -(1 row) - -select x, pg_typeof(x) from anyctest(11, numrange(4,7)) x; - x | pg_typeof --------+----------- - [4,7) | numrange -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12) x; -- fail -ERROR: function anyctest(integer, integer) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x; -- fail -ERROR: function anyctest(numeric, int4range) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11.2, int4range(4,7)) x... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest(11.2, '[4,7)') x; -- fail -ERROR: could not determine polymorphic type anycompatiblerange because input has type unknown -drop function anyctest(anycompatible, anycompatiblerange); -create function anyctest(anycompatiblerange, anycompatiblerange) -returns anycompatible as $$ - select lower($1) + upper($2) -$$ language sql; -select x, pg_typeof(x) from anyctest(int4range(11,12), int4range(4,7)) x; - x | pg_typeof -----+----------- - 18 | integer -(1 row) - -select x, pg_typeof(x) from anyctest(int4range(11,12), numrange(4,7)) x; -- fail -ERROR: function anyctest(int4range, numrange) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(int4range(11,12), numra... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(anycompatiblerange, anycompatiblerange); --- fail, can't infer result type: -create function anyctest(anycompatible) -returns anycompatiblerange as $$ - select $1 -$$ language sql; -ERROR: cannot determine result data type -DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange. -create function anyctest(anycompatible, anycompatiblemultirange) -returns anycompatiblemultirange as $$ - select $2 -$$ language sql; -select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x; - x | pg_typeof ----------+---------------- - {[4,7)} | int4multirange -(1 row) - -select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x; - x | pg_typeof ----------+--------------- - {[4,7)} | nummultirange -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12) x; -- fail -ERROR: function anyctest(integer, integer) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail -ERROR: function anyctest(numeric, int4multirange) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11.2, multirange(int4ra... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail -ERROR: could not determine polymorphic type anycompatiblemultirange because input has type unknown -drop function anyctest(anycompatible, anycompatiblemultirange); -create function anyctest(anycompatiblemultirange, anycompatiblemultirange) -returns anycompatible as $$ - select lower($1) + upper($2) -$$ language sql; -select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x; - x | pg_typeof -----+----------- - 18 | integer -(1 row) - -select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail -ERROR: function anyctest(int4multirange, nummultirange) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(multirange(int4range(11... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(anycompatiblemultirange, anycompatiblemultirange); --- fail, can't infer result type: -create function anyctest(anycompatible) -returns anycompatiblemultirange as $$ - select $1 -$$ language sql; -ERROR: cannot determine result data type -DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange. -create function anyctest(anycompatiblenonarray, anycompatiblenonarray) -returns anycompatiblearray as $$ - select array[$1, $2] -$$ language sql; -select x, pg_typeof(x) from anyctest(11, 12) x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12.3) x; - x | pg_typeof ------------+----------- - {11,12.3} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(array[11], array[1,2]) x; -- fail -ERROR: function anyctest(integer[], integer[]) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(array[11], array[1,2]) ... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(anycompatiblenonarray, anycompatiblenonarray); -create function anyctest(a anyelement, b anyarray, - c anycompatible, d anycompatible) -returns anycompatiblearray as $$ - select array[c, d] -$$ language sql; -select x, pg_typeof(x) from anyctest(11, array[1, 2], 42, 34.5) x; - x | pg_typeof ------------+----------- - {42,34.5} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, array[1, 2], point(1,2), point(3,4)) x; - x | pg_typeof --------------------+----------- - {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, '{1,2}', point(1,2), '(3,4)') x; - x | pg_typeof --------------------+----------- - {"(1,2)","(3,4)"} | point[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, 34.5) x; -- fail -ERROR: function anyctest(integer, numeric[], integer, numeric) does not exist -LINE 1: select x, pg_typeof(x) from anyctest(11, array[1, 2.2], 42, ... - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -drop function anyctest(a anyelement, b anyarray, - c anycompatible, d anycompatible); -create function anyctest(variadic anycompatiblearray) -returns anycompatiblearray as $$ - select $1 -$$ language sql; -select x, pg_typeof(x) from anyctest(11, 12) x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, 12.2) x; - x | pg_typeof ------------+----------- - {11,12.2} | numeric[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, '12') x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(11, '12.2') x; -- fail -ERROR: invalid input syntax for type integer: "12.2" -LINE 1: select x, pg_typeof(x) from anyctest(11, '12.2') x; - ^ -select x, pg_typeof(x) from anyctest(variadic array[11, 12]) x; - x | pg_typeof ----------+----------- - {11,12} | integer[] -(1 row) - -select x, pg_typeof(x) from anyctest(variadic array[11, 12.2]) x; - x | pg_typeof ------------+----------- - {11,12.2} | numeric[] -(1 row) - -drop function anyctest(variadic anycompatiblearray); +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rowtypes.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rowtypes.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/rowtypes.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/rowtypes.out 2021-10-30 22:09:24 -0600 @@ -14,1248 +14,10 @@ (1 row) select row('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname; - row | fullname -------------+------------ - (Joe,Blow) | (Joe,Blow) -(1 row) - -select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname; - fullname | fullname -------------------+-------------- - (Joe,"von Blow") | (Joe,d'Blow) -(1 row) - -select '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname; - fullname | fullname --------------------+----------------- - (Joe,"von""Blow") | (Joe,"d\\Blow") -(1 row) - -select '(Joe,"Blow,Jr")'::fullname; - fullname ------------------ - (Joe,"Blow,Jr") -(1 row) - -select '(Joe,)'::fullname; -- ok, null 2nd column - fullname ----------- - (Joe,) -(1 row) - -select '(Joe)'::fullname; -- bad -ERROR: malformed record literal: "(Joe)" -LINE 1: select '(Joe)'::fullname; - ^ -DETAIL: Too few columns. -select '(Joe,,)'::fullname; -- bad -ERROR: malformed record literal: "(Joe,,)" -LINE 1: select '(Joe,,)'::fullname; - ^ -DETAIL: Too many columns. -select '[]'::fullname; -- bad -ERROR: malformed record literal: "[]" -LINE 1: select '[]'::fullname; - ^ -DETAIL: Missing left parenthesis. -select ' (Joe,Blow) '::fullname; -- ok, extra whitespace - fullname ------------- - (Joe,Blow) -(1 row) - -select '(Joe,Blow) /'::fullname; -- bad -ERROR: malformed record literal: "(Joe,Blow) /" -LINE 1: select '(Joe,Blow) /'::fullname; - ^ -DETAIL: Junk after right parenthesis. -create temp table quadtable(f1 int, q quad); -insert into quadtable values (1, ((3.3,4.4),(5.5,6.6))); -insert into quadtable values (2, ((null,4.4),(5.5,6.6))); -select * from quadtable; - f1 | q -----+--------------------------- - 1 | ("(3.3,4.4)","(5.5,6.6)") - 2 | ("(,4.4)","(5.5,6.6)") -(2 rows) - -select f1, q.c1 from quadtable; -- fails, q is a table reference -ERROR: missing FROM-clause entry for table "q" -LINE 1: select f1, q.c1 from quadtable; - ^ -select f1, (q).c1, (qq.q).c1.i from quadtable qq; - f1 | c1 | i -----+-----------+----- - 1 | (3.3,4.4) | 4.4 - 2 | (,4.4) | 4.4 -(2 rows) - -create temp table people (fn fullname, bd date); -insert into people values ('(Joe,Blow)', '1984-01-10'); -select * from people; - fn | bd -------------+------------ - (Joe,Blow) | 01-10-1984 -(1 row) - --- at the moment this will not work due to ALTER TABLE inadequacy: -alter table fullname add column suffix text default ''; -ERROR: cannot alter table "fullname" because column "people.fn" uses its row type --- but this should work: -alter table fullname add column suffix text default null; -select * from people; - fn | bd --------------+------------ - (Joe,Blow,) | 01-10-1984 -(1 row) - --- test insertion/updating of subfields -update people set fn.suffix = 'Jr'; -select * from people; - fn | bd ----------------+------------ - (Joe,Blow,Jr) | 01-10-1984 -(1 row) - -insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66); -update quadtable set q.c1.r = 12 where f1 = 2; -update quadtable set q.c1 = 12; -- error, type mismatch -ERROR: subfield "c1" is of type complex but expression is of type integer -LINE 1: update quadtable set q.c1 = 12; - ^ -HINT: You will need to rewrite or cast the expression. -select * from quadtable; - f1 | q -----+--------------------------- - 1 | ("(3.3,4.4)","(5.5,6.6)") - 44 | ("(55,)","(,66)") - 2 | ("(12,4.4)","(5.5,6.6)") -(3 rows) - --- The object here is to ensure that toasted references inside --- composite values don't cause problems. The large f1 value will --- be toasted inside pp, it must still work after being copied to people. -create temp table pp (f1 text); -insert into pp values (repeat('abcdefghijkl', 100000)); -insert into people select ('Jim', f1, null)::fullname, current_date from pp; -select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people; - first | substr | length --------+----------------------+--------- - Joe | Blow | 4 - Jim | abcdefghijklabcdefgh | 1200000 -(2 rows) - --- Test row comparison semantics. Prior to PG 8.2 we did this in a totally --- non-spec-compliant way. -select ROW(1,2) < ROW(1,3) as true; - true ------- - t -(1 row) - -select ROW(1,2) < ROW(1,1) as false; - false -------- - f -(1 row) - -select ROW(1,2) < ROW(1,NULL) as null; - null ------- - -(1 row) - -select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined - true ------- - t -(1 row) - -select ROW(11,'ABC') < ROW(11,'DEF') as true; - true ------- - t -(1 row) - -select ROW(11,'ABC') > ROW(11,'DEF') as false; - false -------- - f -(1 row) - -select ROW(12,'ABC') > ROW(11,'DEF') as true; - true ------- - t -(1 row) - --- = and <> have different NULL-behavior than < etc -select ROW(1,2,3) < ROW(1,NULL,4) as null; - null ------- - -(1 row) - -select ROW(1,2,3) = ROW(1,NULL,4) as false; - false -------- - f -(1 row) - -select ROW(1,2,3) <> ROW(1,NULL,4) as true; - true ------- - t -(1 row) - --- We allow operators beyond the six standard ones, if they have btree --- operator classes. -select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true; - true ------- - t -(1 row) - -select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false; - false -------- - f -(1 row) - -select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; -ERROR: could not determine interpretation of row comparison operator ~~ -LINE 1: select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; - ^ -HINT: Row comparison operators must be associated with btree operator families. --- Comparisons of ROW() expressions can cope with some type mismatches -select ROW(1,2) = ROW(1,2::int8); - ?column? ----------- - t -(1 row) - -select ROW(1,2) in (ROW(3,4), ROW(1,2)); - ?column? ----------- - t -(1 row) - -select ROW(1,2) in (ROW(3,4), ROW(1,2::int8)); - ?column? ----------- - t -(1 row) - --- Check row comparison with a subselect -select unique1, unique2 from tenk1 -where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3) - and unique1 <= 20 -order by 1; - unique1 | unique2 ----------+--------- - 0 | 9998 - 1 | 2838 -(2 rows) - --- Also check row comparison with an indexable condition -explain (costs off) -select thousand, tenthous from tenk1 -where (thousand, tenthous) >= (997, 5000) -order by thousand, tenthous; - QUERY PLAN ------------------------------------------------------------ - Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000)) -(2 rows) - -select thousand, tenthous from tenk1 -where (thousand, tenthous) >= (997, 5000) -order by thousand, tenthous; - thousand | tenthous -----------+---------- - 997 | 5997 - 997 | 6997 - 997 | 7997 - 997 | 8997 - 997 | 9997 - 998 | 998 - 998 | 1998 - 998 | 2998 - 998 | 3998 - 998 | 4998 - 998 | 5998 - 998 | 6998 - 998 | 7998 - 998 | 8998 - 998 | 9998 - 999 | 999 - 999 | 1999 - 999 | 2999 - 999 | 3999 - 999 | 4999 - 999 | 5999 - 999 | 6999 - 999 | 7999 - 999 | 8999 - 999 | 9999 -(25 rows) - -explain (costs off) -select thousand, tenthous, four from tenk1 -where (thousand, tenthous, four) > (998, 5000, 3) -order by thousand, tenthous; - QUERY PLAN ------------------------------------------------------------------------ - Sort - Sort Key: thousand, tenthous - -> Bitmap Heap Scan on tenk1 - Filter: (ROW(thousand, tenthous, four) > ROW(998, 5000, 3)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (ROW(thousand, tenthous) >= ROW(998, 5000)) -(6 rows) - -select thousand, tenthous, four from tenk1 -where (thousand, tenthous, four) > (998, 5000, 3) -order by thousand, tenthous; - thousand | tenthous | four -----------+----------+------ - 998 | 5998 | 2 - 998 | 6998 | 2 - 998 | 7998 | 2 - 998 | 8998 | 2 - 998 | 9998 | 2 - 999 | 999 | 3 - 999 | 1999 | 3 - 999 | 2999 | 3 - 999 | 3999 | 3 - 999 | 4999 | 3 - 999 | 5999 | 3 - 999 | 6999 | 3 - 999 | 7999 | 3 - 999 | 8999 | 3 - 999 | 9999 | 3 -(15 rows) - -explain (costs off) -select thousand, tenthous from tenk1 -where (998, 5000) < (thousand, tenthous) -order by thousand, tenthous; - QUERY PLAN ----------------------------------------------------------- - Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: (ROW(thousand, tenthous) > ROW(998, 5000)) -(2 rows) - -select thousand, tenthous from tenk1 -where (998, 5000) < (thousand, tenthous) -order by thousand, tenthous; - thousand | tenthous -----------+---------- - 998 | 5998 - 998 | 6998 - 998 | 7998 - 998 | 8998 - 998 | 9998 - 999 | 999 - 999 | 1999 - 999 | 2999 - 999 | 3999 - 999 | 4999 - 999 | 5999 - 999 | 6999 - 999 | 7999 - 999 | 8999 - 999 | 9999 -(15 rows) - -explain (costs off) -select thousand, hundred from tenk1 -where (998, 5000) < (thousand, hundred) -order by thousand, hundred; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: thousand, hundred - -> Bitmap Heap Scan on tenk1 - Filter: (ROW(998, 5000) < ROW(thousand, hundred)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand >= 998) -(6 rows) - -select thousand, hundred from tenk1 -where (998, 5000) < (thousand, hundred) -order by thousand, hundred; - thousand | hundred -----------+--------- - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 - 999 | 99 -(10 rows) - --- Test case for bug #14010: indexed row comparisons fail with nulls -create temp table test_table (a text, b text); -insert into test_table values ('a', 'b'); -insert into test_table select 'a', null from generate_series(1,1000); -insert into test_table values ('b', 'a'); -create index on test_table (a,b); -set enable_sort = off; -explain (costs off) -select a,b from test_table where (a,b) > ('a','a') order by a,b; - QUERY PLAN --------------------------------------------------------- - Index Only Scan using test_table_a_b_idx on test_table - Index Cond: (ROW(a, b) > ROW('a'::text, 'a'::text)) -(2 rows) - -select a,b from test_table where (a,b) > ('a','a') order by a,b; - a | b ----+--- - a | b - b | a -(2 rows) - -reset enable_sort; --- Check row comparisons with IN -select * from int8_tbl i8 where i8 in (row(123,456)); -- fail, type mismatch -ERROR: cannot compare dissimilar column types bigint and integer at record column 1 -explain (costs off) -select * from int8_tbl i8 -where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); - QUERY PLAN -------------------------------------------------------------------------------- - Seq Scan on int8_tbl i8 - Filter: (i8.* = ANY ('{"(123,456)","(4567890123456789,123)"}'::int8_tbl[])) -(2 rows) - -select * from int8_tbl i8 -where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); - q1 | q2 -------------------+----- - 123 | 456 - 4567890123456789 | 123 -(2 rows) - --- Check ability to select columns from an anonymous rowtype -select (row(1, 2.0)).f1; - f1 ----- - 1 -(1 row) - -select (row(1, 2.0)).f2; - f2 ------ - 2.0 -(1 row) - -select (row(1, 2.0)).nosuch; -- fail -ERROR: could not identify column "nosuch" in record data type -LINE 1: select (row(1, 2.0)).nosuch; - ^ -select (row(1, 2.0)).*; - f1 | f2 -----+----- - 1 | 2.0 -(1 row) - -select (r).f1 from (select row(1, 2.0) as r) ss; - f1 ----- - 1 -(1 row) - -select (r).f3 from (select row(1, 2.0) as r) ss; -- fail -ERROR: could not identify column "f3" in record data type -LINE 1: select (r).f3 from (select row(1, 2.0) as r) ss; - ^ -select (r).* from (select row(1, 2.0) as r) ss; - f1 | f2 -----+----- - 1 | 2.0 -(1 row) - --- Check some corner cases involving empty rowtypes -select ROW(); - row ------ - () -(1 row) - -select ROW() IS NULL; - ?column? ----------- - t -(1 row) - -select ROW() = ROW(); -ERROR: cannot compare rows of zero length -LINE 1: select ROW() = ROW(); - ^ --- Check ability to create arrays of anonymous rowtypes -select array[ row(1,2), row(3,4), row(5,6) ]; - array ---------------------------- - {"(1,2)","(3,4)","(5,6)"} -(1 row) - --- Check ability to compare an anonymous row to elements of an array -select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]); - ?column? ----------- - t -(1 row) - -select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]); - ?column? ----------- - f -(1 row) - --- Check behavior with a non-comparable rowtype -create type cantcompare as (p point, r float8); -create temp table cc (f1 cantcompare); -insert into cc values('("(1,2)",3)'); -insert into cc values('("(4,5)",6)'); -select * from cc order by f1; -- fail, but should complain about cantcompare -ERROR: could not identify an ordering operator for type cantcompare -LINE 1: select * from cc order by f1; - ^ -HINT: Use an explicit ordering operator or modify the query. --- --- Tests for record_{eq,cmp} --- -create type testtype1 as (a int, b int); --- all true -select row(1, 2)::testtype1 < row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 <= row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 = row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 <> row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 3)::testtype1 >= row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 3)::testtype1 > row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - --- all false -select row(1, -2)::testtype1 < row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 <= row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 = row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 <> row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -3)::testtype1 >= row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -3)::testtype1 > row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - --- true, but see *< below -select row(1, -2)::testtype1 < row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - --- mismatches -create type testtype3 as (a int, b text); -select row(1, 2)::testtype1 < row(1, 'abc')::testtype3; -ERROR: cannot compare dissimilar column types integer and text at record column 2 -select row(1, 2)::testtype1 <> row(1, 'abc')::testtype3; -ERROR: cannot compare dissimilar column types integer and text at record column 2 -create type testtype5 as (a int); -select row(1, 2)::testtype1 < row(1)::testtype5; -ERROR: cannot compare record types with different numbers of columns -select row(1, 2)::testtype1 <> row(1)::testtype5; -ERROR: cannot compare record types with different numbers of columns --- non-comparable types -create type testtype6 as (a int, b point); -select row(1, '(1,2)')::testtype6 < row(1, '(1,3)')::testtype6; -ERROR: could not identify a comparison function for type point -select row(1, '(1,2)')::testtype6 <> row(1, '(1,3)')::testtype6; -ERROR: could not identify an equality operator for type point -drop type testtype1, testtype3, testtype5, testtype6; --- --- Tests for record_image_{eq,cmp} --- -create type testtype1 as (a int, b int); --- all true -select row(1, 2)::testtype1 *< row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 *<= row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 *= row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 2)::testtype1 *<> row(1, 3)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 3)::testtype1 *>= row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - -select row(1, 3)::testtype1 *> row(1, 2)::testtype1; - ?column? ----------- - t -(1 row) - --- all false -select row(1, -2)::testtype1 *< row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 *<= row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 *= row(1, -3)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -2)::testtype1 *<> row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -3)::testtype1 *>= row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - -select row(1, -3)::testtype1 *> row(1, -2)::testtype1; - ?column? ----------- - f -(1 row) - --- This returns the "wrong" order because record_image_cmp works on --- unsigned datums without knowing about the actual data type. -select row(1, -2)::testtype1 *< row(1, 3)::testtype1; - ?column? ----------- - f -(1 row) - --- other types -create type testtype2 as (a smallint, b bool); -- byval different sizes -select row(1, true)::testtype2 *< row(2, true)::testtype2; - ?column? ----------- - t -(1 row) - -select row(-2, true)::testtype2 *< row(-1, true)::testtype2; - ?column? ----------- - t -(1 row) - -select row(0, false)::testtype2 *< row(0, true)::testtype2; - ?column? ----------- - t -(1 row) - -select row(0, false)::testtype2 *<> row(0, true)::testtype2; - ?column? ----------- - t -(1 row) - -create type testtype3 as (a int, b text); -- variable length -select row(1, 'abc')::testtype3 *< row(1, 'abd')::testtype3; - ?column? ----------- - t -(1 row) - -select row(1, 'abc')::testtype3 *< row(1, 'abcd')::testtype3; - ?column? ----------- - t -(1 row) - -select row(1, 'abc')::testtype3 *> row(1, 'abd')::testtype3; - ?column? ----------- - f -(1 row) - -select row(1, 'abc')::testtype3 *<> row(1, 'abd')::testtype3; - ?column? ----------- - t -(1 row) - -create type testtype4 as (a int, b point); -- by ref, fixed length -select row(1, '(1,2)')::testtype4 *< row(1, '(1,3)')::testtype4; - ?column? ----------- - t -(1 row) - -select row(1, '(1,2)')::testtype4 *<> row(1, '(1,3)')::testtype4; - ?column? ----------- - t -(1 row) - --- mismatches -select row(1, 2)::testtype1 *< row(1, 'abc')::testtype3; -ERROR: cannot compare dissimilar column types integer and text at record column 2 -select row(1, 2)::testtype1 *<> row(1, 'abc')::testtype3; -ERROR: cannot compare dissimilar column types integer and text at record column 2 -create type testtype5 as (a int); -select row(1, 2)::testtype1 *< row(1)::testtype5; -ERROR: cannot compare record types with different numbers of columns -select row(1, 2)::testtype1 *<> row(1)::testtype5; -ERROR: cannot compare record types with different numbers of columns --- non-comparable types -create type testtype6 as (a int, b point); -select row(1, '(1,2)')::testtype6 *< row(1, '(1,3)')::testtype6; - ?column? ----------- - t -(1 row) - -select row(1, '(1,2)')::testtype6 *>= row(1, '(1,3)')::testtype6; - ?column? ----------- - f -(1 row) - -select row(1, '(1,2)')::testtype6 *<> row(1, '(1,3)')::testtype6; - ?column? ----------- - t -(1 row) - --- anonymous rowtypes in coldeflists -select q.a, q.b = row(2), q.c = array[row(3)], q.d = row(row(4)) from - unnest(array[row(1, row(2), array[row(3)], row(row(4))), - row(2, row(3), array[row(4)], row(row(5)))]) - as q(a int, b record, c record[], d record); - a | ?column? | ?column? | ?column? ----+----------+----------+---------- - 1 | t | t | t - 2 | f | f | f -(2 rows) - -drop type testtype1, testtype2, testtype3, testtype4, testtype5, testtype6; --- --- Test case derived from bug #5716: check multiple uses of a rowtype result --- -BEGIN; -CREATE TABLE price ( - id SERIAL PRIMARY KEY, - active BOOLEAN NOT NULL, - price NUMERIC -); -CREATE TYPE price_input AS ( - id INTEGER, - price NUMERIC -); -CREATE TYPE price_key AS ( - id INTEGER -); -CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$ - SELECT $1.id -$$ LANGUAGE SQL; -CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$ - SELECT $1.id -$$ LANGUAGE SQL; -insert into price values (1,false,42), (10,false,100), (11,true,17.99); -UPDATE price - SET active = true, price = input_prices.price - FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices - WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*); -select * from price; - id | active | price -----+--------+-------- - 1 | f | 42 - 10 | t | 123.00 - 11 | t | 99.99 -(3 rows) - -rollback; --- --- Test case derived from bug #9085: check * qualification of composite --- parameters for SQL functions --- -create temp table compos (f1 int, f2 text); -create function fcompos1(v compos) returns void as $$ -insert into compos values (v); -- fail -$$ language sql; -ERROR: column "f1" is of type integer but expression is of type compos -LINE 2: insert into compos values (v); -- fail - ^ -HINT: You will need to rewrite or cast the expression. -create function fcompos1(v compos) returns void as $$ -insert into compos values (v.*); -$$ language sql; -create function fcompos2(v compos) returns void as $$ -select fcompos1(v); -$$ language sql; -create function fcompos3(v compos) returns void as $$ -select fcompos1(fcompos3.v.*); -$$ language sql; -select fcompos1(row(1,'one')); - fcompos1 ----------- - -(1 row) - -select fcompos2(row(2,'two')); - fcompos2 ----------- - -(1 row) - -select fcompos3(row(3,'three')); - fcompos3 ----------- - -(1 row) - -select * from compos; - f1 | f2 -----+------- - 1 | one - 2 | two - 3 | three -(3 rows) - --- --- We allow I/O conversion casts from composite types to strings to be --- invoked via cast syntax, but not functional syntax. This is because --- the latter is too prone to be invoked unintentionally. --- -select cast (fullname as text) from fullname; - fullname ----------- -(0 rows) - -select fullname::text from fullname; - fullname ----------- -(0 rows) - -select text(fullname) from fullname; -- error -ERROR: function text(fullname) does not exist -LINE 1: select text(fullname) from fullname; - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select fullname.text from fullname; -- error -ERROR: column fullname.text does not exist -LINE 1: select fullname.text from fullname; - ^ --- same, but RECORD instead of named composite type: -select cast (row('Jim', 'Beam') as text); - row ------------- - (Jim,Beam) -(1 row) - -select (row('Jim', 'Beam'))::text; - row ------------- - (Jim,Beam) -(1 row) - -select text(row('Jim', 'Beam')); -- error -ERROR: function text(record) does not exist -LINE 1: select text(row('Jim', 'Beam')); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -select (row('Jim', 'Beam')).text; -- error -ERROR: could not identify column "text" in record data type -LINE 1: select (row('Jim', 'Beam')).text; - ^ --- --- Check the equivalence of functional and column notation --- -insert into fullname values ('Joe', 'Blow'); -select f.last from fullname f; - last ------- - Blow -(1 row) - -select last(f) from fullname f; - last ------- - Blow -(1 row) - -create function longname(fullname) returns text language sql -as $$select $1.first || ' ' || $1.last$$; -select f.longname from fullname f; - longname ----------- - Joe Blow -(1 row) - -select longname(f) from fullname f; - longname ----------- - Joe Blow -(1 row) - --- Starting in v11, the notational form does matter if there's ambiguity -alter table fullname add column longname text; -select f.longname from fullname f; - longname ----------- - -(1 row) - -select longname(f) from fullname f; - longname ----------- - Joe Blow -(1 row) - --- --- Test that composite values are seen to have the correct column names --- (bug #11210 and other reports) --- -select row_to_json(i) from int8_tbl i; - row_to_json ------------------------------------------------- - {"q1":123,"q2":456} - {"q1":123,"q2":4567890123456789} - {"q1":4567890123456789,"q2":123} - {"q1":4567890123456789,"q2":4567890123456789} - {"q1":4567890123456789,"q2":-4567890123456789} -(5 rows) - -select row_to_json(i) from int8_tbl i(x,y); - row_to_json ----------------------------------------------- - {"x":123,"y":456} - {"x":123,"y":4567890123456789} - {"x":4567890123456789,"y":123} - {"x":4567890123456789,"y":4567890123456789} - {"x":4567890123456789,"y":-4567890123456789} -(5 rows) - -create temp view vv1 as select * from int8_tbl; -select row_to_json(i) from vv1 i; - row_to_json ------------------------------------------------- - {"q1":123,"q2":456} - {"q1":123,"q2":4567890123456789} - {"q1":4567890123456789,"q2":123} - {"q1":4567890123456789,"q2":4567890123456789} - {"q1":4567890123456789,"q2":-4567890123456789} -(5 rows) - -select row_to_json(i) from vv1 i(x,y); - row_to_json ----------------------------------------------- - {"x":123,"y":456} - {"x":123,"y":4567890123456789} - {"x":4567890123456789,"y":123} - {"x":4567890123456789,"y":4567890123456789} - {"x":4567890123456789,"y":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1, q2 from int8_tbl) as ss; - row_to_json ------------------------------------------------- - {"q1":123,"q2":456} - {"q1":123,"q2":4567890123456789} - {"q1":4567890123456789,"q2":123} - {"q1":4567890123456789,"q2":4567890123456789} - {"q1":4567890123456789,"q2":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1, q2 from int8_tbl offset 0) as ss; - row_to_json ------------------------------------------------- - {"q1":123,"q2":456} - {"q1":123,"q2":4567890123456789} - {"q1":4567890123456789,"q2":123} - {"q1":4567890123456789,"q2":4567890123456789} - {"q1":4567890123456789,"q2":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1 as a, q2 as b from int8_tbl) as ss; - row_to_json ----------------------------------------------- - {"a":123,"b":456} - {"a":123,"b":4567890123456789} - {"a":4567890123456789,"b":123} - {"a":4567890123456789,"b":4567890123456789} - {"a":4567890123456789,"b":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1 as a, q2 as b from int8_tbl offset 0) as ss; - row_to_json ----------------------------------------------- - {"a":123,"b":456} - {"a":123,"b":4567890123456789} - {"a":4567890123456789,"b":123} - {"a":4567890123456789,"b":4567890123456789} - {"a":4567890123456789,"b":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1 as a, q2 as b from int8_tbl) as ss(x,y); - row_to_json ----------------------------------------------- - {"x":123,"y":456} - {"x":123,"y":4567890123456789} - {"x":4567890123456789,"y":123} - {"x":4567890123456789,"y":4567890123456789} - {"x":4567890123456789,"y":-4567890123456789} -(5 rows) - -select row_to_json(ss) from - (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y); - row_to_json ----------------------------------------------- - {"x":123,"y":456} - {"x":123,"y":4567890123456789} - {"x":4567890123456789,"y":123} - {"x":4567890123456789,"y":4567890123456789} - {"x":4567890123456789,"y":-4567890123456789} -(5 rows) - -explain (costs off) -select row_to_json(q) from - (select thousand, tenthous from tenk1 - where thousand = 42 and tenthous < 2000 offset 0) q; - QUERY PLAN -------------------------------------------------------------- - Subquery Scan on q - -> Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = 42) AND (tenthous < 2000)) -(3 rows) - -select row_to_json(q) from - (select thousand, tenthous from tenk1 - where thousand = 42 and tenthous < 2000 offset 0) q; - row_to_json ---------------------------------- - {"thousand":42,"tenthous":42} - {"thousand":42,"tenthous":1042} -(2 rows) - -select row_to_json(q) from - (select thousand as x, tenthous as y from tenk1 - where thousand = 42 and tenthous < 2000 offset 0) q; - row_to_json -------------------- - {"x":42,"y":42} - {"x":42,"y":1042} -(2 rows) - -select row_to_json(q) from - (select thousand as x, tenthous as y from tenk1 - where thousand = 42 and tenthous < 2000 offset 0) q(a,b); - row_to_json -------------------- - {"a":42,"b":42} - {"a":42,"b":1042} -(2 rows) - -create temp table tt1 as select * from int8_tbl limit 2; -create temp table tt2 () inherits(tt1); -insert into tt2 values(0,0); -select row_to_json(r) from (select q2,q1 from tt1 offset 0) r; - row_to_json ----------------------------------- - {"q2":456,"q1":123} - {"q2":4567890123456789,"q1":123} - {"q2":0,"q1":0} -(3 rows) - --- check no-op rowtype conversions -create temp table tt3 () inherits(tt2); -insert into tt3 values(33,44); -select row_to_json(tt3::tt2::tt1) from tt3; - row_to_json -------------------- - {"q1":33,"q2":44} -(1 row) - --- --- IS [NOT] NULL should not recurse into nested composites (bug #14235) --- -explain (verbose, costs off) -select r, r is null as isnull, r is not null as isnotnull -from (values (1,row(1,2)), (1,row(null,null)), (1,null), - (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Values Scan on "*VALUES*" - Output: ROW("*VALUES*".column1, "*VALUES*".column2), (("*VALUES*".column1 IS NULL) AND ("*VALUES*".column2 IS NOT DISTINCT FROM NULL)), (("*VALUES*".column1 IS NOT NULL) AND ("*VALUES*".column2 IS DISTINCT FROM NULL)) -(2 rows) - -select r, r is null as isnull, r is not null as isnotnull -from (values (1,row(1,2)), (1,row(null,null)), (1,null), - (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); - r | isnull | isnotnull --------------+--------+----------- - (1,"(1,2)") | f | t - (1,"(,)") | f | t - (1,) | f | f - (,"(1,2)") | f | f - (,"(,)") | f | f - (,) | t | f -(6 rows) - -explain (verbose, costs off) -with r(a,b) as materialized - (values (1,row(1,2)), (1,row(null,null)), (1,null), - (null,row(1,2)), (null,row(null,null)), (null,null) ) -select r, r is null as isnull, r is not null as isnotnull from r; - QUERY PLAN ----------------------------------------------------------- - CTE Scan on r - Output: r.*, (r.* IS NULL), (r.* IS NOT NULL) - CTE r - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1, "*VALUES*".column2 -(5 rows) - -with r(a,b) as materialized - (values (1,row(1,2)), (1,row(null,null)), (1,null), - (null,row(1,2)), (null,row(null,null)), (null,null) ) -select r, r is null as isnull, r is not null as isnotnull from r; - r | isnull | isnotnull --------------+--------+----------- - (1,"(1,2)") | f | t - (1,"(,)") | f | t - (1,) | f | f - (,"(1,2)") | f | f - (,"(,)") | f | f - (,) | t | f -(6 rows) - --- --- Tests for component access / FieldSelect --- -CREATE TABLE compositetable(a text, b text); -INSERT INTO compositetable(a, b) VALUES('fa', 'fb'); --- composite type columns can't directly be accessed (error) -SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s; -ERROR: missing FROM-clause entry for table "d" -LINE 1: SELECT d.a FROM (SELECT compositetable AS d FROM compositeta... - ^ --- but can be accessed with proper parens -SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s; - a | b -----+---- - fa | fb -(1 row) - --- system columns can't be accessed in composite types (error) -SELECT (d).ctid FROM (SELECT compositetable AS d FROM compositetable) s; -ERROR: column "ctid" not found in data type compositetable -LINE 1: SELECT (d).ctid FROM (SELECT compositetable AS d FROM compos... - ^ --- accessing non-existing column in NULL datum errors out -SELECT (NULL::compositetable).nonexistent; -ERROR: column "nonexistent" not found in data type compositetable -LINE 1: SELECT (NULL::compositetable).nonexistent; - ^ --- existing column in a NULL composite yield NULL -SELECT (NULL::compositetable).a; - a ---- - -(1 row) - --- oids can't be accessed in composite types (error) -SELECT (NULL::compositetable).oid; -ERROR: column "oid" not found in data type compositetable -LINE 1: SELECT (NULL::compositetable).oid; - ^ -DROP TABLE compositetable; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/largeobject.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/largeobject.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/largeobject.out 2021-10-30 21:52:35 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/largeobject.out 2021-10-30 22:09:24 -0600 @@ -317,165 +317,10 @@ TRUNCATE lotest_stash_values; INSERT INTO lotest_stash_values (loid) SELECT lo_import('/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/data/tenk.data'); -BEGIN; -UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); --- verify length of large object -SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; - lo_lseek ----------- - 670800 -(1 row) - --- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block --- edge case -SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values; - lo_lseek ----------- - 2030 -(1 row) - --- this should get half of the value from page 0 and half from page 1 of the --- large object -SELECT loread(fd, 36) FROM lotest_stash_values; - loread ------------------------------------------------------------------ - AAA\011FBAAAA\011VVVVxx\0122513\01132\0111\0111\0113\01113\0111 -(1 row) - -SELECT lo_tell(fd) FROM lotest_stash_values; - lo_tell ---------- - 2066 -(1 row) - -SELECT lo_lseek(fd, -26, 1) FROM lotest_stash_values; - lo_lseek ----------- - 2040 -(1 row) - -SELECT lowrite(fd, 'abcdefghijklmnop') FROM lotest_stash_values; - lowrite ---------- - 16 -(1 row) - -SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values; - lo_lseek ----------- - 2030 -(1 row) - -SELECT loread(fd, 36) FROM lotest_stash_values; - loread ------------------------------------------------------ - AAA\011FBAAAAabcdefghijklmnop1\0111\0113\01113\0111 -(1 row) - -SELECT lo_close(fd) FROM lotest_stash_values; - lo_close ----------- - 0 -(1 row) - -END; -SELECT lo_export(loid, '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lotest.txt') FROM lotest_stash_values; - lo_export ------------ - 1 -(1 row) - -\lo_import '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lotest.txt' -\set newloid :LASTOID --- just make sure \lo_export does not barf -\lo_export :newloid '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lotest2.txt' --- This is a hack to test that export/import are reversible --- This uses knowledge about the inner workings of large object mechanism --- which should not be used outside it. This makes it a HACK -SELECT pageno, data FROM pg_largeobject WHERE loid = (SELECT loid from lotest_stash_values) -EXCEPT -SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid; - pageno | data ---------+------ -(0 rows) - -SELECT lo_unlink(loid) FROM lotest_stash_values; - lo_unlink ------------ - 1 -(1 row) - -TRUNCATE lotest_stash_values; -\lo_unlink :newloid -\lo_import '/home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/lotest.txt' -\set newloid_1 :LASTOID -SELECT lo_from_bytea(0, lo_get(:newloid_1)) AS newloid_2 -\gset -SELECT md5(lo_get(:newloid_1)) = md5(lo_get(:newloid_2)); - ?column? ----------- - t -(1 row) - -SELECT lo_get(:newloid_1, 0, 20); - lo_get -------------------------------------------- - 8800\0110\0110\0110\0110\0110\0110\011800 -(1 row) - -SELECT lo_get(:newloid_1, 10, 20); - lo_get -------------------------------------------- - \0110\0110\0110\011800\011800\0113800\011 -(1 row) - -SELECT lo_put(:newloid_1, 5, decode('afafafaf', 'hex')); - lo_put --------- - -(1 row) - -SELECT lo_get(:newloid_1, 0, 20); - lo_get -------------------------------------------------- - 8800\011\257\257\257\2570\0110\0110\0110\011800 -(1 row) - -SELECT lo_put(:newloid_1, 4294967310, 'foo'); - lo_put --------- - -(1 row) - -SELECT lo_get(:newloid_1); -ERROR: large object read request is too large -SELECT lo_get(:newloid_1, 4294967294, 100); - lo_get ---------------------------------------------------------------------- - \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000foo -(1 row) - -\lo_unlink :newloid_1 -\lo_unlink :newloid_2 --- This object is left in the database for pg_dump test purposes -SELECT lo_from_bytea(0, E'\\xdeadbeef') AS newloid -\gset -SET bytea_output TO hex; -SELECT lo_get(:newloid); - lo_get ------------- - \xdeadbeef -(1 row) - --- Create one more object that we leave behind for testing pg_dump/pg_upgrade; --- this one intentionally has an OID in the system range -SELECT lo_create(2121); - lo_create ------------ - 2121 -(1 row) - -COMMENT ON LARGE OBJECT 2121 IS 'testing comments'; --- Clean up -DROP TABLE lotest_stash_values; -DROP ROLE regress_lo_user; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/with.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/with.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/with.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/with.out 2021-10-30 22:09:24 -0600 @@ -351,2817 +351,10 @@ SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred ) SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); - count -------- - 50 -(1 row) - --- via a VIEW -CREATE TEMPORARY VIEW vsubdepartment AS - WITH RECURSIVE subdepartment AS - ( - -- non recursive term - SELECT * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id - ) - SELECT * FROM subdepartment; -SELECT * FROM vsubdepartment ORDER BY name; - id | parent_department | name -----+-------------------+------ - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 6 | 4 | F -(5 rows) - --- Check reverse listing -SELECT pg_get_viewdef('vsubdepartment'::regclass); - pg_get_viewdef ------------------------------------------------ - WITH RECURSIVE subdepartment AS ( + - SELECT department.id, + - department.parent_department, + - department.name + - FROM department + - WHERE (department.name = 'A'::text)+ - UNION ALL + - SELECT d.id, + - d.parent_department, + - d.name + - FROM department d, + - subdepartment sd + - WHERE (d.parent_department = sd.id)+ - ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + - FROM subdepartment; -(1 row) - -SELECT pg_get_viewdef('vsubdepartment'::regclass, true); - pg_get_viewdef ---------------------------------------------- - WITH RECURSIVE subdepartment AS ( + - SELECT department.id, + - department.parent_department, + - department.name + - FROM department + - WHERE department.name = 'A'::text+ - UNION ALL + - SELECT d.id, + - d.parent_department, + - d.name + - FROM department d, + - subdepartment sd + - WHERE d.parent_department = sd.id+ - ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + - FROM subdepartment; -(1 row) - --- Another reverse-listing example -CREATE VIEW sums_1_100 AS -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t WHERE n < 100 -) -SELECT sum(n) FROM t; -\d+ sums_1_100 - View "public.sums_1_100" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+--------+-----------+----------+---------+---------+------------- - sum | bigint | | | | plain | -View definition: - WITH RECURSIVE t(n) AS ( - VALUES (1) - UNION ALL - SELECT t_1.n + 1 - FROM t t_1 - WHERE t_1.n < 100 - ) - SELECT sum(t.n) AS sum - FROM t; - --- corner case in which sub-WITH gets initialized first -with recursive q as ( - select * from department - union all - (with x as (select * from q) - select * from x) - ) -select * from q limit 24; - id | parent_department | name -----+-------------------+------ - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G -(24 rows) - -with recursive q as ( - select * from department - union all - (with recursive x as ( - select * from department - union all - (select * from q union all select * from x) - ) - select * from x) - ) -select * from q limit 32; - id | parent_department | name -----+-------------------+------ - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G -(32 rows) - --- recursive term has sub-UNION -WITH RECURSIVE t(i,j) AS ( - VALUES (1,2) - UNION ALL - SELECT t2.i, t.j+1 FROM - (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 - JOIN t ON (t2.i = t.i+1)) - SELECT * FROM t; - i | j ----+--- - 1 | 2 - 2 | 3 - 3 | 4 -(3 rows) - --- --- different tree example --- -CREATE TEMPORARY TABLE tree( - id INTEGER PRIMARY KEY, - parent_id INTEGER REFERENCES tree(id) -); -INSERT INTO tree -VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), - (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); --- --- get all paths from "second level" nodes to leaf nodes --- -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - ORDER BY t1.id, t2.id; - id | path | id | path -----+------+----+------------- - 2 | {2} | 4 | {2,4} - 2 | {2} | 5 | {2,5} - 2 | {2} | 6 | {2,6} - 2 | {2} | 9 | {2,4,9} - 2 | {2} | 10 | {2,4,10} - 2 | {2} | 14 | {2,4,9,14} - 3 | {3} | 7 | {3,7} - 3 | {3} | 8 | {3,8} - 3 | {3} | 11 | {3,7,11} - 3 | {3} | 12 | {3,7,12} - 3 | {3} | 13 | {3,7,13} - 3 | {3} | 15 | {3,7,11,15} - 3 | {3} | 16 | {3,7,11,16} -(13 rows) - --- just count 'em -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - GROUP BY t1.id - ORDER BY t1.id; - id | count -----+------- - 2 | 6 - 3 | 7 -(2 rows) - --- this variant tickled a whole-row-variable bug in 8.4devel -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON -(t1.id=t2.id); - id | path | t2 -----+-------------+-------------------- - 1 | {} | (1,{}) - 2 | {2} | (2,{2}) - 3 | {3} | (3,{3}) - 4 | {2,4} | (4,"{2,4}") - 5 | {2,5} | (5,"{2,5}") - 6 | {2,6} | (6,"{2,6}") - 7 | {3,7} | (7,"{3,7}") - 8 | {3,8} | (8,"{3,8}") - 9 | {2,4,9} | (9,"{2,4,9}") - 10 | {2,4,10} | (10,"{2,4,10}") - 11 | {3,7,11} | (11,"{3,7,11}") - 12 | {3,7,12} | (12,"{3,7,12}") - 13 | {3,7,13} | (13,"{3,7,13}") - 14 | {2,4,9,14} | (14,"{2,4,9,14}") - 15 | {3,7,11,15} | (15,"{3,7,11,15}") - 16 | {3,7,11,16} | (16,"{3,7,11,16}") -(16 rows) - --- SEARCH clause -create temp table graph0( f int, t int, label text ); -insert into graph0 values - (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'); -explain (verbose, costs off) -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; - QUERY PLAN ----------------------------------------------------------------------------------------------- - Sort - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq - Sort Key: search_graph.seq - CTE search_graph - -> Recursive Union - -> Seq Scan on pg_temp.graph0 g - Output: g.f, g.t, g.label, ARRAY[ROW(g.f, g.t)] - -> Merge Join - Output: g_1.f, g_1.t, g_1.label, array_cat(sg.seq, ARRAY[ROW(g_1.f, g_1.t)]) - Merge Cond: (g_1.f = sg.t) - -> Sort - Output: g_1.f, g_1.t, g_1.label - Sort Key: g_1.f - -> Seq Scan on pg_temp.graph0 g_1 - Output: g_1.f, g_1.t, g_1.label - -> Sort - Output: sg.seq, sg.t - Sort Key: sg.t - -> WorkTable Scan on search_graph sg - Output: sg.seq, sg.t - -> CTE Scan on search_graph - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq -(22 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} -(7 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union distinct - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} -(7 rows) - -explain (verbose, costs off) -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - QUERY PLAN -------------------------------------------------------------------------------------------------- - Sort - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq - Sort Key: search_graph.seq - CTE search_graph - -> Recursive Union - -> Seq Scan on pg_temp.graph0 g - Output: g.f, g.t, g.label, ROW('0'::bigint, g.f, g.t) - -> Merge Join - Output: g_1.f, g_1.t, g_1.label, ROW(int8inc((sg.seq)."*DEPTH*"), g_1.f, g_1.t) - Merge Cond: (g_1.f = sg.t) - -> Sort - Output: g_1.f, g_1.t, g_1.label - Sort Key: g_1.f - -> Seq Scan on pg_temp.graph0 g_1 - Output: g_1.f, g_1.t, g_1.label - -> Sort - Output: sg.seq, sg.t - Sort Key: sg.t - -> WorkTable Scan on search_graph sg - Output: sg.seq, sg.t - -> CTE Scan on search_graph - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq -(22 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+--------- - 1 | 2 | arc 1 -> 2 | (0,1,2) - 1 | 3 | arc 1 -> 3 | (0,1,3) - 1 | 4 | arc 1 -> 4 | (0,1,4) - 2 | 3 | arc 2 -> 3 | (0,2,3) - 4 | 5 | arc 4 -> 5 | (0,4,5) - 2 | 3 | arc 2 -> 3 | (1,2,3) - 4 | 5 | arc 4 -> 5 | (1,4,5) -(7 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union distinct - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+--------- - 1 | 2 | arc 1 -> 2 | (0,1,2) - 1 | 3 | arc 1 -> 3 | (0,1,3) - 1 | 4 | arc 1 -> 4 | (0,1,4) - 2 | 3 | arc 2 -> 3 | (0,2,3) - 4 | 5 | arc 4 -> 5 | (0,4,5) - 2 | 3 | arc 2 -> 3 | (1,2,3) - 4 | 5 | arc 4 -> 5 | (1,4,5) -(7 rows) - --- various syntax errors -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by foo, tar set seq -select * from search_graph; -ERROR: search column "foo" not in WITH query column list -LINE 7: ) search depth first by foo, tar set seq - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set label -select * from search_graph; -ERROR: search sequence column name "label" already used in WITH query column list -LINE 7: ) search depth first by f, t set label - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t, f set seq -select * from search_graph; -ERROR: search column "f" specified more than once -LINE 7: ) search depth first by f, t, f set seq - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; -ERROR: with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - (select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t) -) search depth first by f, t set seq -select * from search_graph order by seq; -ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT --- test ruleutils and view expansion -create temp view v_search as -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select f, t, label from search_graph; -select pg_get_viewdef('v_search'); - pg_get_viewdef ------------------------------------------------- - WITH RECURSIVE search_graph(f, t, label) AS (+ - SELECT g.f, + - g.t, + - g.label + - FROM graph0 g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph0 g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + - FROM search_graph; -(1 row) - -select * from v_search; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 -(7 rows) - --- --- test cycle detection --- -create temp table graph( f int, t int, label text ); -insert into graph values - (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1'); -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union all - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- UNION DISTINCT exercises row type hashing support -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union distinct - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- ordering by the path column has same effect as SEARCH DEPTH FIRST -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union all - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph order by path; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} -(25 rows) - --- CYCLE clause -explain (verbose, costs off) -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select * from search_graph; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - CTE Scan on search_graph - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.is_cycle, search_graph.path - CTE search_graph - -> Recursive Union - -> Seq Scan on pg_temp.graph g - Output: g.f, g.t, g.label, false, ARRAY[ROW(g.f, g.t)] - -> Merge Join - Output: g_1.f, g_1.t, g_1.label, CASE WHEN (ROW(g_1.f, g_1.t) = ANY (sg.path)) THEN true ELSE false END, array_cat(sg.path, ARRAY[ROW(g_1.f, g_1.t)]) - Merge Cond: (g_1.f = sg.t) - -> Sort - Output: g_1.f, g_1.t, g_1.label - Sort Key: g_1.f - -> Seq Scan on pg_temp.graph g_1 - Output: g_1.f, g_1.t, g_1.label - -> Sort - Output: sg.path, sg.t - Sort Key: sg.t - -> WorkTable Scan on search_graph sg - Output: sg.path, sg.t - Filter: (NOT sg.is_cycle) -(20 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union distinct - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to 'Y' default 'N' using path -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | N | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | N | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | N | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | N | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | N | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | Y | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | Y | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | Y | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- multiple CTEs -with recursive -graph(f, t, label) as ( - values (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1') -), -search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default false using path -select f, t, label from search_graph; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 5 | 1 | arc 5 -> 1 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - --- star expansion -with recursive a as ( - select 1 as b - union all - select * from a -) cycle b set c using p -select * from a; - b | c | p ----+---+----------- - 1 | f | {(1)} - 1 | t | {(1),(1)} -(2 rows) - --- search+cycle -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq - cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | seq | is_cycle | path ----+---+------------+-------------------------------------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq - cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | seq | is_cycle | path ----+---+------------+---------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | (0,1,2) | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | (0,1,3) | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | (0,2,3) | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | (0,1,4) | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | (0,4,5) | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | (0,5,1) | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | (1,1,2) | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (1,1,3) | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (1,1,4) | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (1,2,3) | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (1,4,5) | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (1,5,1) | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | (2,1,2) | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (2,1,3) | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (2,1,4) | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (2,2,3) | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (2,4,5) | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (2,5,1) | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | (3,1,2) | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (3,1,3) | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (3,1,4) | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (3,2,3) | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (3,4,5) | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (3,5,1) | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | (4,2,3) | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- various syntax errors -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle foo, tar set is_cycle using path -select * from search_graph; -ERROR: cycle column "foo" not in WITH query column list -LINE 7: ) cycle foo, tar set is_cycle using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default 55 using path -select * from search_graph; -ERROR: CYCLE types boolean and integer cannot be matched -LINE 7: ) cycle f, t set is_cycle to true default 55 using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to point '(1,1)' default point '(0,0)' using path -select * from search_graph; -ERROR: could not identify an equality operator for type point -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set label to true default false using path -select * from search_graph; -ERROR: cycle mark column name "label" already used in WITH query column list -LINE 7: ) cycle f, t set label to true default false using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default false using label -select * from search_graph; -ERROR: cycle path column name "label" already used in WITH query column list -LINE 7: ) cycle f, t set is_cycle to true default false using label - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set foo to true default false using foo -select * from search_graph; -ERROR: cycle mark column name and cycle path column name are the same -LINE 7: ) cycle f, t set foo to true default false using foo - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t, f set is_cycle to true default false using path -select * from search_graph; -ERROR: cycle column "f" specified more than once -LINE 7: ) cycle f, t, f set is_cycle to true default false using pat... - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set foo - cycle f, t set foo to true default false using path -select * from search_graph; -ERROR: search sequence column name and cycle mark column name are the same -LINE 7: ) search depth first by f, t set foo - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set foo - cycle f, t set is_cycle to true default false using foo -select * from search_graph; -ERROR: search sequence column name and cycle path column name are the same -LINE 7: ) search depth first by f, t set foo - ^ --- test ruleutils and view expansion -create temp view v_cycle1 as -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select f, t, label from search_graph; -create temp view v_cycle2 as -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to 'Y' default 'N' using path -select f, t, label from search_graph; -select pg_get_viewdef('v_cycle1'); - pg_get_viewdef ------------------------------------------------- - WITH RECURSIVE search_graph(f, t, label) AS (+ - SELECT g.f, + - g.t, + - g.label + - FROM graph g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + - FROM search_graph; -(1 row) - -select pg_get_viewdef('v_cycle2'); - pg_get_viewdef ------------------------------------------------------------------------------ - WITH RECURSIVE search_graph(f, t, label) AS ( + - SELECT g.f, + - g.t, + - g.label + - FROM graph g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + - FROM search_graph; -(1 row) - -select * from v_cycle1; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - -select * from v_cycle2; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - --- --- test multiple WITH queries --- -WITH RECURSIVE - y (id) AS (VALUES (1)), - x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - --- forward reference OK -WITH RECURSIVE - x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS (values (1)) - SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | - 7 | - 8 | - 9 | - 10 | -(10 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | -(6 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 2 - 3 - 4 - 3 - 4 - 5 - 4 - 5 - 6 - 5 - 6 - 7 - 6 - 7 - 8 - 7 - 8 - 9 - 8 - 9 - 10 - 9 - 10 - 10 -(27 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 1 - 2 - 3 - 2 - 3 - 4 - 2 - 3 - 4 - 3 - 4 - 5 - 3 - 4 - 5 - 4 - 5 - 6 - 4 - 5 - 6 - 5 - 6 - 7 - 5 - 6 - 7 - 6 - 7 - 8 - 6 - 7 - 8 - 7 - 8 - 9 - 7 - 8 - 9 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 9 - 10 - 10 - 10 -(54 rows) - --- --- Test WITH attached to a data-modifying statement --- -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); -WITH t AS ( - SELECT a FROM y -) -INSERT INTO y -SELECT a+20 FROM t RETURNING *; - a ----- - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(20 rows) - -WITH t AS ( - SELECT a FROM y -) -UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - -WITH RECURSIVE t(a) AS ( - SELECT 11 - UNION ALL - SELECT a+1 FROM t WHERE a < 50 -) -DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - -DROP TABLE y; --- --- error cases --- --- INTERSECT -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR... - ^ --- EXCEPT -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - ^ -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ... - ^ --- no non-recursive term -WITH RECURSIVE x(n) AS (SELECT n FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x) - ^ --- recursive term in the left hand side (strictly speaking, should allow this) -WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within its non-recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - ^ -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); --- LEFT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) - ^ --- RIGHT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) - ^ --- FULL JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) - ^ --- subquery -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x - WHERE n IN (SELECT * FROM x)) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 2: WHERE n IN (SELECT * FROM x)) - ^ --- aggregate functions -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO... - ^ --- ORDER BY -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - SELECT * FROM x; -ERROR: ORDER BY in a recursive query is not implemented -LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - ^ --- LIMIT/OFFSET -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - SELECT * FROM x; -ERROR: OFFSET in a recursive query is not implemented -LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - ^ --- FOR UPDATE -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) - SELECT * FROM x; -ERROR: FOR UPDATE/SHARE in a recursive query is not implemented --- target list has a recursive query name -WITH RECURSIVE x(id) AS (values (1) - UNION ALL - SELECT (SELECT * FROM x) FROM x WHERE id < 5 -) SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5 - ^ --- mutual recursive query (not implemented) -WITH RECURSIVE - x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), - y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; -ERROR: mutual recursion between WITH items is not implemented -LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ... - ^ --- non-linear recursion is not allowed -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - SELECT * FROM - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) AS t -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 7: SELECT i+1 FROM foo WHERE i < 5) AS t - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - EXCEPT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear within EXCEPT -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - INTERSECT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ --- Wrong type induced from non-recursive term -WITH RECURSIVE foo(i) AS - (SELECT i FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall -LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- rejects different typmod, too (should we allow this?) -WITH RECURSIVE foo(i) AS - (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall -LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- disallow OLD/NEW reference in CTE -CREATE TEMPORARY TABLE x (n integer); -CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD - WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; -ERROR: cannot refer to OLD within WITH query --- --- test for bug #4902 --- -with cte(foo) as ( values(42) ) values((select foo from cte)); - column1 ---------- - 42 -(1 row) - -with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; - foo ------ - 42 -(1 row) - --- test CTE referencing an outer-level variable (to see that changed-parameter --- signaling still works properly after fixing this bug) -select ( with cte(foo) as ( values(f1) ) - select (select foo from cte) ) -from int4_tbl; - foo -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - -select ( with cte(foo) as ( values(f1) ) - values((select foo from cte)) ) -from int4_tbl; - column1 -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - --- --- test for nested-recursive-WITH bug --- -WITH RECURSIVE t(j) AS ( - WITH RECURSIVE s(i) AS ( - VALUES (1) - UNION ALL - SELECT i+1 FROM s WHERE i < 10 - ) - SELECT i FROM s - UNION ALL - SELECT j+1 FROM t WHERE j < 10 -) -SELECT * FROM t; - j ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 5 - 6 - 7 - 8 - 9 - 10 - 6 - 7 - 8 - 9 - 10 - 7 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 10 -(55 rows) - --- --- test WITH attached to intermediate-level set operation --- -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM innermost - UNION SELECT 3) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 - 3 -(3 rows) - -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost -- fail - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; -ERROR: relation "outermost" does not exist -LINE 4: SELECT * FROM outermost - ^ -DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query. -HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. -WITH RECURSIVE outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 -(2 rows) - -WITH RECURSIVE outermost(x) AS ( - WITH innermost as (SELECT 2 FROM outermost) -- fail - SELECT * FROM innermost - UNION SELECT * from outermost -) -SELECT * FROM outermost ORDER BY 1; -ERROR: recursive reference to query "outermost" must not appear within a subquery -LINE 2: WITH innermost as (SELECT 2 FROM outermost) - ^ --- --- This test will fail with the old implementation of PARAM_EXEC parameter --- assignment, because the "q1" Var passed down to A's targetlist subselect --- looks exactly like the "A.id" Var passed down to C's subselect, causing --- the old code to give them the same runtime PARAM_EXEC slot. But the --- lifespans of the two parameters overlap, thanks to B also reading A. --- -with -A as ( select q2 as id, (select q1) as x from int8_tbl ), -B as ( select id, row_number() over (partition by id) as r from A ), -C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) -select * from C; - id | array --------------------+------------------------------------- - 456 | {456} - 4567890123456789 | {4567890123456789,4567890123456789} - 123 | {123} - 4567890123456789 | {4567890123456789,4567890123456789} - -4567890123456789 | {-4567890123456789} -(5 rows) - --- --- Test CTEs read in non-initialization orders --- -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ALL ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - --- --- Data-modifying statements in WITH --- --- INSERT ... RETURNING -WITH t AS ( - INSERT INTO y - VALUES - (11), - (12), - (13), - (14), - (15), - (16), - (17), - (18), - (19), - (20) - RETURNING * -) -SELECT * FROM t; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - --- UPDATE ... RETURNING -WITH t AS ( - UPDATE y - SET a=a+1 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - -SELECT * FROM y; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - --- DELETE ... RETURNING -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(9 rows) - -SELECT * FROM y; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(11 rows) - --- forward reference -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT a+5 FROM t2 WHERE a > 5 - RETURNING * -), t2 AS ( - UPDATE y SET a=a-11 RETURNING * -) -SELECT * FROM t -UNION ALL -SELECT * FROM t2; - a ----- - 11 - 12 - 13 - 14 - 15 - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(16 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 -(16 rows) - --- unconditional DO INSTEAD rule -CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD - INSERT INTO y VALUES(42) RETURNING *; -WITH t AS ( - DELETE FROM y RETURNING * -) -SELECT * FROM t; - a ----- - 42 -(1 row) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 -(17 rows) - -DROP RULE y_rule ON y; --- check merging of outer CTE with CTE in a rule action -CREATE TEMP TABLE bug6051 AS - select i from generate_series(1,3) as t(i); -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -CREATE TEMP TABLE bug6051_2 (i int); -CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - VALUES(NEW.i); -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- -(0 rows) - -SELECT * FROM bug6051_2; - i ---- - 1 - 2 - 3 -(3 rows) - --- check INSERT...SELECT rule actions are disallowed on commands --- that have modifyingCTEs -CREATE OR REPLACE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - SELECT NEW.i; -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -ERROR: INSERT...SELECT rule actions are not supported for queries having data-modifying statements in WITH --- silly example to verify that hasModifyingCTE flag is propagated -CREATE TEMP TABLE bug6051_3 AS - SELECT a FROM generate_series(11,13) AS a; -CREATE RULE bug6051_3_ins AS ON INSERT TO bug6051_3 DO INSTEAD - SELECT i FROM bug6051_2; -BEGIN; SET LOCAL force_parallel_mode = on; -WITH t1 AS ( DELETE FROM bug6051_3 RETURNING * ) - INSERT INTO bug6051_3 SELECT * FROM t1; - i ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(9 rows) - -COMMIT; -SELECT * FROM bug6051_3; - a ---- -(0 rows) - --- a truly recursive CTE in the same list -WITH RECURSIVE t(a) AS ( - SELECT 0 - UNION ALL - SELECT a+1 FROM t WHERE a+1 < 5 -), t2 as ( - INSERT INTO y - SELECT * FROM t RETURNING * -) -SELECT * FROM t2 JOIN y USING (a) ORDER BY a; - a ---- - 0 - 1 - 2 - 3 - 4 -(5 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 - 0 - 1 - 2 - 3 - 4 -(22 rows) - --- data-modifying WITH in a modifying statement -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -INSERT INTO y SELECT -a FROM t RETURNING *; - a ------ - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(16 rows) - -SELECT * FROM y; - a ------ - 11 - 12 - 13 - 14 - 15 - 42 - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(22 rows) - --- check that WITH query is run to completion even if outer query isn't -WITH t AS ( - UPDATE y SET a = a * 100 RETURNING * -) -SELECT * FROM t LIMIT 10; - a ------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 -(10 rows) - -SELECT * FROM y; - a -------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 - -400 - -500 - -600 - -700 - -800 - -900 - -1000 - 0 - -100 - -200 - -300 - -400 -(22 rows) - --- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE -CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; -ALTER TABLE withz ADD UNIQUE (k); -WITH t AS ( - INSERT INTO withz SELECT i, 'insert' - FROM generate_series(0, 16) i - ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' - RETURNING * -) -SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; - k | v | a ----+--------+--- - 0 | insert | 0 - 0 | insert | 0 -(2 rows) - --- Test EXCLUDED.* reference within CTE -WITH aa AS ( - INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v - WHERE withz.k != EXCLUDED.k - RETURNING * -) -SELECT * FROM aa; - k | v ----+--- -(0 rows) - --- New query/snapshot demonstrates side-effects of previous query. -SELECT * FROM withz ORDER BY k; - k | v -----+------------------ - 0 | insert - 1 | 1 v, now update - 2 | insert - 3 | insert - 4 | 4 v, now update - 5 | insert - 6 | insert - 7 | 7 v, now update - 8 | insert - 9 | insert - 10 | 10 v, now update - 11 | insert - 12 | insert - 13 | 13 v, now update - 14 | insert - 15 | insert - 16 | 16 v, now update -(17 rows) - --- --- Ensure subqueries within the update clause work, even if they --- reference outside values --- -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); --- Update a row more than once, in different parts of a wCTE. That is --- an allowed, presumably very rare, edge case, but since it was --- broken in the past, having a test seems worthwhile. -WITH simpletup AS ( - SELECT 2 k, 'Green' v), -upsert_cte AS ( - INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO - UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) - RETURNING k, v) -INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO -UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) -RETURNING k, v; - k | v ----+--- -(0 rows) - -DROP TABLE withz; --- check that run to completion happens in proper ordering -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 3); -CREATE TEMPORARY TABLE yy (a INTEGER); -WITH RECURSIVE t1 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -), t2 AS ( - INSERT INTO yy SELECT * FROM t1 RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 -(6 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 -(3 rows) - -WITH RECURSIVE t1 AS ( - INSERT INTO yy SELECT * FROM t2 RETURNING * -), t2 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(12 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(9 rows) - --- triggers -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 10); -CREATE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger: a = %', new.a; - return new; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (21), - (22), - (23) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger: a = 21 -NOTICE: y_trigger: a = 22 -NOTICE: y_trigger: a = 23 - a ----- - 21 - 22 - 23 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 -(13 rows) - -DROP TRIGGER y_trig ON y; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (31), - (32), - (33) - RETURNING * -) -SELECT * FROM t LIMIT 1; -NOTICE: y_trigger: a = 31 -NOTICE: y_trigger: a = 32 -NOTICE: y_trigger: a = 33 - a ----- - 31 -(1 row) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 -(16 rows) - -DROP TRIGGER y_trig ON y; -CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger'; - return null; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (41), - (42), - (43) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger - a ----- - 41 - 42 - 43 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 - 41 - 42 - 43 -(19 rows) - -DROP TRIGGER y_trig ON y; -DROP FUNCTION y_trigger(); --- WITH attached to inherited UPDATE or DELETE -CREATE TEMP TABLE parent ( id int, val text ); -CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); -CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); -INSERT INTO parent VALUES ( 1, 'p1' ); -INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); -INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); -WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) -UPDATE parent SET id = id + totalid FROM rcte; -SELECT * FROM parent; - id | val -----+----- - 72 | p1 - 82 | c11 - 83 | c12 - 94 | c21 - 95 | c22 -(5 rows) - -WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) -UPDATE parent SET id = id + newid FROM wcte; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 - 137 | c22 -(6 rows) - -WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) -DELETE FROM parent USING rcte WHERE id = maxid; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 -(5 rows) - -WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) -DELETE FROM parent USING wcte WHERE id = newid; -SELECT * FROM parent; - id | val ------+------ - 114 | p1 - 124 | c11 - 125 | c12 - 136 | c21 - 42 | new2 -(5 rows) - --- check EXPLAIN VERBOSE for a wCTE with RETURNING -EXPLAIN (VERBOSE, COSTS OFF) -WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) -DELETE FROM a USING wcte WHERE aa = q2; - QUERY PLAN ------------------------------------------------------------- - Delete on public.a - Delete on public.a a_1 - Delete on public.b a_2 - Delete on public.c a_3 - Delete on public.d a_4 - CTE wcte - -> Insert on public.int8_tbl - Output: int8_tbl.q2 - -> Result - Output: '42'::bigint, '47'::bigint - -> Hash Join - Output: wcte.*, a.tableoid, a.ctid - Hash Cond: (a.aa = wcte.q2) - -> Append - -> Seq Scan on public.a a_1 - Output: a_1.aa, a_1.tableoid, a_1.ctid - -> Seq Scan on public.b a_2 - Output: a_2.aa, a_2.tableoid, a_2.ctid - -> Seq Scan on public.c a_3 - Output: a_3.aa, a_3.tableoid, a_3.ctid - -> Seq Scan on public.d a_4 - Output: a_4.aa, a_4.tableoid, a_4.ctid - -> Hash - Output: wcte.*, wcte.q2 - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 -(26 rows) - --- error cases --- data-modifying WITH tries to use its own output -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT * FROM t -) -VALUES(FALSE); -ERROR: recursive query "t" must not contain data-modifying statements -LINE 1: WITH RECURSIVE t AS ( - ^ --- no RETURNING in a referenced data-modifying WITH -WITH t AS ( - INSERT INTO y VALUES(0) -) -SELECT * FROM t; -ERROR: WITH query "t" does not have a RETURNING clause -LINE 4: SELECT * FROM t; - ^ --- data-modifying WITH allowed only at the top level -SELECT * FROM ( - WITH t AS (UPDATE y SET a=a+1 RETURNING *) - SELECT * FROM t -) ss; -ERROR: WITH clause containing a data-modifying statement must be at the top level -LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *) - ^ --- most variants of rules aren't allowed -CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTHING; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTIFY foo; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO ALSO NOTIFY foo; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO ALSO rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y - DO INSTEAD (NOTIFY foo; NOTIFY bar); -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH -DROP RULE y_rule ON y; --- check that parser lookahead for WITH doesn't cause any odd behavior -create table foo (with baz); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with baz); - ^ -create table foo (with ordinality); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with ordinality); - ^ -with ordinality as (select 1 as x) select * from ordinality; - x ---- - 1 -(1 row) - --- check sane response to attempt to modify CTE relation -WITH test AS (SELECT 42) INSERT INTO test VALUES (1); -ERROR: relation "test" does not exist -LINE 1: WITH test AS (SELECT 42) INSERT INTO test VALUES (1); - ^ --- check response to attempt to modify table with same name as a CTE (perhaps --- surprisingly it works, because CTEs don't hide tables from data-modifying --- statements) -create temp table test (i int); -with test as (select 42) insert into test select * from test; -select * from test; - i ----- - 42 -(1 row) - -drop table test; +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/xml_1.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/xml.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/xml_1.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/xml.out 2021-10-30 22:09:24 -0600 @@ -3,1412 +3,19 @@ data xml ); INSERT INTO xmltest VALUES (1, 'one'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (1, 'one'); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. INSERT INTO xmltest VALUES (2, 'two'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (2, 'two'); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. INSERT INTO xmltest VALUES (3, '', NULL, ''); -ERROR: unsupported XML feature -LINE 1: SELECT xmlconcat('', NULL, '', NULL, ''); -ERROR: unsupported XML feature -LINE 1: SELECT xmlconcat('', NULL, 'r'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, xml 'br'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, array[1, 2, 3]); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SET xmlbinary TO base64; -SELECT xmlelement(name foo, bytea 'bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SET xmlbinary TO hex; -SELECT xmlelement(name foo, bytea 'bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, xmlattributes(true as bar)); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as bar)); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar)); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'br' as funnier)); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content ' '); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content 'abc'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content 'x'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content '&'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content '&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content '&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(content ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document ' '); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document 'abc'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document 'x'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document '&'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document '&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document '&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlparse(document ''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name foo); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name xml); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name xmlstuff); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name foo, 'bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name foo, 'in?>valid'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name foo, null); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name xml, null); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name xmlstuff, null); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name "xml-stylesheet", 'href="mystyle.css" type="text/css"'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name foo, ' bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot(xml '', version no value, standalone no value); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot(xml '', version no value, standalone no... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot(xml '', version '2.0'); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot(xml '', version '2.0'); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot(xml '', version no value, standalone yes); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot(xml '', version no value, standalone ye... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot(xml '', version no value, standalone yes); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot(xml '', version no... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot(xmlroot(xml '', version '1.0'), version '1.1', standalone no); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot(xmlroot(xml '', version '1.0'), version... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot('', version no value, standalone no); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot('... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot('', version no value, standalone no value); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot('... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot('', version no value); -ERROR: unsupported XML feature -LINE 1: SELECT xmlroot('... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlroot ( - xmlelement ( - name gazonk, - xmlattributes ( - 'val' AS name, - 1 + 1 AS num - ), - xmlelement ( - NAME qux, - 'foo' - ) - ), - version '1.0', - standalone yes -); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlserialize(content data as character varying(20)) FROM xmltest; - xmlserialize --------------- -(0 rows) - -SELECT xmlserialize(content 'good' as char(10)); -ERROR: unsupported XML feature -LINE 1: SELECT xmlserialize(content 'good' as char(10)); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlserialize(document 'bad' as text); -ERROR: unsupported XML feature -LINE 1: SELECT xmlserialize(document 'bad' as text); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml 'bar' IS DOCUMENT; -ERROR: unsupported XML feature -LINE 1: SELECT xml 'bar' IS DOCUMENT; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml 'barfoo' IS DOCUMENT; -ERROR: unsupported XML feature -LINE 1: SELECT xml 'barfoo' IS DOCUMENT; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml '' IS NOT DOCUMENT; -ERROR: unsupported XML feature -LINE 1: SELECT xml '' IS NOT DOCUMENT; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml 'abc' IS NOT DOCUMENT; -ERROR: unsupported XML feature -LINE 1: SELECT xml 'abc' IS NOT DOCUMENT; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT '<>' IS NOT DOCUMENT; -ERROR: unsupported XML feature -LINE 1: SELECT '<>' IS NOT DOCUMENT; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlagg(data) FROM xmltest; - xmlagg --------- - -(1 row) - -SELECT xmlagg(data) FROM xmltest WHERE id > 10; - xmlagg --------- - -(1 row) - -SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- Check mapping SQL identifier to XML name -SELECT xmlpi(name ":::_xml_abc135.%-&_"); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmlpi(name "123"); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -PREPARE foo (xml) AS SELECT xmlconcat('', $1); -ERROR: unsupported XML feature -LINE 1: PREPARE foo (xml) AS SELECT xmlconcat('', $1); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SET XML OPTION DOCUMENT; -EXECUTE foo (''); -ERROR: prepared statement "foo" does not exist -EXECUTE foo ('bad'); -ERROR: prepared statement "foo" does not exist -SELECT xml ''; -ERROR: unsupported XML feature -LINE 1: SELECT xml ''; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SET XML OPTION CONTENT; -EXECUTE foo (''); -ERROR: prepared statement "foo" does not exist -EXECUTE foo ('good'); -ERROR: prepared statement "foo" does not exist -SELECT xml ' '; -ERROR: unsupported XML feature -LINE 1: SELECT xml ' '; -ERROR: unsupported XML feature -LINE 1: SELECT xml ' '; -ERROR: unsupported XML feature -LINE 1: SELECT xml ''; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml ' oops '; -ERROR: unsupported XML feature -LINE 1: SELECT xml ' oops '; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml ' '; -ERROR: unsupported XML feature -LINE 1: SELECT xml ' '; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml ''; -ERROR: unsupported XML feature -LINE 1: SELECT xml ''; - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- Test backwards parsing -CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); -CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); -ERROR: unsupported XML feature -LINE 1: CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 as ":one:", 'deuce' as two), 'content&'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview5 AS SELECT xmlparse(content 'x'); -CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); -ERROR: unsupported XML feature -LINE 1: CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); -ERROR: unsupported XML feature -LINE 1: ...EATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as ... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); -ERROR: unsupported XML feature -LINE 1: ...EATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as ... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT table_name, view_definition FROM information_schema.views - WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------------- - xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; - xmlview5 | SELECT XMLPARSE(CONTENT 'x'::text STRIP WHITESPACE) AS "xmlparse"; -(2 rows) - --- Text XPath expressions evaluation -SELECT xpath('/value', data) FROM xmltest; - xpath -------- -(0 rows) - -SELECT xpath(NULL, NULL) IS NULL FROM xmltest; - ?column? ----------- -(0 rows) - -SELECT xpath('', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('//text()', 'number one'); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('//text()', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('//loc:piece/@id', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('//loc:piece', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('//loc:piece', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('//@value', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('''<>''', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('''<>''', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('count(//*)', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('count(//*)', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('count(//*)=0', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('count(//*)=0', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('count(//*)=3', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('count(//*)=3', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('name(/*)', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('name(/*)', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('/nosuchtag', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('/nosuchtag', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath('root', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('root', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- Round-trip non-ASCII data through xpath(). -DO $$ -DECLARE - xml_declaration text := ''; - degree_symbol text; - res xml[]; -BEGIN - -- Per the documentation, except when the server encoding is UTF8, xpath() - -- may not work on non-ASCII data. The untranslatable_character and - -- undefined_function traps below, currently dead code, will become relevant - -- if we remove this limitation. - IF current_setting('server_encoding') <> 'UTF8' THEN - RAISE LOG 'skip: encoding % unsupported for xpath', - current_setting('server_encoding'); - RETURN; - END IF; - - degree_symbol := convert_from('\xc2b0', 'UTF8'); - res := xpath('text()', (xml_declaration || - '' || degree_symbol || '')::xml); - IF degree_symbol <> res[1]::text THEN - RAISE 'expected % (%), got % (%)', - degree_symbol, convert_to(degree_symbol, 'UTF8'), - res[1], convert_to(res[1]::text, 'UTF8'); - END IF; -EXCEPTION - -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" - WHEN untranslatable_character - -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist - OR undefined_function - -- unsupported XML feature - OR feature_not_supported THEN - RAISE LOG 'skip: %', SQLERRM; -END -$$; --- Test xmlexists and xpath_exists -SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); -ERROR: unsupported XML feature -LINE 1: ...sts('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); -ERROR: unsupported XML feature -LINE 1: ...sts('//town[text() = ''Cwmbran'']' PASSING BY REF ''); -ERROR: unsupported XML feature -LINE 1: ...LECT xmlexists('count(/nosuchtag)' PASSING BY REF '')... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml); -ERROR: unsupported XML feature -LINE 1: ...ELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml); -ERROR: unsupported XML feature -LINE 1: ...ELECT xpath_exists('//town[text() = ''Cwmbran'']',''::xml); -ERROR: unsupported XML feature -LINE 1: SELECT xpath_exists('count(/nosuchtag)', ''::xml); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (4, 'BudvarMolsonfreeCarlinglots'::xml); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (5, 'MolsonBudvarfreeCarlinglots'::xml); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (6, 'MolsonfreeCarlinglots'::xml); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest VALUES (7, 'number one'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed('bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed('bar'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed('&'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed('&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed(''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed(''); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xml_is_well_formed('&idontexist;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SET xmloption TO CONTENT; -SELECT xml_is_well_formed('abc'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- Since xpath() deals with namespaces, it's a bit stricter about --- what's well-formed and what's not. If we don't obey these rules --- (i.e. ignore namespace-related errors from libxml), xpath() --- fails in subtle ways. The following would for example produce --- the xml value --- --- which is invalid because '<' may not appear un-escaped in --- attribute values. --- Since different libxml versions emit slightly different --- error messages, we suppress the DETAIL in this test. -\set VERBOSITY terse -SELECT xpath('/*', ''); -ERROR: unsupported XML feature at character 20 -\set VERBOSITY default --- Again, the XML isn't well-formed for namespace purposes -SELECT xpath('/*', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('/*', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- XPath deprecates relative namespaces, but they're not supposed to --- throw an error, only a warning. -SELECT xpath('/*', ''); -ERROR: unsupported XML feature -LINE 1: SELECT xpath('/*', ''); - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- External entity references should not leak filesystem information. -SELECT XMLPARSE(DOCUMENT ']>&c;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT XMLPARSE(DOCUMENT ']>&c;'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- This might or might not load the requested DTD, but it mustn't throw error. -SELECT XMLPARSE(DOCUMENT ' '); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- XMLPATH tests -CREATE TABLE xmldata(data xml); -INSERT INTO xmldata VALUES(' - - AU - Australia - 3 - - - CN - China - 3 - - - HK - HongKong - 3 - - - IN - India - 3 - - - JP - Japan - 3Sinzo Abe - - - SG - Singapore - 3791 - -'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmldata VALUES(' - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- XMLTABLE with columns -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - -CREATE VIEW xmltableview1 AS SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -SELECT * FROM xmltableview1; - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - -\sv xmltableview1 -CREATE OR REPLACE VIEW public.xmltableview1 AS - SELECT "xmltable".id, - "xmltable"._id, - "xmltable".country_name, - "xmltable".country_id, - "xmltable".region_id, - "xmltable".size, - "xmltable".unit, - "xmltable".premier_name - FROM ( SELECT xmldata.data - FROM xmldata) x, - LATERAL XMLTABLE(('/ROWS/ROW'::text) PASSING (x.data) COLUMNS id integer PATH ('@id'::text), _id FOR ORDINALITY, country_name text PATH ('COUNTRY_NAME/text()'::text) NOT NULL, country_id text PATH ('COUNTRY_ID'::text), region_id integer PATH ('REGION_ID'::text), size double precision PATH ('SIZE'::text), unit text PATH ('SIZE/@unit'::text), premier_name text DEFAULT ('not specified'::text) PATH ('PREMIER_NAME'::text)) -EXPLAIN (COSTS OFF) SELECT * FROM xmltableview1; - QUERY PLAN ------------------------------------------ - Nested Loop - -> Seq Scan on xmldata - -> Table Function Scan on "xmltable" -(3 rows) - -EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM xmltableview1; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: "xmltable".id, "xmltable"._id, "xmltable".country_name, "xmltable".country_id, "xmltable".region_id, "xmltable".size, "xmltable".unit, "xmltable".premier_name - -> Seq Scan on public.xmldata - Output: xmldata.data - -> Table Function Scan on "xmltable" - Output: "xmltable".id, "xmltable"._id, "xmltable".country_name, "xmltable".country_id, "xmltable".region_id, "xmltable".size, "xmltable".unit, "xmltable".premier_name - Table Function Call: XMLTABLE(('/ROWS/ROW'::text) PASSING (xmldata.data) COLUMNS id integer PATH ('@id'::text), _id FOR ORDINALITY, country_name text PATH ('COUNTRY_NAME/text()'::text) NOT NULL, country_id text PATH ('COUNTRY_ID'::text), region_id integer PATH ('REGION_ID'::text), size double precision PATH ('SIZE'::text), unit text PATH ('SIZE/@unit'::text), premier_name text DEFAULT ('not specified'::text) PATH ('PREMIER_NAME'::text)) -(7 rows) - --- XMLNAMESPACES tests -SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), - '/zz:rows/zz:row' - PASSING '10' - COLUMNS a int PATH 'zz:a'); -ERROR: unsupported XML feature -LINE 3: PASSING '10' - COLUMNS a int PATH 'zz:a'); -ERROR: unsupported XML feature -LINE 3: PASSING '10' - COLUMNS a int PATH 'a'); -ERROR: unsupported XML feature -LINE 3: PASSING '' - COLUMNS a text PATH 'foo/namespace::node()'); -ERROR: unsupported XML feature -LINE 2: PASSING '' - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- used in prepare statements -PREPARE pp AS -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -EXECUTE pp; - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int); - COUNTRY_NAME | REGION_ID ---------------+----------- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY, "COUNTRY_NAME" text, "REGION_ID" int); - id | COUNTRY_NAME | REGION_ID -----+--------------+----------- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int); - id | COUNTRY_NAME | REGION_ID -----+--------------+----------- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id'); - id ----- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY); - id ----- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH '.'); - id | COUNTRY_NAME | REGION_ID | rawdata -----+--------------+-----------+--------- -(0 rows) - -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH './*'); - id | COUNTRY_NAME | REGION_ID | rawdata -----+--------------+-----------+--------- -(0 rows) - -SELECT * FROM xmltable('/root' passing 'a1aa2a bbbbxxxcccc' COLUMNS element text); -ERROR: unsupported XML feature -LINE 1: SELECT * FROM xmltable('/root' passing 'a1aa1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail -ERROR: unsupported XML feature -LINE 1: SELECT * FROM xmltable('/root' passing 'a1a &"<>!foo]]>2' columns c text); -ERROR: unsupported XML feature -LINE 1: select * from xmltable('d/r' passing ''"&<>' COLUMNS ent text); -ERROR: unsupported XML feature -LINE 1: SELECT * FROM xmltable('/x/a' PASSING '''"&<>' COLUMNS ent xml); -ERROR: unsupported XML feature -LINE 1: SELECT * FROM xmltable('/x/a' PASSING '' Seq Scan on public.xmldata - Output: xmldata.data - -> Table Function Scan on "xmltable" - Output: "xmltable".id, "xmltable"._id, "xmltable".country_name, "xmltable".country_id, "xmltable".region_id, "xmltable".size, "xmltable".unit, "xmltable".premier_name - Table Function Call: XMLTABLE(('/ROWS/ROW'::text) PASSING (xmldata.data) COLUMNS id integer PATH ('@id'::text), _id FOR ORDINALITY, country_name text PATH ('COUNTRY_NAME'::text) NOT NULL, country_id text PATH ('COUNTRY_ID'::text), region_id integer PATH ('REGION_ID'::text), size double precision PATH ('SIZE'::text), unit text PATH ('SIZE/@unit'::text), premier_name text DEFAULT ('not specified'::text) PATH ('PREMIER_NAME'::text)) -(7 rows) - --- test qual -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; - COUNTRY_NAME | REGION_ID ---------------+----------- -(0 rows) - -EXPLAIN (VERBOSE, COSTS OFF) -SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: "xmltable"."COUNTRY_NAME", "xmltable"."REGION_ID" - -> Seq Scan on public.xmldata - Output: xmldata.data - -> Table Function Scan on "xmltable" - Output: "xmltable"."COUNTRY_NAME", "xmltable"."REGION_ID" - Table Function Call: XMLTABLE(('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]'::text) PASSING (xmldata.data) COLUMNS "COUNTRY_NAME" text, "REGION_ID" integer) - Filter: ("xmltable"."COUNTRY_NAME" = 'Japan'::text) -(8 rows) - --- should to work with more data -INSERT INTO xmldata VALUES(' - - CZ - Czech Republic - 2Milos Zeman - - - DE - Germany - 2 - - - FR - France - 2 - -'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmldata VALUES(' - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -INSERT INTO xmldata VALUES(' - - EG - Egypt - 1 - - - SD - Sudan - 1 - -'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmldata VALUES(' - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') - WHERE region_id = 2; - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - -EXPLAIN (VERBOSE, COSTS OFF) -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE', - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') - WHERE region_id = 2; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Nested Loop - Output: "xmltable".id, "xmltable"._id, "xmltable".country_name, "xmltable".country_id, "xmltable".region_id, "xmltable".size, "xmltable".unit, "xmltable".premier_name - -> Seq Scan on public.xmldata - Output: xmldata.data - -> Table Function Scan on "xmltable" - Output: "xmltable".id, "xmltable"._id, "xmltable".country_name, "xmltable".country_id, "xmltable".region_id, "xmltable".size, "xmltable".unit, "xmltable".premier_name - Table Function Call: XMLTABLE(('/ROWS/ROW'::text) PASSING (xmldata.data) COLUMNS id integer PATH ('@id'::text), _id FOR ORDINALITY, country_name text PATH ('COUNTRY_NAME'::text) NOT NULL, country_id text PATH ('COUNTRY_ID'::text), region_id integer PATH ('REGION_ID'::text), size double precision PATH ('SIZE'::text), unit text PATH ('SIZE/@unit'::text), premier_name text DEFAULT ('not specified'::text) PATH ('PREMIER_NAME'::text)) - Filter: ("xmltable".region_id = 2) -(8 rows) - --- should fail, NULL value -SELECT xmltable.* - FROM (SELECT data FROM xmldata) x, - LATERAL XMLTABLE('/ROWS/ROW' - PASSING data - COLUMNS id int PATH '@id', - _id FOR ORDINALITY, - country_name text PATH 'COUNTRY_NAME' NOT NULL, - country_id text PATH 'COUNTRY_ID', - region_id int PATH 'REGION_ID', - size float PATH 'SIZE' NOT NULL, - unit text PATH 'SIZE/@unit', - premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); - id | _id | country_name | country_id | region_id | size | unit | premier_name -----+-----+--------------+------------+-----------+------+------+-------------- -(0 rows) - --- if all is ok, then result is empty --- one line xml test -WITH - x AS (SELECT proname, proowner, procost::numeric, pronargs, - array_to_string(proargnames,',') as proargnames, - case when proargtypes <> '' then array_to_string(proargtypes::oid[],',') end as proargtypes - FROM pg_proc WHERE proname = 'f_leak'), - y AS (SELECT xmlelement(name proc, - xmlforest(proname, proowner, - procost, pronargs, - proargnames, proargtypes)) as proc - FROM x), - z AS (SELECT xmltable.* - FROM y, - LATERAL xmltable('/proc' PASSING proc - COLUMNS proname name, - proowner oid, - procost float, - pronargs int, - proargnames text, - proargtypes text)) - SELECT * FROM z - EXCEPT SELECT * FROM x; -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. --- multi line xml test, result should be empty too -WITH - x AS (SELECT proname, proowner, procost::numeric, pronargs, - array_to_string(proargnames,',') as proargnames, - case when proargtypes <> '' then array_to_string(proargtypes::oid[],',') end as proargtypes - FROM pg_proc), - y AS (SELECT xmlelement(name data, - xmlagg(xmlelement(name proc, - xmlforest(proname, proowner, procost, - pronargs, proargnames, proargtypes)))) as doc - FROM x), - z AS (SELECT xmltable.* - FROM y, - LATERAL xmltable('/data/proc' PASSING doc - COLUMNS proname name, - proowner oid, - procost float, - pronargs int, - proargnames text, - proargtypes text)) - SELECT * FROM z - EXCEPT SELECT * FROM x; -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -CREATE TABLE xmltest2(x xml, _path text); -INSERT INTO xmltest2 VALUES('1', 'A'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest2 VALUES('1', 'A')... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -INSERT INTO xmltest2 VALUES('2', 'B'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest2 VALUES('2', 'B')... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -INSERT INTO xmltest2 VALUES('3', 'C'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest2 VALUES('3', 'C')... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -INSERT INTO xmltest2 VALUES('2', 'D'); -ERROR: unsupported XML feature -LINE 1: INSERT INTO xmltest2 VALUES('2', 'D')... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -SELECT xmltable.* FROM xmltest2, LATERAL xmltable('/d/r' PASSING x COLUMNS a int PATH '' || lower(_path) || 'c'); - a ---- -(0 rows) - -SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH '.'); - a ---- -(0 rows) - -SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH 'x' DEFAULT ascii(_path) - 54); - a ---- -(0 rows) - --- XPath result can be boolean or number too -SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"', d boolean PATH '. = "a"', e integer PATH 'string-length(.)'); -ERROR: unsupported XML feature -LINE 1: SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml ... - ^ -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. -\x -SELECT * FROM XMLTABLE('*' PASSING 'pre&deeppost' COLUMNS x xml PATH 'node()', y xml PATH '/'); -ERROR: unsupported XML feature -LINE 1: SELECT * FROM XMLTABLE('*' PASSING 'pre"', b xml PATH '""'); -ERROR: unsupported XML feature -DETAIL: This functionality requires the server to be built with libxml support. -HINT: You need to rebuild PostgreSQL using --with-libxml. +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_join.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_join.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_join.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_join.out 2021-10-30 22:09:26 -0600 @@ -1,4864 +1,2 @@ --- --- PARTITION_JOIN --- Test partitionwise join between partitioned tables --- --- Enable partitionwise join, which by default is disabled. -SET enable_partitionwise_join to true; --- --- partitioned by a single column --- -CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); -CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); -CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); -CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); -INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; -CREATE INDEX iprt1_p1_a on prt1_p1(a); -CREATE INDEX iprt1_p2_a on prt1_p2(a); -CREATE INDEX iprt1_p3_a on prt1_p3(a); -ANALYZE prt1; -CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); -CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); -CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); -CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); -INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; -CREATE INDEX iprt2_p1_b on prt2_p1(b); -CREATE INDEX iprt2_p2_b on prt2_p2(b); -CREATE INDEX iprt2_p3_b on prt2_p3(b); -ANALYZE prt2; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p1 t2_1 - -> Hash - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p2 t2_2 - -> Hash - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 -(4 rows) - --- left outer join, with whole-row reference; partitionwise join does not apply -EXPLAIN (COSTS OFF) -SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Hash Right Join - Hash Cond: (t2.b = t1.a) - -> Append - -> Seq Scan on prt2_p1 t2_1 - -> Seq Scan on prt2_p2 t2_2 - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) -(16 rows) - -SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - t1 | t2 ---------------+-------------- - (0,0,0000) | (0,0,0000) - (50,0,0050) | - (100,0,0100) | - (150,0,0150) | (0,150,0150) - (200,0,0200) | - (250,0,0250) | - (300,0,0300) | (0,300,0300) - (350,0,0350) | - (400,0,0400) | - (450,0,0450) | (0,450,0450) - (500,0,0500) | - (550,0,0550) | -(12 rows) - --- right outer join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ---------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Right Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p1 t1_1 - -> Hash - -> Seq Scan on prt2_p1 t2_1 - Filter: (a = 0) - -> Hash Right Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p2 t1_2 - -> Hash - -> Seq Scan on prt2_p2 t2_2 - Filter: (a = 0) - -> Nested Loop Left Join - -> Seq Scan on prt2_p3 t2_3 - Filter: (a = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 - Index Cond: (a = t2_3.b) -(20 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 - | | 75 | 0075 - | | 225 | 0225 - | | 375 | 0375 - | | 525 | 0525 -(8 rows) - --- full outer join, with placeholder vars -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - QUERY PLAN ----------------------------------------------------------------- - Sort - Sort Key: prt1.a, prt2.b - -> Append - -> Hash Full Join - Hash Cond: (prt1_1.a = prt2_1.b) - Filter: (((50) = prt1_1.a) OR ((75) = prt2_1.b)) - -> Seq Scan on prt1_p1 prt1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p1 prt2_1 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: (prt1_2.a = prt2_2.b) - Filter: (((50) = prt1_2.a) OR ((75) = prt2_2.b)) - -> Seq Scan on prt1_p2 prt1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p2 prt2_2 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: (prt1_3.a = prt2_3.b) - Filter: (((50) = prt1_3.a) OR ((75) = prt2_3.b)) - -> Seq Scan on prt1_p3 prt1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p3 prt2_3 - Filter: (a = 0) -(27 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - a | c | b | c -----+------+----+------ - 50 | 0050 | | - | | 75 | 0075 -(2 rows) - --- Join with pruned partitions from joining relations -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------ - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p2 t2 - Filter: (b > 250) - -> Hash - -> Seq Scan on prt1_p2 t1 - Filter: ((a < 450) AND (b = 0)) -(9 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 300 | 0300 | 300 | 0300 -(1 row) - --- Currently we can't do partitioned join if nullable-side partitions are pruned -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: prt1.a, prt2.b - -> Hash Right Join - Hash Cond: (prt2.b = prt1.a) - -> Append - -> Seq Scan on prt2_p2 prt2_1 - Filter: (b > 250) - -> Seq Scan on prt2_p3 prt2_2 - Filter: (b > 250) - -> Hash - -> Append - -> Seq Scan on prt1_p1 prt1_1 - Filter: ((a < 450) AND (b = 0)) - -> Seq Scan on prt1_p2 prt1_2 - Filter: ((a < 450) AND (b = 0)) -(15 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | -(9 rows) - --- Currently we can't do partitioned join if nullable-side partitions are pruned -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ----------------------------------------------------- - Sort - Sort Key: prt1.a, prt2.b - -> Hash Full Join - Hash Cond: (prt1.a = prt2.b) - Filter: ((prt1.b = 0) OR (prt2.a = 0)) - -> Append - -> Seq Scan on prt1_p1 prt1_1 - Filter: (a < 450) - -> Seq Scan on prt1_p2 prt1_2 - Filter: (a < 450) - -> Hash - -> Append - -> Seq Scan on prt2_p2 prt2_1 - Filter: (b > 250) - -> Seq Scan on prt2_p3 prt2_2 - Filter: (b > 250) -(16 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | - | | 375 | 0375 - | | 450 | 0450 - | | 525 | 0525 -(12 rows) - --- Semi-join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Semi Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p1 t2_1 - Filter: (a = 0) - -> Hash Semi Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p2 t2_2 - Filter: (a = 0) - -> Nested Loop Semi Join - Join Filter: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) - -> Materialize - -> Seq Scan on prt2_p3 t2_3 - Filter: (a = 0) -(24 rows) - -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) - --- Anti-join with aggregates -EXPLAIN (COSTS OFF) -SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); - QUERY PLAN --------------------------------------------------- - Aggregate - -> Append - -> Hash Anti Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p1 t1_1 - -> Hash - -> Seq Scan on prt2_p1 t2_1 - -> Hash Anti Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p2 t1_2 - -> Hash - -> Seq Scan on prt2_p2 t2_2 - -> Hash Anti Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_p3 t1_3 - -> Hash - -> Seq Scan on prt2_p3 t2_3 -(17 rows) - -SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); - sum | avg | sum | avg --------+----------------------+------+--------------------- - 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000 -(1 row) - --- lateral reference -EXPLAIN (COSTS OFF) -SELECT * FROM prt1 t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss - ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Left Join - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Nested Loop - -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1 - Index Cond: (a = t1_1.a) - -> Index Scan using iprt2_p1_b on prt2_p1 t3_1 - Index Cond: (b = t2_1.a) - -> Nested Loop Left Join - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Nested Loop - -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 - Index Cond: (a = t1_2.a) - -> Index Scan using iprt2_p2_b on prt2_p2 t3_2 - Index Cond: (b = t2_2.a) - -> Nested Loop Left Join - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) - -> Nested Loop - -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 - Index Cond: (a = t1_3.a) - -> Index Scan using iprt2_p3_b on prt2_p3 t3_3 - Index Cond: (b = t2_3.a) -(27 rows) - -SELECT * FROM prt1 t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss - ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; - a | b | c | t2a | t3a | least ------+---+------+-----+-----+------- - 0 | 0 | 0000 | 0 | 0 | 0 - 50 | 0 | 0050 | | | - 100 | 0 | 0100 | | | - 150 | 0 | 0150 | 150 | 0 | 150 - 200 | 0 | 0200 | | | - 250 | 0 | 0250 | | | - 300 | 0 | 0300 | 300 | 0 | 300 - 350 | 0 | 0350 | | | - 400 | 0 | 0400 | | | - 450 | 0 | 0450 | 450 | 0 | 450 - 500 | 0 | 0500 | | | - 550 | 0 | 0550 | | | -(12 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss - ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Left Join - Hash Cond: ((t1.c)::text = (t2.c)::text) - Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) - -> Append - -> Seq Scan on prt1_p1 t1_1 - -> Seq Scan on prt1_p2 t1_2 - -> Seq Scan on prt1_p3 t1_3 - -> Hash - -> Append - -> Hash Join - Hash Cond: (t2_1.a = t3_1.b) - -> Seq Scan on prt1_p1 t2_1 - -> Hash - -> Seq Scan on prt2_p1 t3_1 - -> Hash Join - Hash Cond: (t2_2.a = t3_2.b) - -> Seq Scan on prt1_p2 t2_2 - -> Hash - -> Seq Scan on prt2_p2 t3_2 - -> Hash Join - Hash Cond: (t2_3.a = t3_3.b) - -> Seq Scan on prt1_p3 t2_3 - -> Hash - -> Seq Scan on prt2_p3 t3_3 -(26 rows) - -SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss - ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; - a | t2a | t2c ------+-----+------ - 0 | 0 | 0000 - 50 | | - 100 | | - 150 | 150 | 0150 - 200 | | - 250 | | - 300 | 300 | 0300 - 350 | | - 400 | | - 450 | 450 | 0450 - 500 | | - 550 | | -(12 rows) - --- bug with inadequate sort key representation -SET enable_partitionwise_aggregate TO true; -SET enable_hashjoin TO false; -EXPLAIN (COSTS OFF) -SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) - WHERE a BETWEEN 490 AND 510 - GROUP BY 1, 2 ORDER BY 1, 2; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------ - Group - Group Key: (COALESCE(prt1.a, p2.a)), (COALESCE(prt1.b, p2.b)) - -> Merge Append - Sort Key: (COALESCE(prt1.a, p2.a)), (COALESCE(prt1.b, p2.b)) - -> Group - Group Key: (COALESCE(prt1.a, p2.a)), (COALESCE(prt1.b, p2.b)) - -> Sort - Sort Key: (COALESCE(prt1.a, p2.a)), (COALESCE(prt1.b, p2.b)) - -> Merge Full Join - Merge Cond: ((prt1.a = p2.a) AND (prt1.b = p2.b)) - Filter: ((COALESCE(prt1.a, p2.a) >= 490) AND (COALESCE(prt1.a, p2.a) <= 510)) - -> Sort - Sort Key: prt1.a, prt1.b - -> Seq Scan on prt1_p1 prt1 - -> Sort - Sort Key: p2.a, p2.b - -> Seq Scan on prt2_p1 p2 - -> Group - Group Key: (COALESCE(prt1_1.a, p2_1.a)), (COALESCE(prt1_1.b, p2_1.b)) - -> Sort - Sort Key: (COALESCE(prt1_1.a, p2_1.a)), (COALESCE(prt1_1.b, p2_1.b)) - -> Merge Full Join - Merge Cond: ((prt1_1.a = p2_1.a) AND (prt1_1.b = p2_1.b)) - Filter: ((COALESCE(prt1_1.a, p2_1.a) >= 490) AND (COALESCE(prt1_1.a, p2_1.a) <= 510)) - -> Sort - Sort Key: prt1_1.a, prt1_1.b - -> Seq Scan on prt1_p2 prt1_1 - -> Sort - Sort Key: p2_1.a, p2_1.b - -> Seq Scan on prt2_p2 p2_1 - -> Group - Group Key: (COALESCE(prt1_2.a, p2_2.a)), (COALESCE(prt1_2.b, p2_2.b)) - -> Sort - Sort Key: (COALESCE(prt1_2.a, p2_2.a)), (COALESCE(prt1_2.b, p2_2.b)) - -> Merge Full Join - Merge Cond: ((prt1_2.a = p2_2.a) AND (prt1_2.b = p2_2.b)) - Filter: ((COALESCE(prt1_2.a, p2_2.a) >= 490) AND (COALESCE(prt1_2.a, p2_2.a) <= 510)) - -> Sort - Sort Key: prt1_2.a, prt1_2.b - -> Seq Scan on prt1_p3 prt1_2 - -> Sort - Sort Key: p2_2.a, p2_2.b - -> Seq Scan on prt2_p3 p2_2 -(43 rows) - -SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) - WHERE a BETWEEN 490 AND 510 - GROUP BY 1, 2 ORDER BY 1, 2; - a | b ------+---- - 490 | 15 - 492 | 17 - 494 | 19 - 495 | 20 - 496 | 21 - 498 | 23 - 500 | 0 - 501 | 1 - 502 | 2 - 504 | 4 - 506 | 6 - 507 | 7 - 508 | 8 - 510 | 10 -(14 rows) - -RESET enable_partitionwise_aggregate; -RESET enable_hashjoin; --- --- partitioned by expression --- -CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); -CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); -CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); -CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); -INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; -CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); -CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); -CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); -ANALYZE prt1_e; -CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); -CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); -CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); -CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); -INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; -ANALYZE prt2_e; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Join - Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2)) - -> Seq Scan on prt2_e_p1 t2_1 - -> Hash - -> Seq Scan on prt1_e_p1 t1_1 - Filter: (c = 0) - -> Hash Join - Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2)) - -> Seq Scan on prt2_e_p2 t2_2 - -> Hash - -> Seq Scan on prt1_e_p2 t1_2 - Filter: (c = 0) - -> Hash Join - Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2)) - -> Seq Scan on prt2_e_p3 t2_3 - -> Hash - -> Seq Scan on prt1_e_p3 t1_3 - Filter: (c = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 150 | 0 | 150 | 0 - 300 | 0 | 300 | 0 - 450 | 0 | 450 | 0 -(4 rows) - --- --- N-way join --- -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop - Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p1 t2_1 - -> Hash - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3_1 - Index Cond: (((a + b) / 2) = t2_1.b) - -> Nested Loop - Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p2 t2_2 - -> Hash - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2 - Index Cond: (((a + b) / 2) = t2_2.b) - -> Nested Loop - Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2)) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3 - Index Cond: (((a + b) / 2) = t2_3.b) -(33 rows) - -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 -(4 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b, ((t3.a + t3.b)) - -> Append - -> Hash Right Join - Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a) - -> Seq Scan on prt1_e_p1 t3_1 - -> Hash - -> Hash Right Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p1 t2_1 - -> Hash - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a) - -> Seq Scan on prt1_e_p2 t3_2 - -> Hash - -> Hash Right Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p2 t2_2 - -> Hash - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (((t3_3.a + t3_3.b) / 2) = t1_3.a) - -> Seq Scan on prt1_e_p3 t3_3 - -> Hash - -> Hash Right Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) -(33 rows) - -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN -------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b, ((t3.a + t3.b)) - -> Append - -> Nested Loop Left Join - -> Hash Right Join - Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_p1 t1_1 - -> Hash - -> Seq Scan on prt1_e_p1 t3_1 - Filter: (c = 0) - -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 - Index Cond: (b = t1_1.a) - -> Nested Loop Left Join - -> Hash Right Join - Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_p2 t1_2 - -> Hash - -> Seq Scan on prt1_e_p2 t3_2 - Filter: (c = 0) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 - Index Cond: (b = t1_2.a) - -> Nested Loop Left Join - -> Hash Right Join - Hash Cond: (t1_3.a = ((t3_3.a + t3_3.b) / 2)) - -> Seq Scan on prt1_p3 t1_3 - -> Hash - -> Seq Scan on prt1_e_p3 t3_3 - Filter: (c = 0) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 - Index Cond: (b = t1_3.a) -(30 rows) - -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) - --- --- 3-way full join --- -EXPLAIN (COSTS OFF) -SELECT COUNT(*) FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) FULL JOIN prt2 p3(b,a,c) USING (a, b) - WHERE a BETWEEN 490 AND 510; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ - Aggregate - -> Append - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_1.a, p2_1.a) = p3_1.a) AND (COALESCE(prt1_1.b, p2_1.b) = p3_1.b)) - Filter: ((COALESCE(COALESCE(prt1_1.a, p2_1.a), p3_1.a) >= 490) AND (COALESCE(COALESCE(prt1_1.a, p2_1.a), p3_1.a) <= 510)) - -> Hash Full Join - Hash Cond: ((prt1_1.a = p2_1.a) AND (prt1_1.b = p2_1.b)) - -> Seq Scan on prt1_p1 prt1_1 - -> Hash - -> Seq Scan on prt2_p1 p2_1 - -> Hash - -> Seq Scan on prt2_p1 p3_1 - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_2.a, p2_2.a) = p3_2.a) AND (COALESCE(prt1_2.b, p2_2.b) = p3_2.b)) - Filter: ((COALESCE(COALESCE(prt1_2.a, p2_2.a), p3_2.a) >= 490) AND (COALESCE(COALESCE(prt1_2.a, p2_2.a), p3_2.a) <= 510)) - -> Hash Full Join - Hash Cond: ((prt1_2.a = p2_2.a) AND (prt1_2.b = p2_2.b)) - -> Seq Scan on prt1_p2 prt1_2 - -> Hash - -> Seq Scan on prt2_p2 p2_2 - -> Hash - -> Seq Scan on prt2_p2 p3_2 - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_3.a, p2_3.a) = p3_3.a) AND (COALESCE(prt1_3.b, p2_3.b) = p3_3.b)) - Filter: ((COALESCE(COALESCE(prt1_3.a, p2_3.a), p3_3.a) >= 490) AND (COALESCE(COALESCE(prt1_3.a, p2_3.a), p3_3.a) <= 510)) - -> Hash Full Join - Hash Cond: ((prt1_3.a = p2_3.a) AND (prt1_3.b = p2_3.b)) - -> Seq Scan on prt1_p3 prt1_3 - -> Hash - -> Seq Scan on prt2_p3 p2_3 - -> Hash - -> Seq Scan on prt2_p3 p3_3 -(32 rows) - -SELECT COUNT(*) FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) FULL JOIN prt2 p3(b,a,c) USING (a, b) - WHERE a BETWEEN 490 AND 510; - count -------- - 14 -(1 row) - --- --- 4-way full join --- -EXPLAIN (COSTS OFF) -SELECT COUNT(*) FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) FULL JOIN prt2 p3(b,a,c) USING (a, b) FULL JOIN prt1 p4 (a,b,c) USING (a, b) - WHERE a BETWEEN 490 AND 510; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Aggregate - -> Append - -> Hash Full Join - Hash Cond: ((COALESCE(COALESCE(prt1_1.a, p2_1.a), p3_1.a) = p4_1.a) AND (COALESCE(COALESCE(prt1_1.b, p2_1.b), p3_1.b) = p4_1.b)) - Filter: ((COALESCE(COALESCE(COALESCE(prt1_1.a, p2_1.a), p3_1.a), p4_1.a) >= 490) AND (COALESCE(COALESCE(COALESCE(prt1_1.a, p2_1.a), p3_1.a), p4_1.a) <= 510)) - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_1.a, p2_1.a) = p3_1.a) AND (COALESCE(prt1_1.b, p2_1.b) = p3_1.b)) - -> Hash Full Join - Hash Cond: ((prt1_1.a = p2_1.a) AND (prt1_1.b = p2_1.b)) - -> Seq Scan on prt1_p1 prt1_1 - -> Hash - -> Seq Scan on prt2_p1 p2_1 - -> Hash - -> Seq Scan on prt2_p1 p3_1 - -> Hash - -> Seq Scan on prt1_p1 p4_1 - -> Hash Full Join - Hash Cond: ((COALESCE(COALESCE(prt1_2.a, p2_2.a), p3_2.a) = p4_2.a) AND (COALESCE(COALESCE(prt1_2.b, p2_2.b), p3_2.b) = p4_2.b)) - Filter: ((COALESCE(COALESCE(COALESCE(prt1_2.a, p2_2.a), p3_2.a), p4_2.a) >= 490) AND (COALESCE(COALESCE(COALESCE(prt1_2.a, p2_2.a), p3_2.a), p4_2.a) <= 510)) - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_2.a, p2_2.a) = p3_2.a) AND (COALESCE(prt1_2.b, p2_2.b) = p3_2.b)) - -> Hash Full Join - Hash Cond: ((prt1_2.a = p2_2.a) AND (prt1_2.b = p2_2.b)) - -> Seq Scan on prt1_p2 prt1_2 - -> Hash - -> Seq Scan on prt2_p2 p2_2 - -> Hash - -> Seq Scan on prt2_p2 p3_2 - -> Hash - -> Seq Scan on prt1_p2 p4_2 - -> Hash Full Join - Hash Cond: ((COALESCE(COALESCE(prt1_3.a, p2_3.a), p3_3.a) = p4_3.a) AND (COALESCE(COALESCE(prt1_3.b, p2_3.b), p3_3.b) = p4_3.b)) - Filter: ((COALESCE(COALESCE(COALESCE(prt1_3.a, p2_3.a), p3_3.a), p4_3.a) >= 490) AND (COALESCE(COALESCE(COALESCE(prt1_3.a, p2_3.a), p3_3.a), p4_3.a) <= 510)) - -> Hash Full Join - Hash Cond: ((COALESCE(prt1_3.a, p2_3.a) = p3_3.a) AND (COALESCE(prt1_3.b, p2_3.b) = p3_3.b)) - -> Hash Full Join - Hash Cond: ((prt1_3.a = p2_3.a) AND (prt1_3.b = p2_3.b)) - -> Seq Scan on prt1_p3 prt1_3 - -> Hash - -> Seq Scan on prt2_p3 p2_3 - -> Hash - -> Seq Scan on prt2_p3 p3_3 - -> Hash - -> Seq Scan on prt1_p3 p4_3 -(44 rows) - -SELECT COUNT(*) FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) FULL JOIN prt2 p3(b,a,c) USING (a, b) FULL JOIN prt1 p4 (a,b,c) USING (a, b) - WHERE a BETWEEN 490 AND 510; - count -------- - 14 -(1 row) - --- Cases with non-nullable expressions in subquery results; --- make sure these go to null as expected -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: prt1.a, prt2.b, ((prt1_e.a + prt1_e.b)) - -> Append - -> Hash Full Join - Hash Cond: (prt1_1.a = ((prt1_e_1.a + prt1_e_1.b) / 2)) - Filter: ((prt1_1.a = (50)) OR (prt2_1.b = (75)) OR (((prt1_e_1.a + prt1_e_1.b) / 2) = (50))) - -> Hash Full Join - Hash Cond: (prt1_1.a = prt2_1.b) - -> Seq Scan on prt1_p1 prt1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p1 prt2_1 - Filter: (a = 0) - -> Hash - -> Seq Scan on prt1_e_p1 prt1_e_1 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: (prt1_2.a = ((prt1_e_2.a + prt1_e_2.b) / 2)) - Filter: ((prt1_2.a = (50)) OR (prt2_2.b = (75)) OR (((prt1_e_2.a + prt1_e_2.b) / 2) = (50))) - -> Hash Full Join - Hash Cond: (prt1_2.a = prt2_2.b) - -> Seq Scan on prt1_p2 prt1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p2 prt2_2 - Filter: (a = 0) - -> Hash - -> Seq Scan on prt1_e_p2 prt1_e_2 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: (prt1_3.a = ((prt1_e_3.a + prt1_e_3.b) / 2)) - Filter: ((prt1_3.a = (50)) OR (prt2_3.b = (75)) OR (((prt1_e_3.a + prt1_e_3.b) / 2) = (50))) - -> Hash Full Join - Hash Cond: (prt1_3.a = prt2_3.b) - -> Seq Scan on prt1_p3 prt1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_p3 prt2_3 - Filter: (a = 0) - -> Hash - -> Seq Scan on prt1_e_p3 prt1_e_3 - Filter: (c = 0) -(42 rows) - -SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; - a | phv | b | phv | ?column? | phv -----+-----+----+-----+----------+----- - 50 | 50 | | | 100 | 50 - | | 75 | 75 | | -(2 rows) - --- Semi-join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop - Join Filter: (t1_2.a = t1_5.b) - -> HashAggregate - Group Key: t1_5.b - -> Hash Join - Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_5.b) - -> Seq Scan on prt1_e_p1 t2_1 - -> Hash - -> Seq Scan on prt2_p1 t1_5 - Filter: (a = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1_2 - Index Cond: (a = ((t2_1.a + t2_1.b) / 2)) - Filter: (b = 0) - -> Nested Loop - Join Filter: (t1_3.a = t1_6.b) - -> HashAggregate - Group Key: t1_6.b - -> Hash Join - Hash Cond: (((t2_2.a + t2_2.b) / 2) = t1_6.b) - -> Seq Scan on prt1_e_p2 t2_2 - -> Hash - -> Seq Scan on prt2_p2 t1_6 - Filter: (a = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_3 - Index Cond: (a = ((t2_2.a + t2_2.b) / 2)) - Filter: (b = 0) - -> Nested Loop - Join Filter: (t1_4.a = t1_7.b) - -> HashAggregate - Group Key: t1_7.b - -> Nested Loop - -> Seq Scan on prt2_p3 t1_7 - Filter: (a = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3 - Index Cond: (((a + b) / 2) = t1_7.b) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_4 - Index Cond: (a = ((t2_3.a + t2_3.b) / 2)) - Filter: (b = 0) -(41 rows) - -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ---------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop - -> HashAggregate - Group Key: t1_6.b - -> Hash Semi Join - Hash Cond: (t1_6.b = ((t1_9.a + t1_9.b) / 2)) - -> Seq Scan on prt2_p1 t1_6 - -> Hash - -> Seq Scan on prt1_e_p1 t1_9 - Filter: (c = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1_3 - Index Cond: (a = t1_6.b) - Filter: (b = 0) - -> Nested Loop - -> HashAggregate - Group Key: t1_7.b - -> Hash Semi Join - Hash Cond: (t1_7.b = ((t1_10.a + t1_10.b) / 2)) - -> Seq Scan on prt2_p2 t1_7 - -> Hash - -> Seq Scan on prt1_e_p2 t1_10 - Filter: (c = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_4 - Index Cond: (a = t1_7.b) - Filter: (b = 0) - -> Nested Loop - -> HashAggregate - Group Key: t1_8.b - -> Hash Semi Join - Hash Cond: (t1_8.b = ((t1_11.a + t1_11.b) / 2)) - -> Seq Scan on prt2_p3 t1_8 - -> Hash - -> Seq Scan on prt1_e_p3 t1_11 - Filter: (c = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_5 - Index Cond: (a = t1_8.b) - Filter: (b = 0) -(39 rows) - -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) - --- test merge joins -SET enable_hashjoin TO off; -SET enable_nestloop TO off; -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------------------- - Merge Append - Sort Key: t1.a - -> Merge Semi Join - Merge Cond: (t1_3.a = t1_6.b) - -> Sort - Sort Key: t1_3.a - -> Seq Scan on prt1_p1 t1_3 - Filter: (b = 0) - -> Merge Semi Join - Merge Cond: (t1_6.b = (((t1_9.a + t1_9.b) / 2))) - -> Sort - Sort Key: t1_6.b - -> Seq Scan on prt2_p1 t1_6 - -> Sort - Sort Key: (((t1_9.a + t1_9.b) / 2)) - -> Seq Scan on prt1_e_p1 t1_9 - Filter: (c = 0) - -> Merge Semi Join - Merge Cond: (t1_4.a = t1_7.b) - -> Sort - Sort Key: t1_4.a - -> Seq Scan on prt1_p2 t1_4 - Filter: (b = 0) - -> Merge Semi Join - Merge Cond: (t1_7.b = (((t1_10.a + t1_10.b) / 2))) - -> Sort - Sort Key: t1_7.b - -> Seq Scan on prt2_p2 t1_7 - -> Sort - Sort Key: (((t1_10.a + t1_10.b) / 2)) - -> Seq Scan on prt1_e_p2 t1_10 - Filter: (c = 0) - -> Merge Semi Join - Merge Cond: (t1_5.a = t1_8.b) - -> Sort - Sort Key: t1_5.a - -> Seq Scan on prt1_p3 t1_5 - Filter: (b = 0) - -> Merge Semi Join - Merge Cond: (t1_8.b = (((t1_11.a + t1_11.b) / 2))) - -> Sort - Sort Key: t1_8.b - -> Seq Scan on prt2_p3 t1_8 - -> Sort - Sort Key: (((t1_11.a + t1_11.b) / 2)) - -> Seq Scan on prt1_e_p3 t1_11 - Filter: (c = 0) -(47 rows) - -SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN ----------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b, ((t3.a + t3.b)) - -> Append - -> Merge Left Join - Merge Cond: (t1_1.a = t2_1.b) - -> Sort - Sort Key: t1_1.a - -> Merge Left Join - Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a) - -> Sort - Sort Key: (((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_e_p1 t3_1 - Filter: (c = 0) - -> Sort - Sort Key: t1_1.a - -> Seq Scan on prt1_p1 t1_1 - -> Sort - Sort Key: t2_1.b - -> Seq Scan on prt2_p1 t2_1 - -> Merge Left Join - Merge Cond: (t1_2.a = t2_2.b) - -> Sort - Sort Key: t1_2.a - -> Merge Left Join - Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a) - -> Sort - Sort Key: (((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_e_p2 t3_2 - Filter: (c = 0) - -> Sort - Sort Key: t1_2.a - -> Seq Scan on prt1_p2 t1_2 - -> Sort - Sort Key: t2_2.b - -> Seq Scan on prt2_p2 t2_2 - -> Merge Left Join - Merge Cond: (t1_3.a = t2_3.b) - -> Sort - Sort Key: t1_3.a - -> Merge Left Join - Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a) - -> Sort - Sort Key: (((t3_3.a + t3_3.b) / 2)) - -> Seq Scan on prt1_e_p3 t3_3 - Filter: (c = 0) - -> Sort - Sort Key: t1_3.a - -> Seq Scan on prt1_p3 t1_3 - -> Sort - Sort Key: t2_3.b - -> Seq Scan on prt2_p3 t2_3 -(51 rows) - -SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) - --- MergeAppend on nullable column --- This should generate a partitionwise join, but currently fails to -EXPLAIN (COSTS OFF) -SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: prt1.a, prt2.b - -> Merge Left Join - Merge Cond: (prt1.a = prt2.b) - -> Sort - Sort Key: prt1.a - -> Append - -> Seq Scan on prt1_p1 prt1_1 - Filter: ((a < 450) AND (b = 0)) - -> Seq Scan on prt1_p2 prt1_2 - Filter: ((a < 450) AND (b = 0)) - -> Sort - Sort Key: prt2.b - -> Append - -> Seq Scan on prt2_p2 prt2_1 - Filter: (b > 250) - -> Seq Scan on prt2_p3 prt2_2 - Filter: (b > 250) -(18 rows) - -SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | b ------+----- - 0 | - 50 | - 100 | - 150 | - 200 | - 250 | - 300 | 300 - 350 | - 400 | -(9 rows) - --- merge join when expression with whole-row reference needs to be sorted; --- partitionwise join does not apply -EXPLAIN (COSTS OFF) -SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------------------------------------------ - Merge Join - Merge Cond: ((t1.a = t2.b) AND (((((t1.*)::prt1))::text) = ((((t2.*)::prt2))::text))) - -> Sort - Sort Key: t1.a, ((((t1.*)::prt1))::text) - -> Result - -> Append - -> Seq Scan on prt1_p1 t1_1 - -> Seq Scan on prt1_p2 t1_2 - -> Seq Scan on prt1_p3 t1_3 - -> Sort - Sort Key: t2.b, ((((t2.*)::prt2))::text) - -> Result - -> Append - -> Seq Scan on prt2_p1 t2_1 - -> Seq Scan on prt2_p2 t2_2 - -> Seq Scan on prt2_p3 t2_3 -(16 rows) - -SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; - a | b -----+---- - 0 | 0 - 6 | 6 - 12 | 12 - 18 | 18 - 24 | 24 -(5 rows) - -RESET enable_hashjoin; -RESET enable_nestloop; --- --- partitioned by multiple columns --- -CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); -CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; -ANALYZE prt1_m; -CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); -CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; -ANALYZE prt2_m; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: prt1_m.a, prt2_m.b - -> Append - -> Hash Full Join - Hash Cond: ((prt1_m_1.a = ((prt2_m_1.b + prt2_m_1.a) / 2)) AND (((prt1_m_1.a + prt1_m_1.b) / 2) = prt2_m_1.b)) - -> Seq Scan on prt1_m_p1 prt1_m_1 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p1 prt2_m_1 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_2.a = ((prt2_m_2.b + prt2_m_2.a) / 2)) AND (((prt1_m_2.a + prt1_m_2.b) / 2) = prt2_m_2.b)) - -> Seq Scan on prt1_m_p2 prt1_m_2 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p2 prt2_m_2 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_3.a = ((prt2_m_3.b + prt2_m_3.a) / 2)) AND (((prt1_m_3.a + prt1_m_3.b) / 2) = prt2_m_3.b)) - -> Seq Scan on prt1_m_p3 prt1_m_3 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p3 prt2_m_3 - Filter: (c = 0) -(24 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 50 | 0 | | - 100 | 0 | | - 150 | 0 | 150 | 0 - 200 | 0 | | - 250 | 0 | | - 300 | 0 | 300 | 0 - 350 | 0 | | - 400 | 0 | | - 450 | 0 | 450 | 0 - 500 | 0 | | - 550 | 0 | | - | | 75 | 0 - | | 225 | 0 - | | 375 | 0 - | | 525 | 0 -(16 rows) - --- --- tests for list partitioned tables. --- -CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1; -CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE plt2; --- --- list partitioned by expression --- -CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); -CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1_e; --- test partition matching with N-way join -EXPLAIN (COSTS OFF) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - QUERY PLAN --------------------------------------------------------------------------------- - GroupAggregate - Group Key: t1.c, t2.c, t3.c - -> Sort - Sort Key: t1.c, t3.c - -> Append - -> Hash Join - Hash Cond: (t1_1.c = ltrim(t3_1.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_1.b = t2_1.b) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_p1 t1_1 - -> Hash - -> Seq Scan on plt2_p1 t2_1 - -> Hash - -> Seq Scan on plt1_e_p1 t3_1 - -> Hash Join - Hash Cond: (t1_2.c = ltrim(t3_2.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_p2 t1_2 - -> Hash - -> Seq Scan on plt2_p2 t2_2 - -> Hash - -> Seq Scan on plt1_e_p2 t3_2 - -> Hash Join - Hash Cond: (t1_3.c = ltrim(t3_3.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_3.b = t2_3.b) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_p3 t1_3 - -> Hash - -> Seq Scan on plt2_p3 t2_3 - -> Hash - -> Seq Scan on plt1_e_p3 t3_3 -(32 rows) - -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - avg | avg | avg | c | c | c -----------------------+----------------------+-----------------------+------+------+------- - 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000 - 75.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 - 123.0000000000000000 | 123.0000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 - 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 - 225.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 - 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 - 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006 - 375.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007 - 423.0000000000000000 | 423.0000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008 - 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009 - 525.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010 - 573.0000000000000000 | 573.0000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011 -(12 rows) - --- joins where one of the relations is proven empty -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b, prt1 t3 WHERE t2.b = t3.a; - QUERY PLAN --------------------------------------------------- - Hash Left Join - Hash Cond: (t2.b = a) - -> Append - -> Hash Join - Hash Cond: (t3_1.a = t2_1.b) - -> Seq Scan on prt1_p1 t3_1 - -> Hash - -> Seq Scan on prt2_p1 t2_1 - -> Hash Join - Hash Cond: (t3_2.a = t2_2.b) - -> Seq Scan on prt1_p2 t3_2 - -> Hash - -> Seq Scan on prt2_p2 t2_2 - -> Hash Join - Hash Cond: (t3_3.a = t2_3.b) - -> Seq Scan on prt1_p3 t3_3 - -> Hash - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Result - One-Time Filter: false -(21 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------- - Sort - Sort Key: a, t2.b - -> Hash Left Join - Hash Cond: (t2.b = a) - -> Append - -> Seq Scan on prt2_p1 t2_1 - Filter: (a = 0) - -> Seq Scan on prt2_p2 t2_2 - Filter: (a = 0) - -> Seq Scan on prt2_p3 t2_3 - Filter: (a = 0) - -> Hash - -> Result - One-Time Filter: false -(14 rows) - --- --- tests for hash partitioned tables. --- -CREATE TABLE pht1 (a int, b int, c text) PARTITION BY HASH(c); -CREATE TABLE pht1_p1 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 0); -CREATE TABLE pht1_p2 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 1); -CREATE TABLE pht1_p3 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 2); -INSERT INTO pht1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE pht1; -CREATE TABLE pht2 (a int, b int, c text) PARTITION BY HASH(c); -CREATE TABLE pht2_p1 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 0); -CREATE TABLE pht2_p2 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 1); -CREATE TABLE pht2_p3 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 2); -INSERT INTO pht2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE pht2; --- --- hash partitioned by expression --- -CREATE TABLE pht1_e (a int, b int, c text) PARTITION BY HASH(ltrim(c, 'A')); -CREATE TABLE pht1_e_p1 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 0); -CREATE TABLE pht1_e_p2 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 1); -CREATE TABLE pht1_e_p3 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 2); -INSERT INTO pht1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 299, 2) i; -ANALYZE pht1_e; --- test partition matching with N-way join -EXPLAIN (COSTS OFF) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - QUERY PLAN --------------------------------------------------------------------------------- - GroupAggregate - Group Key: t1.c, t2.c, t3.c - -> Sort - Sort Key: t1.c, t3.c - -> Append - -> Hash Join - Hash Cond: (t1_1.c = ltrim(t3_1.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_1.b = t2_1.b) AND (t1_1.c = t2_1.c)) - -> Seq Scan on pht1_p1 t1_1 - -> Hash - -> Seq Scan on pht2_p1 t2_1 - -> Hash - -> Seq Scan on pht1_e_p1 t3_1 - -> Hash Join - Hash Cond: (t1_2.c = ltrim(t3_2.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c)) - -> Seq Scan on pht1_p2 t1_2 - -> Hash - -> Seq Scan on pht2_p2 t2_2 - -> Hash - -> Seq Scan on pht1_e_p2 t3_2 - -> Hash Join - Hash Cond: (t1_3.c = ltrim(t3_3.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_3.b = t2_3.b) AND (t1_3.c = t2_3.c)) - -> Seq Scan on pht1_p3 t1_3 - -> Hash - -> Seq Scan on pht2_p3 t2_3 - -> Hash - -> Seq Scan on pht1_e_p3 t3_3 -(32 rows) - -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - avg | avg | avg | c | c | c -----------------------+----------------------+----------------------+------+------+------- - 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000 - 75.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 - 123.0000000000000000 | 123.0000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 - 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 - 225.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 - 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 -(6 rows) - --- test default partition behavior for range -ALTER TABLE prt1 DETACH PARTITION prt1_p3; -ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; -ANALYZE prt1; -ALTER TABLE prt2 DETACH PARTITION prt2_p3; -ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; -ANALYZE prt2; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p1 t2_1 - -> Hash - -> Seq Scan on prt1_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p2 t2_2 - -> Hash - -> Seq Scan on prt1_p2 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_p3 t2_3 - -> Hash - -> Seq Scan on prt1_p3 t1_3 - Filter: (b = 0) -(21 rows) - --- test default partition behavior for list -ALTER TABLE plt1 DETACH PARTITION plt1_p3; -ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; -ANALYZE plt1; -ALTER TABLE plt2 DETACH PARTITION plt2_p3; -ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT; -ANALYZE plt2; -EXPLAIN (COSTS OFF) -SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.c - -> HashAggregate - Group Key: t1.c, t2.c - -> Append - -> Hash Join - Hash Cond: (t2_1.c = t1_1.c) - -> Seq Scan on plt2_p1 t2_1 - -> Hash - -> Seq Scan on plt1_p1 t1_1 - Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_2.c = t1_2.c) - -> Seq Scan on plt2_p2 t2_2 - -> Hash - -> Seq Scan on plt1_p2 t1_2 - Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_3.c = t1_3.c) - -> Seq Scan on plt2_p3 t2_3 - -> Hash - -> Seq Scan on plt1_p3 t1_3 - Filter: ((a % 25) = 0) -(23 rows) - --- --- multiple levels of partitioning --- -CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a); -CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES FROM (0) TO (250); -CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); -CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES IN ('0000', '0001'); -CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES IN ('0002', '0003'); -CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (b); -CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES FROM (0) TO (13); -CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES FROM (13) TO (25); -INSERT INTO prt1_l SELECT i, i % 25, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE prt1_l; -CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b); -CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES FROM (0) TO (250); -CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); -CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES IN ('0000', '0001'); -CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES IN ('0002', '0003'); -CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (a); -CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES FROM (0) TO (13); -CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES FROM (13) TO (25); -INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE prt2_l; --- inner join, qual covering only top-level partitions -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN -------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_l_p1 t2_1 - -> Hash - -> Seq Scan on prt1_l_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Append - -> Seq Scan on prt2_l_p2_p1 t2_3 - -> Seq Scan on prt2_l_p2_p2 t2_4 - -> Hash - -> Append - -> Seq Scan on prt1_l_p2_p1 t1_3 - Filter: (b = 0) - -> Seq Scan on prt1_l_p2_p2 t1_4 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_6.b = t1_5.a) - -> Append - -> Seq Scan on prt2_l_p3_p1 t2_6 - -> Seq Scan on prt2_l_p3_p2 t2_7 - -> Hash - -> Seq Scan on prt1_l_p3_p1 t1_5 - Filter: (b = 0) -(28 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0002 | 150 | 0002 - 300 | 0000 | 300 | 0000 - 450 | 0002 | 450 | 0002 -(4 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Right Join - Hash Cond: ((t2_1.b = t1_1.a) AND ((t2_1.c)::text = (t1_1.c)::text)) - -> Seq Scan on prt2_l_p1 t2_1 - -> Hash - -> Seq Scan on prt1_l_p1 t1_1 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: ((t2_2.b = t1_2.a) AND ((t2_2.c)::text = (t1_2.c)::text)) - -> Seq Scan on prt2_l_p2_p1 t2_2 - -> Hash - -> Seq Scan on prt1_l_p2_p1 t1_2 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: ((t2_3.b = t1_3.a) AND ((t2_3.c)::text = (t1_3.c)::text)) - -> Seq Scan on prt2_l_p2_p2 t2_3 - -> Hash - -> Seq Scan on prt1_l_p2_p2 t1_3 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: ((t2_5.b = t1_4.a) AND ((t2_5.c)::text = (t1_4.c)::text)) - -> Append - -> Seq Scan on prt2_l_p3_p1 t2_5 - -> Seq Scan on prt2_l_p3_p2 t2_6 - -> Hash - -> Seq Scan on prt1_l_p3_p1 t1_4 - Filter: (b = 0) -(29 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 50 | 0002 | | - 100 | 0000 | | - 150 | 0002 | 150 | 0002 - 200 | 0000 | | - 250 | 0002 | | - 300 | 0000 | 300 | 0000 - 350 | 0002 | | - 400 | 0000 | | - 450 | 0002 | 450 | 0002 - 500 | 0000 | | - 550 | 0002 | | -(12 rows) - --- right join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Right Join - Hash Cond: ((t1_1.a = t2_1.b) AND ((t1_1.c)::text = (t2_1.c)::text)) - -> Seq Scan on prt1_l_p1 t1_1 - -> Hash - -> Seq Scan on prt2_l_p1 t2_1 - Filter: (a = 0) - -> Hash Right Join - Hash Cond: ((t1_2.a = t2_2.b) AND ((t1_2.c)::text = (t2_2.c)::text)) - -> Seq Scan on prt1_l_p2_p1 t1_2 - -> Hash - -> Seq Scan on prt2_l_p2_p1 t2_2 - Filter: (a = 0) - -> Hash Right Join - Hash Cond: ((t1_3.a = t2_3.b) AND ((t1_3.c)::text = (t2_3.c)::text)) - -> Seq Scan on prt1_l_p2_p2 t1_3 - -> Hash - -> Seq Scan on prt2_l_p2_p2 t2_3 - Filter: (a = 0) - -> Hash Right Join - Hash Cond: ((t1_5.a = t2_4.b) AND ((t1_5.c)::text = (t2_4.c)::text)) - -> Append - -> Seq Scan on prt1_l_p3_p1 t1_5 - -> Seq Scan on prt1_l_p3_p2 t1_6 - -> Hash - -> Seq Scan on prt2_l_p3_p1 t2_4 - Filter: (a = 0) -(29 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0002 | 150 | 0002 - 300 | 0000 | 300 | 0000 - 450 | 0002 | 450 | 0002 - | | 75 | 0003 - | | 225 | 0001 - | | 375 | 0003 - | | 525 | 0001 -(8 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; - QUERY PLAN ----------------------------------------------------------------------------------------------------- - Sort - Sort Key: prt1_l.a, prt2_l.b - -> Append - -> Hash Full Join - Hash Cond: ((prt1_l_1.a = prt2_l_1.b) AND ((prt1_l_1.c)::text = (prt2_l_1.c)::text)) - -> Seq Scan on prt1_l_p1 prt1_l_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_l_p1 prt2_l_1 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: ((prt1_l_2.a = prt2_l_2.b) AND ((prt1_l_2.c)::text = (prt2_l_2.c)::text)) - -> Seq Scan on prt1_l_p2_p1 prt1_l_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_l_p2_p1 prt2_l_2 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: ((prt1_l_3.a = prt2_l_3.b) AND ((prt1_l_3.c)::text = (prt2_l_3.c)::text)) - -> Seq Scan on prt1_l_p2_p2 prt1_l_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_l_p2_p2 prt2_l_3 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: ((prt1_l_4.a = prt2_l_4.b) AND ((prt1_l_4.c)::text = (prt2_l_4.c)::text)) - -> Seq Scan on prt1_l_p3_p1 prt1_l_4 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_l_p3_p1 prt2_l_4 - Filter: (a = 0) -(31 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 50 | 0002 | | - 100 | 0000 | | - 150 | 0002 | 150 | 0002 - 200 | 0000 | | - 250 | 0002 | | - 300 | 0000 | 300 | 0000 - 350 | 0002 | | - 400 | 0000 | | - 450 | 0002 | 450 | 0002 - 500 | 0000 | | - 550 | 0002 | | - | | 75 | 0003 - | | 225 | 0001 - | | 375 | 0003 - | | 525 | 0001 -(16 rows) - --- lateral partitionwise join -EXPLAIN (COSTS OFF) -SELECT * FROM prt1_l t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss - ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Left Join - -> Seq Scan on prt1_l_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: ((t3_1.b = t2_1.a) AND ((t3_1.c)::text = (t2_1.c)::text)) - -> Seq Scan on prt2_l_p1 t3_1 - -> Hash - -> Seq Scan on prt1_l_p1 t2_1 - Filter: ((t1_1.a = a) AND ((t1_1.c)::text = (c)::text)) - -> Nested Loop Left Join - -> Seq Scan on prt1_l_p2_p1 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: ((t3_2.b = t2_2.a) AND ((t3_2.c)::text = (t2_2.c)::text)) - -> Seq Scan on prt2_l_p2_p1 t3_2 - -> Hash - -> Seq Scan on prt1_l_p2_p1 t2_2 - Filter: ((t1_2.a = a) AND ((t1_2.c)::text = (c)::text)) - -> Nested Loop Left Join - -> Seq Scan on prt1_l_p2_p2 t1_3 - Filter: (b = 0) - -> Hash Join - Hash Cond: ((t3_3.b = t2_3.a) AND ((t3_3.c)::text = (t2_3.c)::text)) - -> Seq Scan on prt2_l_p2_p2 t3_3 - -> Hash - -> Seq Scan on prt1_l_p2_p2 t2_3 - Filter: ((t1_3.a = a) AND ((t1_3.c)::text = (c)::text)) - -> Nested Loop Left Join - -> Seq Scan on prt1_l_p3_p1 t1_4 - Filter: (b = 0) - -> Hash Join - Hash Cond: ((t3_5.b = t2_5.a) AND ((t3_5.c)::text = (t2_5.c)::text)) - -> Append - -> Seq Scan on prt2_l_p3_p1 t3_5 - -> Seq Scan on prt2_l_p3_p2 t3_6 - -> Hash - -> Append - -> Seq Scan on prt1_l_p3_p1 t2_5 - Filter: ((t1_4.a = a) AND ((t1_4.c)::text = (c)::text)) - -> Seq Scan on prt1_l_p3_p2 t2_6 - Filter: ((t1_4.a = a) AND ((t1_4.c)::text = (c)::text)) -(44 rows) - -SELECT * FROM prt1_l t1 LEFT JOIN LATERAL - (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss - ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; - a | b | c | t2a | t2c | t2b | t3b | least ------+---+------+-----+------+-----+-----+------- - 0 | 0 | 0000 | 0 | 0000 | 0 | 0 | 0 - 50 | 0 | 0002 | | | | | - 100 | 0 | 0000 | | | | | - 150 | 0 | 0002 | 150 | 0002 | 0 | 150 | 150 - 200 | 0 | 0000 | | | | | - 250 | 0 | 0002 | | | | | - 300 | 0 | 0000 | 300 | 0000 | 0 | 300 | 300 - 350 | 0 | 0002 | | | | | - 400 | 0 | 0000 | | | | | - 450 | 0 | 0002 | 450 | 0002 | 0 | 450 | 450 - 500 | 0 | 0000 | | | | | - 550 | 0 | 0002 | | | | | -(12 rows) - --- join with one side empty -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c; - QUERY PLAN -------------------------------------------------------------------------- - Hash Left Join - Hash Cond: ((t2.b = a) AND (t2.a = b) AND ((t2.c)::text = (c)::text)) - -> Append - -> Seq Scan on prt2_l_p1 t2_1 - -> Seq Scan on prt2_l_p2_p1 t2_2 - -> Seq Scan on prt2_l_p2_p2 t2_3 - -> Seq Scan on prt2_l_p3_p1 t2_4 - -> Seq Scan on prt2_l_p3_p2 t2_5 - -> Hash - -> Result - One-Time Filter: false -(11 rows) - --- Test case to verify proper handling of subqueries in a partitioned delete. --- The weird-looking lateral join is just there to force creation of a --- nestloop parameter within the subquery, which exposes the problem if the --- planner fails to make multiple copies of the subquery as appropriate. -EXPLAIN (COSTS OFF) -DELETE FROM prt1_l -WHERE EXISTS ( - SELECT 1 - FROM int4_tbl, - LATERAL (SELECT int4_tbl.f1 FROM int8_tbl LIMIT 2) ss - WHERE prt1_l.c IS NULL); - QUERY PLAN ----------------------------------------------------------- - Delete on prt1_l - Delete on prt1_l_p1 prt1_l_1 - Delete on prt1_l_p3_p1 prt1_l_2 - Delete on prt1_l_p3_p2 prt1_l_3 - -> Nested Loop Semi Join - -> Append - -> Seq Scan on prt1_l_p1 prt1_l_1 - Filter: (c IS NULL) - -> Seq Scan on prt1_l_p3_p1 prt1_l_2 - Filter: (c IS NULL) - -> Seq Scan on prt1_l_p3_p2 prt1_l_3 - Filter: (c IS NULL) - -> Materialize - -> Nested Loop - -> Seq Scan on int4_tbl - -> Subquery Scan on ss - -> Limit - -> Seq Scan on int8_tbl -(18 rows) - --- --- negative testcases --- -CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c); -CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES FROM ('0000') TO ('0250'); -CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES FROM ('0250') TO ('0500'); -INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i; -ANALYZE prt1_n; -CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007'); -CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011'); -INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE prt2_n; -CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007'); -CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010'); -CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011'); -INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE prt3_n; -CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a); -CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES FROM (0) TO (300); -CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES FROM (300) TO (500); -CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES FROM (500) TO (600); -INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE prt4_n; --- partitionwise join can not be applied if the partition ranges differ -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; - QUERY PLAN ----------------------------------------------- - Hash Join - Hash Cond: (t1.a = t2.a) - -> Append - -> Seq Scan on prt1_p1 t1_1 - -> Seq Scan on prt1_p2 t1_2 - -> Seq Scan on prt1_p3 t1_3 - -> Hash - -> Append - -> Seq Scan on prt4_n_p1 t2_1 - -> Seq Scan on prt4_n_p2 t2_2 - -> Seq Scan on prt4_n_p3 t2_3 -(11 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b; - QUERY PLAN --------------------------------------------------------- - Hash Join - Hash Cond: (t2.a = t1.a) - -> Append - -> Seq Scan on prt4_n_p1 t2_1 - -> Seq Scan on prt4_n_p2 t2_2 - -> Seq Scan on prt4_n_p3 t2_3 - -> Hash - -> Append - -> Hash Join - Hash Cond: (t1_1.a = t3_1.b) - -> Seq Scan on prt1_p1 t1_1 - -> Hash - -> Seq Scan on prt2_p1 t3_1 - -> Hash Join - Hash Cond: (t1_2.a = t3_2.b) - -> Seq Scan on prt1_p2 t1_2 - -> Hash - -> Seq Scan on prt2_p2 t3_2 - -> Hash Join - Hash Cond: (t1_3.a = t3_3.b) - -> Seq Scan on prt1_p3 t1_3 - -> Hash - -> Seq Scan on prt2_p3 t3_3 -(23 rows) - --- partitionwise join can not be applied if there are no equi-join conditions --- between partition keys -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); - QUERY PLAN ---------------------------------------------------------- - Nested Loop Left Join - -> Append - -> Seq Scan on prt1_p1 t1_1 - -> Seq Scan on prt1_p2 t1_2 - -> Seq Scan on prt1_p3 t1_3 - -> Append - -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 - Index Cond: (b > t1.a) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 - Index Cond: (b > t1.a) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 - Index Cond: (b > t1.a) -(12 rows) - --- equi-join with join condition on partial keys does not qualify for --- partitionwise join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2; - QUERY PLAN ----------------------------------------------- - Hash Join - Hash Cond: (((t2.b + t2.a) / 2) = t1.a) - -> Append - -> Seq Scan on prt2_m_p1 t2_1 - -> Seq Scan on prt2_m_p2 t2_2 - -> Seq Scan on prt2_m_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt1_m_p1 t1_1 - -> Seq Scan on prt1_m_p2 t1_2 - -> Seq Scan on prt1_m_p3 t1_3 -(11 rows) - --- equi-join between out-of-order partition key columns does not qualify for --- partitionwise join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b; - QUERY PLAN ----------------------------------------------- - Hash Left Join - Hash Cond: (t1.a = t2.b) - -> Append - -> Seq Scan on prt1_m_p1 t1_1 - -> Seq Scan on prt1_m_p2 t1_2 - -> Seq Scan on prt1_m_p3 t1_3 - -> Hash - -> Append - -> Seq Scan on prt2_m_p1 t2_1 - -> Seq Scan on prt2_m_p2 t2_2 - -> Seq Scan on prt2_m_p3 t2_3 -(11 rows) - --- equi-join between non-key columns does not qualify for partitionwise join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c; - QUERY PLAN ----------------------------------------------- - Hash Left Join - Hash Cond: (t1.c = t2.c) - -> Append - -> Seq Scan on prt1_m_p1 t1_1 - -> Seq Scan on prt1_m_p2 t1_2 - -> Seq Scan on prt1_m_p3 t1_3 - -> Hash - -> Append - -> Seq Scan on prt2_m_p1 t2_1 - -> Seq Scan on prt2_m_p2 t2_2 - -> Seq Scan on prt2_m_p3 t2_3 -(11 rows) - --- partitionwise join can not be applied for a join between list and range --- partitioned tables -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c); - QUERY PLAN ----------------------------------------------- - Hash Right Join - Hash Cond: (t2.c = (t1.c)::text) - -> Append - -> Seq Scan on prt2_n_p1 t2_1 - -> Seq Scan on prt2_n_p2 t2_2 - -> Hash - -> Append - -> Seq Scan on prt1_n_p1 t1_1 - -> Seq Scan on prt1_n_p2 t1_2 -(9 rows) - --- partitionwise join can not be applied between tables with different --- partition lists -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOIN plt1 t3 ON (t1.c = t3.c); - QUERY PLAN ----------------------------------------------------------- - Hash Join - Hash Cond: (t2.c = (t1.c)::text) - -> Append - -> Seq Scan on prt2_n_p1 t2_1 - -> Seq Scan on prt2_n_p2 t2_2 - -> Hash - -> Hash Join - Hash Cond: (t3.c = (t1.c)::text) - -> Append - -> Seq Scan on plt1_p1 t3_1 - -> Seq Scan on plt1_p2 t3_2 - -> Seq Scan on plt1_p3 t3_3 - -> Hash - -> Append - -> Seq Scan on prt1_n_p1 t1_1 - -> Seq Scan on prt1_n_p2 t1_2 -(16 rows) - --- partitionwise join can not be applied for a join between key column and --- non-key column -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); - QUERY PLAN ----------------------------------------------- - Hash Full Join - Hash Cond: ((t2.c)::text = (t1.c)::text) - -> Append - -> Seq Scan on prt1_p1 t2_1 - -> Seq Scan on prt1_p2 t2_2 - -> Seq Scan on prt1_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt1_n_p1 t1_1 - -> Seq Scan on prt1_n_p2 t1_2 -(10 rows) - --- --- Test some other plan types in a partitionwise join (unfortunately, --- we need larger tables to get the planner to choose these plan types) --- -create temp table prtx1 (a integer, b integer, c integer) - partition by range (a); -create temp table prtx1_1 partition of prtx1 for values from (1) to (11); -create temp table prtx1_2 partition of prtx1 for values from (11) to (21); -create temp table prtx1_3 partition of prtx1 for values from (21) to (31); -create temp table prtx2 (a integer, b integer, c integer) - partition by range (a); -create temp table prtx2_1 partition of prtx2 for values from (1) to (11); -create temp table prtx2_2 partition of prtx2 for values from (11) to (21); -create temp table prtx2_3 partition of prtx2 for values from (21) to (31); -insert into prtx1 select 1 + i%30, i, i - from generate_series(1,1000) i; -insert into prtx2 select 1 + i%30, i, i - from generate_series(1,500) i, generate_series(1,10) j; -create index on prtx2 (b); -create index on prtx2 (c); -analyze prtx1; -analyze prtx2; -explain (costs off) -select * from prtx1 -where not exists (select 1 from prtx2 - where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) - and a<20 and c=120; - QUERY PLAN -------------------------------------------------------------- - Append - -> Nested Loop Anti Join - -> Seq Scan on prtx1_1 - Filter: ((a < 20) AND (c = 120)) - -> Bitmap Heap Scan on prtx2_1 - Recheck Cond: ((b = prtx1_1.b) AND (c = 123)) - Filter: (a = prtx1_1.a) - -> BitmapAnd - -> Bitmap Index Scan on prtx2_1_b_idx - Index Cond: (b = prtx1_1.b) - -> Bitmap Index Scan on prtx2_1_c_idx - Index Cond: (c = 123) - -> Nested Loop Anti Join - -> Seq Scan on prtx1_2 - Filter: ((a < 20) AND (c = 120)) - -> Bitmap Heap Scan on prtx2_2 - Recheck Cond: ((b = prtx1_2.b) AND (c = 123)) - Filter: (a = prtx1_2.a) - -> BitmapAnd - -> Bitmap Index Scan on prtx2_2_b_idx - Index Cond: (b = prtx1_2.b) - -> Bitmap Index Scan on prtx2_2_c_idx - Index Cond: (c = 123) -(23 rows) - -select * from prtx1 -where not exists (select 1 from prtx2 - where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) - and a<20 and c=120; - a | b | c ----+-----+----- - 1 | 120 | 120 -(1 row) - -explain (costs off) -select * from prtx1 -where not exists (select 1 from prtx2 - where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) - and a<20 and c=91; - QUERY PLAN ------------------------------------------------------------------ - Append - -> Nested Loop Anti Join - -> Seq Scan on prtx1_1 - Filter: ((a < 20) AND (c = 91)) - -> Bitmap Heap Scan on prtx2_1 - Recheck Cond: ((b = (prtx1_1.b + 1)) OR (c = 99)) - Filter: (a = prtx1_1.a) - -> BitmapOr - -> Bitmap Index Scan on prtx2_1_b_idx - Index Cond: (b = (prtx1_1.b + 1)) - -> Bitmap Index Scan on prtx2_1_c_idx - Index Cond: (c = 99) - -> Nested Loop Anti Join - -> Seq Scan on prtx1_2 - Filter: ((a < 20) AND (c = 91)) - -> Bitmap Heap Scan on prtx2_2 - Recheck Cond: ((b = (prtx1_2.b + 1)) OR (c = 99)) - Filter: (a = prtx1_2.a) - -> BitmapOr - -> Bitmap Index Scan on prtx2_2_b_idx - Index Cond: (b = (prtx1_2.b + 1)) - -> Bitmap Index Scan on prtx2_2_c_idx - Index Cond: (c = 99) -(23 rows) - -select * from prtx1 -where not exists (select 1 from prtx2 - where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) - and a<20 and c=91; - a | b | c ----+----+---- - 2 | 91 | 91 -(1 row) - --- --- Test advanced partition-matching algorithm for partitioned join --- --- Tests for range-partitioned tables -CREATE TABLE prt1_adv (a int, b int, c varchar) PARTITION BY RANGE (a); -CREATE TABLE prt1_adv_p1 PARTITION OF prt1_adv FOR VALUES FROM (100) TO (200); -CREATE TABLE prt1_adv_p2 PARTITION OF prt1_adv FOR VALUES FROM (200) TO (300); -CREATE TABLE prt1_adv_p3 PARTITION OF prt1_adv FOR VALUES FROM (300) TO (400); -CREATE INDEX prt1_adv_a_idx ON prt1_adv (a); -INSERT INTO prt1_adv SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(100, 399) i; -ANALYZE prt1_adv; -CREATE TABLE prt2_adv (a int, b int, c varchar) PARTITION BY RANGE (b); -CREATE TABLE prt2_adv_p1 PARTITION OF prt2_adv FOR VALUES FROM (100) TO (150); -CREATE TABLE prt2_adv_p2 PARTITION OF prt2_adv FOR VALUES FROM (200) TO (300); -CREATE TABLE prt2_adv_p3 PARTITION OF prt2_adv FOR VALUES FROM (350) TO (500); -CREATE INDEX prt2_adv_b_idx ON prt2_adv (b); -INSERT INTO prt2_adv_p1 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(100, 149) i; -INSERT INTO prt2_adv_p2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(200, 299) i; -INSERT INTO prt2_adv_p3 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(350, 499) i; -ANALYZE prt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 - 350 | 0350 | 350 | 0350 - 375 | 0375 | 375 | 0375 -(8 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Semi Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Semi Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Semi Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 -(21 rows) - -SELECT t1.* FROM prt1_adv t1 WHERE EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 100 | 0 | 0100 - 125 | 0 | 0125 - 200 | 0 | 0200 - 225 | 0 | 0225 - 250 | 0 | 0250 - 275 | 0 | 0275 - 350 | 0 | 0350 - 375 | 0 | 0375 -(8 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Right Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 150 | 0150 | | - 175 | 0175 | | - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 - 300 | 0300 | | - 325 | 0325 | | - 350 | 0350 | 350 | 0350 - 375 | 0375 | 375 | 0375 -(12 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Anti Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Anti Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Anti Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 -(21 rows) - -SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 150 | 0 | 0150 - 175 | 0 | 0175 - 300 | 0 | 0300 - 325 | 0 | 0325 -(4 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 175 phv, * FROM prt1_adv WHERE prt1_adv.b = 0) t1 FULL JOIN (SELECT 425 phv, * FROM prt2_adv WHERE prt2_adv.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------------------------------- - Sort - Sort Key: prt1_adv.a, prt2_adv.b - -> Append - -> Hash Full Join - Hash Cond: (prt1_adv_1.a = prt2_adv_1.b) - Filter: (((175) = prt1_adv_1.a) OR ((425) = prt2_adv_1.b)) - -> Seq Scan on prt1_adv_p1 prt1_adv_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p1 prt2_adv_1 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: (prt1_adv_2.a = prt2_adv_2.b) - Filter: (((175) = prt1_adv_2.a) OR ((425) = prt2_adv_2.b)) - -> Seq Scan on prt1_adv_p2 prt1_adv_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p2 prt2_adv_2 - Filter: (a = 0) - -> Hash Full Join - Hash Cond: (prt2_adv_3.b = prt1_adv_3.a) - Filter: (((175) = prt1_adv_3.a) OR ((425) = prt2_adv_3.b)) - -> Seq Scan on prt2_adv_p3 prt2_adv_3 - Filter: (a = 0) - -> Hash - -> Seq Scan on prt1_adv_p3 prt1_adv_3 - Filter: (b = 0) -(27 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 175 phv, * FROM prt1_adv WHERE prt1_adv.b = 0) t1 FULL JOIN (SELECT 425 phv, * FROM prt2_adv WHERE prt2_adv.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 175 | 0175 | | - | | 425 | 0425 -(2 rows) - --- Test cases where one side has an extra partition -CREATE TABLE prt2_adv_extra PARTITION OF prt2_adv FOR VALUES FROM (500) TO (MAXVALUE); -INSERT INTO prt2_adv SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(500, 599) i; -ANALYZE prt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 - 350 | 0350 | 350 | 0350 - 375 | 0375 | 375 | 0375 -(8 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Semi Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Semi Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Semi Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 -(21 rows) - -SELECT t1.* FROM prt1_adv t1 WHERE EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 100 | 0 | 0100 - 125 | 0 | 0125 - 200 | 0 | 0200 - 225 | 0 | 0225 - 250 | 0 | 0250 - 275 | 0 | 0275 - 350 | 0 | 0350 - 375 | 0 | 0375 -(8 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Append - -> Hash Right Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (t2_3.b = t1_3.a) - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(21 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 150 | 0150 | | - 175 | 0175 | | - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 - 300 | 0300 | | - 325 | 0325 | | - 350 | 0350 | 350 | 0350 - 375 | 0375 | 375 | 0375 -(12 rows) - --- left join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.b, t1.c, t2.a, t2.c FROM prt2_adv t1 LEFT JOIN prt1_adv t2 ON (t1.b = t2.a) WHERE t1.a = 0 ORDER BY t1.b, t2.a; - QUERY PLAN ---------------------------------------------------------- - Sort - Sort Key: t1.b, t2.a - -> Hash Right Join - Hash Cond: (t2.a = t1.b) - -> Append - -> Seq Scan on prt1_adv_p1 t2_1 - -> Seq Scan on prt1_adv_p2 t2_2 - -> Seq Scan on prt1_adv_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt2_adv_p1 t1_1 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p2 t1_2 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3 t1_3 - Filter: (a = 0) - -> Seq Scan on prt2_adv_extra t1_4 - Filter: (a = 0) -(18 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Anti Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Anti Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Anti Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 -(21 rows) - -SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 150 | 0 | 0150 - 175 | 0 | 0175 - 300 | 0 | 0300 - 325 | 0 | 0325 -(4 rows) - --- anti join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt2_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt1_adv t2 WHERE t1.b = t2.a) AND t1.a = 0 ORDER BY t1.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.b - -> Hash Anti Join - Hash Cond: (t1.b = t2.a) - -> Append - -> Seq Scan on prt2_adv_p1 t1_1 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p2 t1_2 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3 t1_3 - Filter: (a = 0) - -> Seq Scan on prt2_adv_extra t1_4 - Filter: (a = 0) - -> Hash - -> Append - -> Seq Scan on prt1_adv_p1 t2_1 - -> Seq Scan on prt1_adv_p2 t2_2 - -> Seq Scan on prt1_adv_p3 t2_3 -(18 rows) - --- full join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 175 phv, * FROM prt1_adv WHERE prt1_adv.b = 0) t1 FULL JOIN (SELECT 425 phv, * FROM prt2_adv WHERE prt2_adv.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - QUERY PLAN ----------------------------------------------------------------- - Sort - Sort Key: prt1_adv.a, prt2_adv.b - -> Hash Full Join - Hash Cond: (prt2_adv.b = prt1_adv.a) - Filter: (((175) = prt1_adv.a) OR ((425) = prt2_adv.b)) - -> Append - -> Seq Scan on prt2_adv_p1 prt2_adv_1 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p2 prt2_adv_2 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3 prt2_adv_3 - Filter: (a = 0) - -> Seq Scan on prt2_adv_extra prt2_adv_4 - Filter: (a = 0) - -> Hash - -> Append - -> Seq Scan on prt1_adv_p1 prt1_adv_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 prt1_adv_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 prt1_adv_3 - Filter: (b = 0) -(22 rows) - --- 3-way join where not every pair of relations can do partitioned join -EXPLAIN (COSTS OFF) -SELECT t1.b, t1.c, t2.a, t2.c, t3.a, t3.c FROM prt2_adv t1 LEFT JOIN prt1_adv t2 ON (t1.b = t2.a) INNER JOIN prt1_adv t3 ON (t1.b = t3.a) WHERE t1.a = 0 ORDER BY t1.b, t2.a, t3.a; - QUERY PLAN --------------------------------------------------------------------------------- - Sort - Sort Key: t1.b, t2.a - -> Append - -> Nested Loop Left Join - -> Nested Loop - -> Seq Scan on prt2_adv_p1 t1_1 - Filter: (a = 0) - -> Index Scan using prt1_adv_p1_a_idx on prt1_adv_p1 t3_1 - Index Cond: (a = t1_1.b) - -> Index Scan using prt1_adv_p1_a_idx on prt1_adv_p1 t2_1 - Index Cond: (a = t1_1.b) - -> Hash Right Join - Hash Cond: (t2_2.a = t1_2.b) - -> Seq Scan on prt1_adv_p2 t2_2 - -> Hash - -> Hash Join - Hash Cond: (t3_2.a = t1_2.b) - -> Seq Scan on prt1_adv_p2 t3_2 - -> Hash - -> Seq Scan on prt2_adv_p2 t1_2 - Filter: (a = 0) - -> Hash Right Join - Hash Cond: (t2_3.a = t1_3.b) - -> Seq Scan on prt1_adv_p3 t2_3 - -> Hash - -> Hash Join - Hash Cond: (t3_3.a = t1_3.b) - -> Seq Scan on prt1_adv_p3 t3_3 - -> Hash - -> Seq Scan on prt2_adv_p3 t1_3 - Filter: (a = 0) -(31 rows) - -SELECT t1.b, t1.c, t2.a, t2.c, t3.a, t3.c FROM prt2_adv t1 LEFT JOIN prt1_adv t2 ON (t1.b = t2.a) INNER JOIN prt1_adv t3 ON (t1.b = t3.a) WHERE t1.a = 0 ORDER BY t1.b, t2.a, t3.a; - b | c | a | c | a | c ------+------+-----+------+-----+------ - 100 | 0100 | 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 | 125 | 0125 - 200 | 0200 | 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 | 275 | 0275 - 350 | 0350 | 350 | 0350 | 350 | 0350 - 375 | 0375 | 375 | 0375 | 375 | 0375 -(8 rows) - -DROP TABLE prt2_adv_extra; --- Test cases where a partition on one side matches multiple partitions on --- the other side; we currently can't do partitioned join in such cases -ALTER TABLE prt2_adv DETACH PARTITION prt2_adv_p3; --- Split prt2_adv_p3 into two partitions so that prt1_adv_p3 matches both -CREATE TABLE prt2_adv_p3_1 PARTITION OF prt2_adv FOR VALUES FROM (350) TO (375); -CREATE TABLE prt2_adv_p3_2 PARTITION OF prt2_adv FOR VALUES FROM (375) TO (500); -INSERT INTO prt2_adv SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(350, 499) i; -ANALYZE prt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3_1 t2_3 - -> Seq Scan on prt2_adv_p3_2 t2_4 - -> Hash - -> Append - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(17 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Semi Join - Hash Cond: (t1.a = t2.b) - -> Append - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3_1 t2_3 - -> Seq Scan on prt2_adv_p3_2 t2_4 -(17 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b - -> Hash Right Join - Hash Cond: (t2.b = t1.a) - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3_1 t2_3 - -> Seq Scan on prt2_adv_p3_2 t2_4 - -> Hash - -> Append - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) -(17 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Anti Join - Hash Cond: (t1.a = t2.b) - -> Append - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) - -> Hash - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3_1 t2_3 - -> Seq Scan on prt2_adv_p3_2 t2_4 -(17 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 175 phv, * FROM prt1_adv WHERE prt1_adv.b = 0) t1 FULL JOIN (SELECT 425 phv, * FROM prt2_adv WHERE prt2_adv.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; - QUERY PLAN ----------------------------------------------------------------- - Sort - Sort Key: prt1_adv.a, prt2_adv.b - -> Hash Full Join - Hash Cond: (prt2_adv.b = prt1_adv.a) - Filter: (((175) = prt1_adv.a) OR ((425) = prt2_adv.b)) - -> Append - -> Seq Scan on prt2_adv_p1 prt2_adv_1 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p2 prt2_adv_2 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3_1 prt2_adv_3 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3_2 prt2_adv_4 - Filter: (a = 0) - -> Hash - -> Append - -> Seq Scan on prt1_adv_p1 prt1_adv_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 prt1_adv_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 prt1_adv_3 - Filter: (b = 0) -(22 rows) - -DROP TABLE prt2_adv_p3_1; -DROP TABLE prt2_adv_p3_2; -ANALYZE prt2_adv; --- Test default partitions -ALTER TABLE prt1_adv DETACH PARTITION prt1_adv_p1; --- Change prt1_adv_p1 to the default partition -ALTER TABLE prt1_adv ATTACH PARTITION prt1_adv_p1 DEFAULT; -ALTER TABLE prt1_adv DETACH PARTITION prt1_adv_p3; -ANALYZE prt1_adv; --- We can do partitioned join even if only one of relations has the default --- partition -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_2.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_2 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_1.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_1 - Filter: (b = 0) -(15 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 -(6 rows) - --- Restore prt1_adv_p3 -ALTER TABLE prt1_adv ATTACH PARTITION prt1_adv_p3 FOR VALUES FROM (300) TO (400); -ANALYZE prt1_adv; --- Restore prt2_adv_p3 -ALTER TABLE prt2_adv ATTACH PARTITION prt2_adv_p3 FOR VALUES FROM (350) TO (500); -ANALYZE prt2_adv; --- Partitioned join can't be applied because the default partition of prt1_adv --- matches prt2_adv_p1 and prt2_adv_p3 -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt1_adv_p2 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p1 t1_3 - Filter: (b = 0) -(16 rows) - -ALTER TABLE prt2_adv DETACH PARTITION prt2_adv_p3; --- Change prt2_adv_p3 to the default partition -ALTER TABLE prt2_adv ATTACH PARTITION prt2_adv_p3 DEFAULT; -ANALYZE prt2_adv; --- Partitioned join can't be applied because the default partition of prt1_adv --- matches prt2_adv_p1 and prt2_adv_p3 -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on prt1_adv_p2 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p1 t1_3 - Filter: (b = 0) -(16 rows) - -DROP TABLE prt1_adv_p3; -ANALYZE prt1_adv; -DROP TABLE prt2_adv_p3; -ANALYZE prt2_adv; -CREATE TABLE prt3_adv (a int, b int, c varchar) PARTITION BY RANGE (a); -CREATE TABLE prt3_adv_p1 PARTITION OF prt3_adv FOR VALUES FROM (200) TO (300); -CREATE TABLE prt3_adv_p2 PARTITION OF prt3_adv FOR VALUES FROM (300) TO (400); -CREATE INDEX prt3_adv_a_idx ON prt3_adv (a); -INSERT INTO prt3_adv SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(200, 399) i; -ANALYZE prt3_adv; --- 3-way join to test the default partition of a join relation -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c, t3.a, t3.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) LEFT JOIN prt3_adv t3 ON (t1.a = t3.a) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.b, t3.a - -> Append - -> Hash Right Join - Hash Cond: (t3_1.a = t1_1.a) - -> Seq Scan on prt3_adv_p1 t3_1 - -> Hash - -> Hash Right Join - Hash Cond: (t2_2.b = t1_1.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_1 - Filter: (b = 0) - -> Hash Right Join - Hash Cond: (t3_2.a = t1_2.a) - -> Seq Scan on prt3_adv_p2 t3_2 - -> Hash - -> Hash Right Join - Hash Cond: (t2_1.b = t1_2.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_2 - Filter: (b = 0) -(23 rows) - -SELECT t1.a, t1.c, t2.b, t2.c, t3.a, t3.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = t2.b) LEFT JOIN prt3_adv t3 ON (t1.a = t3.a) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a; - a | c | b | c | a | c ------+------+-----+------+-----+------ - 100 | 0100 | 100 | 0100 | | - 125 | 0125 | 125 | 0125 | | - 150 | 0150 | | | | - 175 | 0175 | | | | - 200 | 0200 | 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 | 275 | 0275 -(8 rows) - -DROP TABLE prt1_adv; -DROP TABLE prt2_adv; -DROP TABLE prt3_adv; --- Test interaction of partitioned join with partition pruning -CREATE TABLE prt1_adv (a int, b int, c varchar) PARTITION BY RANGE (a); -CREATE TABLE prt1_adv_p1 PARTITION OF prt1_adv FOR VALUES FROM (100) TO (200); -CREATE TABLE prt1_adv_p2 PARTITION OF prt1_adv FOR VALUES FROM (200) TO (300); -CREATE TABLE prt1_adv_p3 PARTITION OF prt1_adv FOR VALUES FROM (300) TO (400); -CREATE INDEX prt1_adv_a_idx ON prt1_adv (a); -INSERT INTO prt1_adv SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(100, 399) i; -ANALYZE prt1_adv; -CREATE TABLE prt2_adv (a int, b int, c varchar) PARTITION BY RANGE (b); -CREATE TABLE prt2_adv_p1 PARTITION OF prt2_adv FOR VALUES FROM (100) TO (200); -CREATE TABLE prt2_adv_p2 PARTITION OF prt2_adv FOR VALUES FROM (200) TO (400); -CREATE INDEX prt2_adv_b_idx ON prt2_adv (b); -INSERT INTO prt2_adv SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(100, 399) i; -ANALYZE prt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.a < 300 AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: ((a < 300) AND (b = 0)) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: ((a < 300) AND (b = 0)) -(15 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.a < 300 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 150 | 0150 | 150 | 0150 - 175 | 0175 | 175 | 0175 - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 -(8 rows) - -DROP TABLE prt1_adv_p3; -CREATE TABLE prt1_adv_default PARTITION OF prt1_adv DEFAULT; -ANALYZE prt1_adv; -CREATE TABLE prt2_adv_default PARTITION OF prt2_adv DEFAULT; -ANALYZE prt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.a >= 100 AND t1.a < 300 AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: ((a >= 100) AND (a < 300) AND (b = 0)) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: ((a >= 100) AND (a < 300) AND (b = 0)) -(15 rows) - -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 INNER JOIN prt2_adv t2 ON (t1.a = t2.b) WHERE t1.a >= 100 AND t1.a < 300 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 100 | 0100 | 100 | 0100 - 125 | 0125 | 125 | 0125 - 150 | 0150 | 150 | 0150 - 175 | 0175 | 175 | 0175 - 200 | 0200 | 200 | 0200 - 225 | 0225 | 225 | 0225 - 250 | 0250 | 250 | 0250 - 275 | 0275 | 275 | 0275 -(8 rows) - -DROP TABLE prt1_adv; -DROP TABLE prt2_adv; --- Tests for list-partitioned tables -CREATE TABLE plt1_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt1_adv_p1 PARTITION OF plt1_adv FOR VALUES IN ('0001', '0003'); -CREATE TABLE plt1_adv_p2 PARTITION OF plt1_adv FOR VALUES IN ('0004', '0006'); -CREATE TABLE plt1_adv_p3 PARTITION OF plt1_adv FOR VALUES IN ('0008', '0009'); -INSERT INTO plt1_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (1, 3, 4, 6, 8, 9); -ANALYZE plt1_adv; -CREATE TABLE plt2_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt2_adv_p1 PARTITION OF plt2_adv FOR VALUES IN ('0002', '0003'); -CREATE TABLE plt2_adv_p2 PARTITION OF plt2_adv FOR VALUES IN ('0004', '0006'); -CREATE TABLE plt2_adv_p3 PARTITION OF plt2_adv FOR VALUES IN ('0007', '0009'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 9 | 0009 | 9 | 0009 -(4 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Semi Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Semi Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Semi Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c ----+---+------ - 3 | 3 | 0003 - 4 | 4 | 0004 - 6 | 6 | 0006 - 9 | 9 | 0009 -(4 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 -(6 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Anti Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c ----+---+------ - 1 | 1 | 0001 - 8 | 8 | 0008 -(2 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN ------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a, t2.a - -> Append - -> Hash Full Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - Filter: ((COALESCE(t1_1.b, 0) < 10) AND (COALESCE(t2_1.b, 0) < 10)) - -> Seq Scan on plt1_adv_p1 t1_1 - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash Full Join - Hash Cond: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - Filter: ((COALESCE(t1_2.b, 0) < 10) AND (COALESCE(t2_2.b, 0) < 10)) - -> Seq Scan on plt1_adv_p2 t1_2 - -> Hash - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash Full Join - Hash Cond: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - Filter: ((COALESCE(t1_3.b, 0) < 10) AND (COALESCE(t2_3.b, 0) < 10)) - -> Seq Scan on plt1_adv_p3 t1_3 - -> Hash - -> Seq Scan on plt2_adv_p3 t2_3 -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - a | c | a | c ----+------+---+------ - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 - | | 2 | 0002 - | | 7 | 0007 -(8 rows) - --- Test cases where one side has an extra partition -CREATE TABLE plt2_adv_extra PARTITION OF plt2_adv FOR VALUES IN ('0000'); -INSERT INTO plt2_adv_extra VALUES (0, 0, '0000'); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 9 | 0009 | 9 | 0009 -(4 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Semi Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Semi Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Semi Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c ----+---+------ - 3 | 3 | 0003 - 4 | 4 | 0004 - 6 | 6 | 0006 - 9 | 9 | 0009 -(4 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 -(6 rows) - --- left join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt2_adv t1 LEFT JOIN plt1_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ---------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Right Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt1_adv_p1 t2_1 - -> Seq Scan on plt1_adv_p2 t2_2 - -> Seq Scan on plt1_adv_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on plt2_adv_extra t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t1_4 - Filter: (b < 10) -(18 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Nested Loop Anti Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c ----+---+------ - 1 | 1 | 0001 - 8 | 8 | 0008 -(2 rows) - --- anti join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt2_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt1_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Anti Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) - -> Append - -> Seq Scan on plt2_adv_extra t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t1_4 - Filter: (b < 10) - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t2_1 - -> Seq Scan on plt1_adv_p2 t2_2 - -> Seq Scan on plt1_adv_p3 t2_3 -(18 rows) - --- full join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN -------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.a - -> Hash Full Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - Filter: ((COALESCE(t1.b, 0) < 10) AND (COALESCE(t2.b, 0) < 10)) - -> Append - -> Seq Scan on plt2_adv_extra t2_1 - -> Seq Scan on plt2_adv_p1 t2_2 - -> Seq Scan on plt2_adv_p2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - -> Seq Scan on plt1_adv_p2 t1_2 - -> Seq Scan on plt1_adv_p3 t1_3 -(15 rows) - -DROP TABLE plt2_adv_extra; --- Test cases where a partition on one side matches multiple partitions on --- the other side; we currently can't do partitioned join in such cases -ALTER TABLE plt2_adv DETACH PARTITION plt2_adv_p2; --- Split plt2_adv_p2 into two partitions so that plt1_adv_p2 matches both -CREATE TABLE plt2_adv_p2_1 PARTITION OF plt2_adv FOR VALUES IN ('0004'); -CREATE TABLE plt2_adv_p2_2 PARTITION OF plt2_adv FOR VALUES IN ('0006'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (4, 6); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(17 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Semi Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Hash - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 -(17 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Right Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(17 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Anti Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Hash - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 -(17 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN -------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.a - -> Hash Full Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - Filter: ((COALESCE(t1.b, 0) < 10) AND (COALESCE(t2.b, 0) < 10)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - -> Seq Scan on plt1_adv_p2 t1_2 - -> Seq Scan on plt1_adv_p3 t1_3 -(15 rows) - -DROP TABLE plt2_adv_p2_1; -DROP TABLE plt2_adv_p2_2; --- Restore plt2_adv_p2 -ALTER TABLE plt2_adv ATTACH PARTITION plt2_adv_p2 FOR VALUES IN ('0004', '0006'); --- Test NULL partitions -ALTER TABLE plt1_adv DETACH PARTITION plt1_adv_p1; --- Change plt1_adv_p1 to the NULL partition -CREATE TABLE plt1_adv_p1_null PARTITION OF plt1_adv FOR VALUES IN (NULL, '0001', '0003'); -INSERT INTO plt1_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (1, 3); -INSERT INTO plt1_adv VALUES (-1, -1, NULL); -ANALYZE plt1_adv; -ALTER TABLE plt2_adv DETACH PARTITION plt2_adv_p3; --- Change plt2_adv_p3 to the NULL partition -CREATE TABLE plt2_adv_p3_null PARTITION OF plt2_adv FOR VALUES IN (NULL, '0007', '0009'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (7, 9); -INSERT INTO plt2_adv VALUES (-1, -1, NULL); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1_null t1_1 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3_null t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 9 | 0009 | 9 | 0009 -(4 rows) - --- semi join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Semi Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1_null t1_1 - Filter: (b < 10) - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Semi Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Semi Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3_null t2_3 -(19 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c ----+---+------ - 3 | 3 | 0003 - 4 | 4 | 0004 - 6 | 6 | 0006 - 9 | 9 | 0009 -(4 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1_null t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3_null t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c -----+------+---+------ - -1 | | | - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 -(7 rows) - --- anti join -EXPLAIN (COSTS OFF) -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Anti Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1_null t1_1 - Filter: (b < 10) - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3_null t2_3 -(19 rows) - -SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - a | b | c -----+----+------ - -1 | -1 | - 1 | 1 | 0001 - 8 | 8 | 0008 -(3 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN ------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a, t2.a - -> Append - -> Hash Full Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - Filter: ((COALESCE(t1_1.b, 0) < 10) AND (COALESCE(t2_1.b, 0) < 10)) - -> Seq Scan on plt1_adv_p1_null t1_1 - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash Full Join - Hash Cond: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - Filter: ((COALESCE(t1_2.b, 0) < 10) AND (COALESCE(t2_2.b, 0) < 10)) - -> Seq Scan on plt1_adv_p2 t1_2 - -> Hash - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash Full Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - Filter: ((COALESCE(t1_3.b, 0) < 10) AND (COALESCE(t2_3.b, 0) < 10)) - -> Seq Scan on plt2_adv_p3_null t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - a | c | a | c -----+------+----+------ - -1 | | | - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 - | | -1 | - | | 2 | 0002 - | | 7 | 0007 -(10 rows) - -DROP TABLE plt1_adv_p1_null; --- Restore plt1_adv_p1 -ALTER TABLE plt1_adv ATTACH PARTITION plt1_adv_p1 FOR VALUES IN ('0001', '0003'); --- Add to plt1_adv the extra NULL partition containing only NULL values as the --- key values -CREATE TABLE plt1_adv_extra PARTITION OF plt1_adv FOR VALUES IN (NULL); -INSERT INTO plt1_adv VALUES (-1, -1, NULL); -ANALYZE plt1_adv; -DROP TABLE plt2_adv_p3_null; --- Restore plt2_adv_p3 -ALTER TABLE plt2_adv ATTACH PARTITION plt2_adv_p3 FOR VALUES IN ('0007', '0009'); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 9 | 0009 | 9 | 0009 -(4 rows) - --- left join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ---------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Right Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2 t2_2 - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Seq Scan on plt1_adv_extra t1_4 - Filter: (b < 10) -(18 rows) - --- full join; currently we can't do partitioned join if there are no matched --- partitions on the nullable side -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN -------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t2.a - -> Hash Full Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) - Filter: ((COALESCE(t1.b, 0) < 10) AND (COALESCE(t2.b, 0) < 10)) - -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - -> Seq Scan on plt1_adv_p2 t1_2 - -> Seq Scan on plt1_adv_p3 t1_3 - -> Seq Scan on plt1_adv_extra t1_4 - -> Hash - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2 t2_2 - -> Seq Scan on plt2_adv_p3 t2_3 -(15 rows) - --- Add to plt2_adv the extra NULL partition containing only NULL values as the --- key values -CREATE TABLE plt2_adv_extra PARTITION OF plt2_adv FOR VALUES IN (NULL); -INSERT INTO plt2_adv VALUES (-1, -1, NULL); -ANALYZE plt2_adv; --- inner join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) -(21 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 9 | 0009 | 9 | 0009 -(4 rows) - --- left join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Nested Loop Left Join - Join Filter: ((t1_4.a = t2_4.a) AND (t1_4.c = t2_4.c)) - -> Seq Scan on plt1_adv_extra t1_4 - Filter: (b < 10) - -> Seq Scan on plt2_adv_extra t2_4 -(26 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c -----+------+---+------ - -1 | | | - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 -(7 rows) - --- full join -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - QUERY PLAN ------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a, t2.a - -> Append - -> Hash Full Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - Filter: ((COALESCE(t1_1.b, 0) < 10) AND (COALESCE(t2_1.b, 0) < 10)) - -> Seq Scan on plt1_adv_p1 t1_1 - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash Full Join - Hash Cond: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - Filter: ((COALESCE(t1_2.b, 0) < 10) AND (COALESCE(t2_2.b, 0) < 10)) - -> Seq Scan on plt1_adv_p2 t1_2 - -> Hash - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash Full Join - Hash Cond: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - Filter: ((COALESCE(t1_3.b, 0) < 10) AND (COALESCE(t2_3.b, 0) < 10)) - -> Seq Scan on plt1_adv_p3 t1_3 - -> Hash - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash Full Join - Hash Cond: ((t1_4.a = t2_4.a) AND (t1_4.c = t2_4.c)) - Filter: ((COALESCE(t1_4.b, 0) < 10) AND (COALESCE(t2_4.b, 0) < 10)) - -> Seq Scan on plt1_adv_extra t1_4 - -> Hash - -> Seq Scan on plt2_adv_extra t2_4 -(27 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 FULL JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE coalesce(t1.b, 0) < 10 AND coalesce(t2.b, 0) < 10 ORDER BY t1.a, t2.a; - a | c | a | c -----+------+----+------ - -1 | | | - 1 | 0001 | | - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 - 8 | 0008 | | - 9 | 0009 | 9 | 0009 - | | -1 | - | | 2 | 0002 - | | 7 | 0007 -(10 rows) - --- 3-way join to test the NULL partition of a join relation -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) LEFT JOIN plt1_adv t3 ON (t1.a = t3.a AND t1.c = t3.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t3_1.a = t1_1.a) AND (t3_1.c = t1_1.c)) - -> Seq Scan on plt1_adv_p1 t3_1 - -> Hash - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t3_2.a = t1_2.a) AND (t3_2.c = t1_2.c)) - -> Seq Scan on plt1_adv_p2 t3_2 - -> Hash - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t3_3.a = t1_3.a) AND (t3_3.c = t1_3.c)) - -> Seq Scan on plt1_adv_p3 t3_3 - -> Hash - -> Hash Right Join - Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) - -> Seq Scan on plt2_adv_p3 t2_3 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) - -> Nested Loop Left Join - Join Filter: ((t1_4.a = t3_4.a) AND (t1_4.c = t3_4.c)) - -> Nested Loop Left Join - Join Filter: ((t1_4.a = t2_4.a) AND (t1_4.c = t2_4.c)) - -> Seq Scan on plt1_adv_extra t1_4 - Filter: (b < 10) - -> Seq Scan on plt2_adv_extra t2_4 - -> Seq Scan on plt1_adv_extra t3_4 -(41 rows) - -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) LEFT JOIN plt1_adv t3 ON (t1.a = t3.a AND t1.c = t3.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c | a | c -----+------+---+------+---+------ - -1 | | | | | - 1 | 0001 | | | 1 | 0001 - 3 | 0003 | 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 | 6 | 0006 - 8 | 0008 | | | 8 | 0008 - 9 | 0009 | 9 | 0009 | 9 | 0009 -(7 rows) - -DROP TABLE plt1_adv_extra; -DROP TABLE plt2_adv_extra; --- Test default partitions -ALTER TABLE plt1_adv DETACH PARTITION plt1_adv_p1; --- Change plt1_adv_p1 to the default partition -ALTER TABLE plt1_adv ATTACH PARTITION plt1_adv_p1 DEFAULT; -DROP TABLE plt1_adv_p3; -ANALYZE plt1_adv; -DROP TABLE plt2_adv_p3; -ANALYZE plt2_adv; --- We can do partitioned join even if only one of relations has the default --- partition -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_2.a) AND (t2_1.c = t1_2.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_1.a) AND (t2_2.c = t1_1.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_1 - Filter: (b < 10) -(15 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 -(3 rows) - -ALTER TABLE plt2_adv DETACH PARTITION plt2_adv_p2; --- Change plt2_adv_p2 to contain '0005' in addition to '0004' and '0006' as --- the key values -CREATE TABLE plt2_adv_p2_ext PARTITION OF plt2_adv FOR VALUES IN ('0004', '0005', '0006'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (4, 5, 6); -ANALYZE plt2_adv; --- Partitioned join can't be applied because the default partition of plt1_adv --- matches plt2_adv_p1 and plt2_adv_p2_ext -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_ext t2_2 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p2 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p1 t1_2 - Filter: (b < 10) -(13 rows) - -ALTER TABLE plt2_adv DETACH PARTITION plt2_adv_p2_ext; --- Change plt2_adv_p2_ext to the default partition -ALTER TABLE plt2_adv ATTACH PARTITION plt2_adv_p2_ext DEFAULT; -ANALYZE plt2_adv; --- Partitioned join can't be applied because the default partition of plt1_adv --- matches plt2_adv_p1 and plt2_adv_p2_ext -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_ext t2_2 - -> Hash - -> Append - -> Seq Scan on plt1_adv_p2 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p1 t1_2 - Filter: (b < 10) -(13 rows) - -DROP TABLE plt2_adv_p2_ext; --- Restore plt2_adv_p2 -ALTER TABLE plt2_adv ATTACH PARTITION plt2_adv_p2 FOR VALUES IN ('0004', '0006'); -ANALYZE plt2_adv; -CREATE TABLE plt3_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt3_adv_p1 PARTITION OF plt3_adv FOR VALUES IN ('0004', '0006'); -CREATE TABLE plt3_adv_p2 PARTITION OF plt3_adv FOR VALUES IN ('0007', '0009'); -INSERT INTO plt3_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (4, 6, 7, 9); -ANALYZE plt3_adv; --- 3-way join to test the default partition of a join relation -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) LEFT JOIN plt3_adv t3 ON (t1.a = t3.a AND t1.c = t3.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Right Join - Hash Cond: ((t3_1.a = t1_1.a) AND (t3_1.c = t1_1.c)) - -> Seq Scan on plt3_adv_p1 t3_1 - -> Hash - -> Hash Right Join - Hash Cond: ((t2_2.a = t1_1.a) AND (t2_2.c = t1_1.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_1 - Filter: (b < 10) - -> Hash Right Join - Hash Cond: ((t3_2.a = t1_2.a) AND (t3_2.c = t1_2.c)) - -> Seq Scan on plt3_adv_p2 t3_2 - -> Hash - -> Hash Right Join - Hash Cond: ((t2_1.a = t1_2.a) AND (t2_1.c = t1_2.c)) - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_2 - Filter: (b < 10) -(23 rows) - -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) LEFT JOIN plt3_adv t3 ON (t1.a = t3.a AND t1.c = t3.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c | a | c ----+------+---+------+---+------ - 1 | 0001 | | | | - 3 | 0003 | 3 | 0003 | | - 4 | 0004 | 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 | 6 | 0006 -(4 rows) - --- Test cases where one side has the default partition while the other side --- has the NULL partition -DROP TABLE plt2_adv_p1; --- Add the NULL partition to plt2_adv -CREATE TABLE plt2_adv_p1_null PARTITION OF plt2_adv FOR VALUES IN (NULL, '0001', '0003'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (1, 3); -INSERT INTO plt2_adv VALUES (-1, -1, NULL); -ANALYZE plt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_2.a) AND (t2_1.c = t1_2.c)) - -> Seq Scan on plt2_adv_p1_null t2_1 - -> Hash - -> Seq Scan on plt1_adv_p1 t1_2 - Filter: (b < 10) - -> Hash Join - Hash Cond: ((t2_2.a = t1_1.a) AND (t2_2.c = t1_1.c)) - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1_1 - Filter: (b < 10) -(15 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 1 | 0001 | 1 | 0001 - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 -(4 rows) - -DROP TABLE plt2_adv_p1_null; --- Add the NULL partition that contains only NULL values as the key values -CREATE TABLE plt2_adv_p1_null PARTITION OF plt2_adv FOR VALUES IN (NULL); -INSERT INTO plt2_adv VALUES (-1, -1, NULL); -ANALYZE plt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Seq Scan on plt2_adv_p2 t2 - -> Hash - -> Seq Scan on plt1_adv_p2 t1 - Filter: (b < 10) -(8 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 4 | 0004 | 4 | 0004 - 6 | 0006 | 6 | 0006 -(2 rows) - -DROP TABLE plt1_adv; -DROP TABLE plt2_adv; -DROP TABLE plt3_adv; --- Test interaction of partitioned join with partition pruning -CREATE TABLE plt1_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt1_adv_p1 PARTITION OF plt1_adv FOR VALUES IN ('0001'); -CREATE TABLE plt1_adv_p2 PARTITION OF plt1_adv FOR VALUES IN ('0002'); -CREATE TABLE plt1_adv_p3 PARTITION OF plt1_adv FOR VALUES IN ('0003'); -CREATE TABLE plt1_adv_p4 PARTITION OF plt1_adv FOR VALUES IN (NULL, '0004', '0005'); -INSERT INTO plt1_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (1, 2, 3, 4, 5); -INSERT INTO plt1_adv VALUES (-1, -1, NULL); -ANALYZE plt1_adv; -CREATE TABLE plt2_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt2_adv_p1 PARTITION OF plt2_adv FOR VALUES IN ('0001', '0002'); -CREATE TABLE plt2_adv_p2 PARTITION OF plt2_adv FOR VALUES IN (NULL); -CREATE TABLE plt2_adv_p3 PARTITION OF plt2_adv FOR VALUES IN ('0003'); -CREATE TABLE plt2_adv_p4 PARTITION OF plt2_adv FOR VALUES IN ('0004', '0005'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 10, 'FM0000') FROM generate_series(1, 299) i WHERE i % 10 IN (1, 2, 3, 4, 5); -INSERT INTO plt2_adv VALUES (-1, -1, NULL); -ANALYZE plt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IN ('0003', '0004', '0005') AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p3 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_1 - Filter: ((b < 10) AND (c = ANY ('{0003,0004,0005}'::text[]))) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p4 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p4 t1_2 - Filter: ((b < 10) AND (c = ANY ('{0003,0004,0005}'::text[]))) -(15 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IN ('0003', '0004', '0005') AND t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 5 | 0005 | 5 | 0005 -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IS NULL AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Right Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Seq Scan on plt2_adv_p4 t2 - -> Hash - -> Seq Scan on plt1_adv_p4 t1 - Filter: ((c IS NULL) AND (b < 10)) -(8 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IS NULL AND t1.b < 10 ORDER BY t1.a; - a | c | a | c -----+---+---+--- - -1 | | | -(1 row) - -CREATE TABLE plt1_adv_default PARTITION OF plt1_adv DEFAULT; -ANALYZE plt1_adv; -CREATE TABLE plt2_adv_default PARTITION OF plt2_adv DEFAULT; -ANALYZE plt2_adv; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IN ('0003', '0004', '0005') AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------------------------------------------ - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) - -> Seq Scan on plt2_adv_p3 t2_1 - -> Hash - -> Seq Scan on plt1_adv_p3 t1_1 - Filter: ((b < 10) AND (c = ANY ('{0003,0004,0005}'::text[]))) - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) - -> Seq Scan on plt2_adv_p4 t2_2 - -> Hash - -> Seq Scan on plt1_adv_p4 t1_2 - Filter: ((b < 10) AND (c = ANY ('{0003,0004,0005}'::text[]))) -(15 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 INNER JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IN ('0003', '0004', '0005') AND t1.b < 10 ORDER BY t1.a; - a | c | a | c ----+------+---+------ - 3 | 0003 | 3 | 0003 - 4 | 0004 | 4 | 0004 - 5 | 0005 | 5 | 0005 -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IS NULL AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- - Sort - Sort Key: t1.a - -> Hash Right Join - Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) - -> Seq Scan on plt2_adv_p4 t2 - -> Hash - -> Seq Scan on plt1_adv_p4 t1 - Filter: ((c IS NULL) AND (b < 10)) -(8 rows) - -SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE t1.c IS NULL AND t1.b < 10 ORDER BY t1.a; - a | c | a | c -----+---+---+--- - -1 | | | -(1 row) - -DROP TABLE plt1_adv; -DROP TABLE plt2_adv; --- Test the process_outer_partition() code path -CREATE TABLE plt1_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt1_adv_p1 PARTITION OF plt1_adv FOR VALUES IN ('0000', '0001', '0002'); -CREATE TABLE plt1_adv_p2 PARTITION OF plt1_adv FOR VALUES IN ('0003', '0004'); -INSERT INTO plt1_adv SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i; -ANALYZE plt1_adv; -CREATE TABLE plt2_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt2_adv_p1 PARTITION OF plt2_adv FOR VALUES IN ('0002'); -CREATE TABLE plt2_adv_p2 PARTITION OF plt2_adv FOR VALUES IN ('0003', '0004'); -INSERT INTO plt2_adv SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (2, 3, 4); -ANALYZE plt2_adv; -CREATE TABLE plt3_adv (a int, b int, c text) PARTITION BY LIST (c); -CREATE TABLE plt3_adv_p1 PARTITION OF plt3_adv FOR VALUES IN ('0001'); -CREATE TABLE plt3_adv_p2 PARTITION OF plt3_adv FOR VALUES IN ('0003', '0004'); -INSERT INTO plt3_adv SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 3, 4); -ANALYZE plt3_adv; --- This tests that when merging partitions from plt1_adv and plt2_adv in --- merge_list_bounds(), process_outer_partition() returns an already-assigned --- merged partition when re-called with plt1_adv_p1 for the second list value --- '0001' of that partition -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.c = t2.c)) FULL JOIN plt3_adv t3 ON (t1.c = t3.c) WHERE coalesce(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a; - QUERY PLAN ------------------------------------------------------------------------------------------------ - Sort - Sort Key: t1.c, t1.a, t2.a, t3.a - -> Append - -> Hash Full Join - Hash Cond: (t1_1.c = t3_1.c) - Filter: (((COALESCE(t1_1.a, 0) % 5) <> 3) AND ((COALESCE(t1_1.a, 0) % 5) <> 4)) - -> Hash Left Join - Hash Cond: (t1_1.c = t2_1.c) - -> Seq Scan on plt1_adv_p1 t1_1 - -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Hash - -> Seq Scan on plt3_adv_p1 t3_1 - -> Hash Full Join - Hash Cond: (t1_2.c = t3_2.c) - Filter: (((COALESCE(t1_2.a, 0) % 5) <> 3) AND ((COALESCE(t1_2.a, 0) % 5) <> 4)) - -> Hash Left Join - Hash Cond: (t1_2.c = t2_2.c) - -> Seq Scan on plt1_adv_p2 t1_2 - -> Hash - -> Seq Scan on plt2_adv_p2 t2_2 - -> Hash - -> Seq Scan on plt3_adv_p2 t3_2 -(23 rows) - -SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.c = t2.c)) FULL JOIN plt3_adv t3 ON (t1.c = t3.c) WHERE coalesce(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a; - a | c | a | c | a | c -----+------+----+------+----+------ - 0 | 0000 | | | | - 5 | 0000 | | | | - 10 | 0000 | | | | - 15 | 0000 | | | | - 20 | 0000 | | | | - 1 | 0001 | | | 1 | 0001 - 1 | 0001 | | | 6 | 0001 - 1 | 0001 | | | 11 | 0001 - 1 | 0001 | | | 16 | 0001 - 1 | 0001 | | | 21 | 0001 - 6 | 0001 | | | 1 | 0001 - 6 | 0001 | | | 6 | 0001 - 6 | 0001 | | | 11 | 0001 - 6 | 0001 | | | 16 | 0001 - 6 | 0001 | | | 21 | 0001 - 11 | 0001 | | | 1 | 0001 - 11 | 0001 | | | 6 | 0001 - 11 | 0001 | | | 11 | 0001 - 11 | 0001 | | | 16 | 0001 - 11 | 0001 | | | 21 | 0001 - 16 | 0001 | | | 1 | 0001 - 16 | 0001 | | | 6 | 0001 - 16 | 0001 | | | 11 | 0001 - 16 | 0001 | | | 16 | 0001 - 16 | 0001 | | | 21 | 0001 - 21 | 0001 | | | 1 | 0001 - 21 | 0001 | | | 6 | 0001 - 21 | 0001 | | | 11 | 0001 - 21 | 0001 | | | 16 | 0001 - 21 | 0001 | | | 21 | 0001 - 2 | 0002 | 2 | 0002 | | - 2 | 0002 | 7 | 0002 | | - 2 | 0002 | 12 | 0002 | | - 2 | 0002 | 17 | 0002 | | - 2 | 0002 | 22 | 0002 | | - 7 | 0002 | 2 | 0002 | | - 7 | 0002 | 7 | 0002 | | - 7 | 0002 | 12 | 0002 | | - 7 | 0002 | 17 | 0002 | | - 7 | 0002 | 22 | 0002 | | - 12 | 0002 | 2 | 0002 | | - 12 | 0002 | 7 | 0002 | | - 12 | 0002 | 12 | 0002 | | - 12 | 0002 | 17 | 0002 | | - 12 | 0002 | 22 | 0002 | | - 17 | 0002 | 2 | 0002 | | - 17 | 0002 | 7 | 0002 | | - 17 | 0002 | 12 | 0002 | | - 17 | 0002 | 17 | 0002 | | - 17 | 0002 | 22 | 0002 | | - 22 | 0002 | 2 | 0002 | | - 22 | 0002 | 7 | 0002 | | - 22 | 0002 | 12 | 0002 | | - 22 | 0002 | 17 | 0002 | | - 22 | 0002 | 22 | 0002 | | -(55 rows) - -DROP TABLE plt1_adv; -DROP TABLE plt2_adv; -DROP TABLE plt3_adv; --- Tests for multi-level partitioned tables -CREATE TABLE alpha (a double precision, b int, c text) PARTITION BY RANGE (a); -CREATE TABLE alpha_neg PARTITION OF alpha FOR VALUES FROM ('-Infinity') TO (0) PARTITION BY RANGE (b); -CREATE TABLE alpha_pos PARTITION OF alpha FOR VALUES FROM (0) TO (10.0) PARTITION BY LIST (c); -CREATE TABLE alpha_neg_p1 PARTITION OF alpha_neg FOR VALUES FROM (100) TO (200); -CREATE TABLE alpha_neg_p2 PARTITION OF alpha_neg FOR VALUES FROM (200) TO (300); -CREATE TABLE alpha_neg_p3 PARTITION OF alpha_neg FOR VALUES FROM (300) TO (400); -CREATE TABLE alpha_pos_p1 PARTITION OF alpha_pos FOR VALUES IN ('0001', '0003'); -CREATE TABLE alpha_pos_p2 PARTITION OF alpha_pos FOR VALUES IN ('0004', '0006'); -CREATE TABLE alpha_pos_p3 PARTITION OF alpha_pos FOR VALUES IN ('0008', '0009'); -INSERT INTO alpha_neg SELECT -1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(100, 399) i WHERE i % 10 IN (1, 3, 4, 6, 8, 9); -INSERT INTO alpha_pos SELECT 1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(100, 399) i WHERE i % 10 IN (1, 3, 4, 6, 8, 9); -ANALYZE alpha; -CREATE TABLE beta (a double precision, b int, c text) PARTITION BY RANGE (a); -CREATE TABLE beta_neg PARTITION OF beta FOR VALUES FROM (-10.0) TO (0) PARTITION BY RANGE (b); -CREATE TABLE beta_pos PARTITION OF beta FOR VALUES FROM (0) TO ('Infinity') PARTITION BY LIST (c); -CREATE TABLE beta_neg_p1 PARTITION OF beta_neg FOR VALUES FROM (100) TO (150); -CREATE TABLE beta_neg_p2 PARTITION OF beta_neg FOR VALUES FROM (200) TO (300); -CREATE TABLE beta_neg_p3 PARTITION OF beta_neg FOR VALUES FROM (350) TO (500); -CREATE TABLE beta_pos_p1 PARTITION OF beta_pos FOR VALUES IN ('0002', '0003'); -CREATE TABLE beta_pos_p2 PARTITION OF beta_pos FOR VALUES IN ('0004', '0006'); -CREATE TABLE beta_pos_p3 PARTITION OF beta_pos FOR VALUES IN ('0007', '0009'); -INSERT INTO beta_neg SELECT -1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(100, 149) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -INSERT INTO beta_neg SELECT -1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(200, 299) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -INSERT INTO beta_neg SELECT -1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(350, 499) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -INSERT INTO beta_pos SELECT 1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(100, 149) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -INSERT INTO beta_pos SELECT 1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(200, 299) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -INSERT INTO beta_pos SELECT 1.0, i, to_char(i % 10, 'FM0000') FROM generate_series(350, 499) i WHERE i % 10 IN (2, 3, 4, 6, 7, 9); -ANALYZE beta; -EXPLAIN (COSTS OFF) -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.b = t2.b) WHERE t1.b >= 125 AND t1.b < 225 ORDER BY t1.a, t1.b; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: t1.a, t1.b - -> Append - -> Hash Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.b = t2_1.b)) - -> Seq Scan on alpha_neg_p1 t1_1 - Filter: ((b >= 125) AND (b < 225)) - -> Hash - -> Seq Scan on beta_neg_p1 t2_1 - -> Hash Join - Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.b = t1_2.b)) - -> Seq Scan on beta_neg_p2 t2_2 - -> Hash - -> Seq Scan on alpha_neg_p2 t1_2 - Filter: ((b >= 125) AND (b < 225)) - -> Hash Join - Hash Cond: ((t2_4.a = t1_4.a) AND (t2_4.b = t1_4.b)) - -> Append - -> Seq Scan on beta_pos_p1 t2_4 - -> Seq Scan on beta_pos_p2 t2_5 - -> Seq Scan on beta_pos_p3 t2_6 - -> Hash - -> Append - -> Seq Scan on alpha_pos_p1 t1_4 - Filter: ((b >= 125) AND (b < 225)) - -> Seq Scan on alpha_pos_p2 t1_5 - Filter: ((b >= 125) AND (b < 225)) - -> Seq Scan on alpha_pos_p3 t1_6 - Filter: ((b >= 125) AND (b < 225)) -(29 rows) - -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.b = t2.b) WHERE t1.b >= 125 AND t1.b < 225 ORDER BY t1.a, t1.b; - a | b | c | a | b | c -----+-----+------+----+-----+------ - -1 | 126 | 0006 | -1 | 126 | 0006 - -1 | 129 | 0009 | -1 | 129 | 0009 - -1 | 133 | 0003 | -1 | 133 | 0003 - -1 | 134 | 0004 | -1 | 134 | 0004 - -1 | 136 | 0006 | -1 | 136 | 0006 - -1 | 139 | 0009 | -1 | 139 | 0009 - -1 | 143 | 0003 | -1 | 143 | 0003 - -1 | 144 | 0004 | -1 | 144 | 0004 - -1 | 146 | 0006 | -1 | 146 | 0006 - -1 | 149 | 0009 | -1 | 149 | 0009 - -1 | 203 | 0003 | -1 | 203 | 0003 - -1 | 204 | 0004 | -1 | 204 | 0004 - -1 | 206 | 0006 | -1 | 206 | 0006 - -1 | 209 | 0009 | -1 | 209 | 0009 - -1 | 213 | 0003 | -1 | 213 | 0003 - -1 | 214 | 0004 | -1 | 214 | 0004 - -1 | 216 | 0006 | -1 | 216 | 0006 - -1 | 219 | 0009 | -1 | 219 | 0009 - -1 | 223 | 0003 | -1 | 223 | 0003 - -1 | 224 | 0004 | -1 | 224 | 0004 - 1 | 126 | 0006 | 1 | 126 | 0006 - 1 | 129 | 0009 | 1 | 129 | 0009 - 1 | 133 | 0003 | 1 | 133 | 0003 - 1 | 134 | 0004 | 1 | 134 | 0004 - 1 | 136 | 0006 | 1 | 136 | 0006 - 1 | 139 | 0009 | 1 | 139 | 0009 - 1 | 143 | 0003 | 1 | 143 | 0003 - 1 | 144 | 0004 | 1 | 144 | 0004 - 1 | 146 | 0006 | 1 | 146 | 0006 - 1 | 149 | 0009 | 1 | 149 | 0009 - 1 | 203 | 0003 | 1 | 203 | 0003 - 1 | 204 | 0004 | 1 | 204 | 0004 - 1 | 206 | 0006 | 1 | 206 | 0006 - 1 | 209 | 0009 | 1 | 209 | 0009 - 1 | 213 | 0003 | 1 | 213 | 0003 - 1 | 214 | 0004 | 1 | 214 | 0004 - 1 | 216 | 0006 | 1 | 216 | 0006 - 1 | 219 | 0009 | 1 | 219 | 0009 - 1 | 223 | 0003 | 1 | 223 | 0003 - 1 | 224 | 0004 | 1 | 224 | 0004 -(40 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE ((t1.b >= 100 AND t1.b < 110) OR (t1.b >= 200 AND t1.b < 210)) AND ((t2.b >= 100 AND t2.b < 110) OR (t2.b >= 200 AND t2.b < 210)) AND t1.c IN ('0004', '0009') ORDER BY t1.a, t1.b, t2.b; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t1.b, t2.b - -> Append - -> Hash Join - Hash Cond: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Append - -> Seq Scan on alpha_neg_p1 t1_2 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Seq Scan on alpha_neg_p2 t1_3 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Hash - -> Append - -> Seq Scan on beta_neg_p1 t2_2 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Seq Scan on beta_neg_p2 t2_3 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Nested Loop - Join Filter: ((t1_4.a = t2_4.a) AND (t1_4.c = t2_4.c)) - -> Seq Scan on alpha_pos_p2 t1_4 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Seq Scan on beta_pos_p2 t2_4 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Nested Loop - Join Filter: ((t1_5.a = t2_5.a) AND (t1_5.c = t2_5.c)) - -> Seq Scan on alpha_pos_p3 t1_5 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Seq Scan on beta_pos_p3 t2_5 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) -(28 rows) - -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.c = t2.c) WHERE ((t1.b >= 100 AND t1.b < 110) OR (t1.b >= 200 AND t1.b < 210)) AND ((t2.b >= 100 AND t2.b < 110) OR (t2.b >= 200 AND t2.b < 210)) AND t1.c IN ('0004', '0009') ORDER BY t1.a, t1.b, t2.b; - a | b | c | a | b | c -----+-----+------+----+-----+------ - -1 | 104 | 0004 | -1 | 104 | 0004 - -1 | 104 | 0004 | -1 | 204 | 0004 - -1 | 109 | 0009 | -1 | 109 | 0009 - -1 | 109 | 0009 | -1 | 209 | 0009 - -1 | 204 | 0004 | -1 | 104 | 0004 - -1 | 204 | 0004 | -1 | 204 | 0004 - -1 | 209 | 0009 | -1 | 109 | 0009 - -1 | 209 | 0009 | -1 | 209 | 0009 - 1 | 104 | 0004 | 1 | 104 | 0004 - 1 | 104 | 0004 | 1 | 204 | 0004 - 1 | 109 | 0009 | 1 | 109 | 0009 - 1 | 109 | 0009 | 1 | 209 | 0009 - 1 | 204 | 0004 | 1 | 104 | 0004 - 1 | 204 | 0004 | 1 | 204 | 0004 - 1 | 209 | 0009 | 1 | 109 | 0009 - 1 | 209 | 0009 | 1 | 209 | 0009 -(16 rows) - -EXPLAIN (COSTS OFF) -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) WHERE ((t1.b >= 100 AND t1.b < 110) OR (t1.b >= 200 AND t1.b < 210)) AND ((t2.b >= 100 AND t2.b < 110) OR (t2.b >= 200 AND t2.b < 210)) AND t1.c IN ('0004', '0009') ORDER BY t1.a, t1.b; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: t1.a, t1.b - -> Append - -> Hash Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.b = t2_1.b) AND (t1_1.c = t2_1.c)) - -> Seq Scan on alpha_neg_p1 t1_1 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Hash - -> Seq Scan on beta_neg_p1 t2_1 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Hash Join - Hash Cond: ((t1_2.a = t2_2.a) AND (t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c)) - -> Seq Scan on alpha_neg_p2 t1_2 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Hash - -> Seq Scan on beta_neg_p2 t2_2 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Nested Loop - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.b = t2_3.b) AND (t1_3.c = t2_3.c)) - -> Seq Scan on alpha_pos_p2 t1_3 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Seq Scan on beta_pos_p2 t2_3 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) - -> Nested Loop - Join Filter: ((t1_4.a = t2_4.a) AND (t1_4.b = t2_4.b) AND (t1_4.c = t2_4.c)) - -> Seq Scan on alpha_pos_p3 t1_4 - Filter: ((c = ANY ('{0004,0009}'::text[])) AND (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210)))) - -> Seq Scan on beta_pos_p3 t2_4 - Filter: (((b >= 100) AND (b < 110)) OR ((b >= 200) AND (b < 210))) -(29 rows) - -SELECT t1.*, t2.* FROM alpha t1 INNER JOIN beta t2 ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) WHERE ((t1.b >= 100 AND t1.b < 110) OR (t1.b >= 200 AND t1.b < 210)) AND ((t2.b >= 100 AND t2.b < 110) OR (t2.b >= 200 AND t2.b < 210)) AND t1.c IN ('0004', '0009') ORDER BY t1.a, t1.b; - a | b | c | a | b | c -----+-----+------+----+-----+------ - -1 | 104 | 0004 | -1 | 104 | 0004 - -1 | 109 | 0009 | -1 | 109 | 0009 - -1 | 204 | 0004 | -1 | 204 | 0004 - -1 | 209 | 0009 | -1 | 209 | 0009 - 1 | 104 | 0004 | 1 | 104 | 0004 - 1 | 109 | 0009 | 1 | 109 | 0009 - 1 | 204 | 0004 | 1 | 204 | 0004 - 1 | 209 | 0009 | 1 | 209 | 0009 -(8 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_prune.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_prune.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_prune.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_prune.out 2021-10-30 22:09:26 -0600 @@ -1,3865 +1,2 @@ --- --- Test partitioning planner code --- --- Force generic plans to be used for all prepared statements in this file. -set plan_cache_mode = force_generic_plan; -create table lp (a char) partition by list (a); -create table lp_default partition of lp default; -create table lp_ef partition of lp for values in ('e', 'f'); -create table lp_ad partition of lp for values in ('a', 'd'); -create table lp_bc partition of lp for values in ('b', 'c'); -create table lp_g partition of lp for values in ('g'); -create table lp_null partition of lp for values in (null); -explain (costs off) select * from lp; - QUERY PLAN ------------------------------------ - Append - -> Seq Scan on lp_ad lp_1 - -> Seq Scan on lp_bc lp_2 - -> Seq Scan on lp_ef lp_3 - -> Seq Scan on lp_g lp_4 - -> Seq Scan on lp_null lp_5 - -> Seq Scan on lp_default lp_6 -(7 rows) - -explain (costs off) select * from lp where a > 'a' and a < 'd'; - QUERY PLAN ------------------------------------------------------------ - Append - -> Seq Scan on lp_bc lp_1 - Filter: ((a > 'a'::bpchar) AND (a < 'd'::bpchar)) - -> Seq Scan on lp_default lp_2 - Filter: ((a > 'a'::bpchar) AND (a < 'd'::bpchar)) -(5 rows) - -explain (costs off) select * from lp where a > 'a' and a <= 'd'; - QUERY PLAN ------------------------------------------------------------- - Append - -> Seq Scan on lp_ad lp_1 - Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar)) - -> Seq Scan on lp_bc lp_2 - Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar)) - -> Seq Scan on lp_default lp_3 - Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar)) -(7 rows) - -explain (costs off) select * from lp where a = 'a'; - QUERY PLAN ------------------------------ - Seq Scan on lp_ad lp - Filter: (a = 'a'::bpchar) -(2 rows) - -explain (costs off) select * from lp where 'a' = a; /* commuted */ - QUERY PLAN ------------------------------ - Seq Scan on lp_ad lp - Filter: ('a'::bpchar = a) -(2 rows) - -explain (costs off) select * from lp where a is not null; - QUERY PLAN ------------------------------------ - Append - -> Seq Scan on lp_ad lp_1 - Filter: (a IS NOT NULL) - -> Seq Scan on lp_bc lp_2 - Filter: (a IS NOT NULL) - -> Seq Scan on lp_ef lp_3 - Filter: (a IS NOT NULL) - -> Seq Scan on lp_g lp_4 - Filter: (a IS NOT NULL) - -> Seq Scan on lp_default lp_5 - Filter: (a IS NOT NULL) -(11 rows) - -explain (costs off) select * from lp where a is null; - QUERY PLAN ------------------------- - Seq Scan on lp_null lp - Filter: (a IS NULL) -(2 rows) - -explain (costs off) select * from lp where a = 'a' or a = 'c'; - QUERY PLAN ----------------------------------------------------------- - Append - -> Seq Scan on lp_ad lp_1 - Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar)) - -> Seq Scan on lp_bc lp_2 - Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar)) -(5 rows) - -explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c'); - QUERY PLAN --------------------------------------------------------------------------------- - Append - -> Seq Scan on lp_ad lp_1 - Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar))) - -> Seq Scan on lp_bc lp_2 - Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar))) -(5 rows) - -explain (costs off) select * from lp where a <> 'g'; - QUERY PLAN ------------------------------------- - Append - -> Seq Scan on lp_ad lp_1 - Filter: (a <> 'g'::bpchar) - -> Seq Scan on lp_bc lp_2 - Filter: (a <> 'g'::bpchar) - -> Seq Scan on lp_ef lp_3 - Filter: (a <> 'g'::bpchar) - -> Seq Scan on lp_default lp_4 - Filter: (a <> 'g'::bpchar) -(9 rows) - -explain (costs off) select * from lp where a <> 'a' and a <> 'd'; - QUERY PLAN -------------------------------------------------------------- - Append - -> Seq Scan on lp_bc lp_1 - Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) - -> Seq Scan on lp_ef lp_2 - Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) - -> Seq Scan on lp_g lp_3 - Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) - -> Seq Scan on lp_default lp_4 - Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) -(9 rows) - -explain (costs off) select * from lp where a not in ('a', 'd'); - QUERY PLAN ------------------------------------------------- - Append - -> Seq Scan on lp_bc lp_1 - Filter: (a <> ALL ('{a,d}'::bpchar[])) - -> Seq Scan on lp_ef lp_2 - Filter: (a <> ALL ('{a,d}'::bpchar[])) - -> Seq Scan on lp_g lp_3 - Filter: (a <> ALL ('{a,d}'::bpchar[])) - -> Seq Scan on lp_default lp_4 - Filter: (a <> ALL ('{a,d}'::bpchar[])) -(9 rows) - --- collation matches the partitioning collation, pruning works -create table coll_pruning (a text collate "C") partition by list (a); -create table coll_pruning_a partition of coll_pruning for values in ('a'); -create table coll_pruning_b partition of coll_pruning for values in ('b'); -create table coll_pruning_def partition of coll_pruning default; -explain (costs off) select * from coll_pruning where a collate "C" = 'a' collate "C"; - QUERY PLAN ------------------------------------------ - Seq Scan on coll_pruning_a coll_pruning - Filter: (a = 'a'::text COLLATE "C") -(2 rows) - --- collation doesn't match the partitioning collation, no pruning occurs -explain (costs off) select * from coll_pruning where a collate "POSIX" = 'a' collate "POSIX"; - QUERY PLAN ---------------------------------------------------------- - Append - -> Seq Scan on coll_pruning_a coll_pruning_1 - Filter: ((a)::text = 'a'::text COLLATE "POSIX") - -> Seq Scan on coll_pruning_b coll_pruning_2 - Filter: ((a)::text = 'a'::text COLLATE "POSIX") - -> Seq Scan on coll_pruning_def coll_pruning_3 - Filter: ((a)::text = 'a'::text COLLATE "POSIX") -(7 rows) - -create table rlp (a int, b varchar) partition by range (a); -create table rlp_default partition of rlp default partition by list (a); -create table rlp_default_default partition of rlp_default default; -create table rlp_default_10 partition of rlp_default for values in (10); -create table rlp_default_30 partition of rlp_default for values in (30); -create table rlp_default_null partition of rlp_default for values in (null); -create table rlp1 partition of rlp for values from (minvalue) to (1); -create table rlp2 partition of rlp for values from (1) to (10); -create table rlp3 (b varchar, a int) partition by list (b varchar_ops); -create table rlp3_default partition of rlp3 default; -create table rlp3abcd partition of rlp3 for values in ('ab', 'cd'); -create table rlp3efgh partition of rlp3 for values in ('ef', 'gh'); -create table rlp3nullxy partition of rlp3 for values in (null, 'xy'); -alter table rlp attach partition rlp3 for values from (15) to (20); -create table rlp4 partition of rlp for values from (20) to (30) partition by range (a); -create table rlp4_default partition of rlp4 default; -create table rlp4_1 partition of rlp4 for values from (20) to (25); -create table rlp4_2 partition of rlp4 for values from (25) to (29); -create table rlp5 partition of rlp for values from (31) to (maxvalue) partition by range (a); -create table rlp5_default partition of rlp5 default; -create table rlp5_1 partition of rlp5 for values from (31) to (40); -explain (costs off) select * from rlp where a < 1; - QUERY PLAN ----------------------- - Seq Scan on rlp1 rlp - Filter: (a < 1) -(2 rows) - -explain (costs off) select * from rlp where 1 > a; /* commuted */ - QUERY PLAN ----------------------- - Seq Scan on rlp1 rlp - Filter: (1 > a) -(2 rows) - -explain (costs off) select * from rlp where a <= 1; - QUERY PLAN ------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a <= 1) - -> Seq Scan on rlp2 rlp_2 - Filter: (a <= 1) -(5 rows) - -explain (costs off) select * from rlp where a = 1; - QUERY PLAN ----------------------- - Seq Scan on rlp2 rlp - Filter: (a = 1) -(2 rows) - -explain (costs off) select * from rlp where a = 1::bigint; /* same as above */ - QUERY PLAN ------------------------------ - Seq Scan on rlp2 rlp - Filter: (a = '1'::bigint) -(2 rows) - -explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */ - QUERY PLAN ------------------------------------------------ - Append - -> Seq Scan on rlp1 rlp_1 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp2 rlp_2 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp3abcd rlp_3 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp3efgh rlp_4 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp3nullxy rlp_5 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp3_default rlp_6 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp4_1 rlp_7 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp4_2 rlp_8 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp4_default rlp_9 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp5_1 rlp_10 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp5_default rlp_11 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp_default_10 rlp_12 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp_default_30 rlp_13 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp_default_null rlp_14 - Filter: ((a)::numeric = '1'::numeric) - -> Seq Scan on rlp_default_default rlp_15 - Filter: ((a)::numeric = '1'::numeric) -(31 rows) - -explain (costs off) select * from rlp where a <= 10; - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a <= 10) - -> Seq Scan on rlp2 rlp_2 - Filter: (a <= 10) - -> Seq Scan on rlp_default_10 rlp_3 - Filter: (a <= 10) - -> Seq Scan on rlp_default_default rlp_4 - Filter: (a <= 10) -(9 rows) - -explain (costs off) select * from rlp where a > 10; - QUERY PLAN ----------------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: (a > 10) - -> Seq Scan on rlp3efgh rlp_2 - Filter: (a > 10) - -> Seq Scan on rlp3nullxy rlp_3 - Filter: (a > 10) - -> Seq Scan on rlp3_default rlp_4 - Filter: (a > 10) - -> Seq Scan on rlp4_1 rlp_5 - Filter: (a > 10) - -> Seq Scan on rlp4_2 rlp_6 - Filter: (a > 10) - -> Seq Scan on rlp4_default rlp_7 - Filter: (a > 10) - -> Seq Scan on rlp5_1 rlp_8 - Filter: (a > 10) - -> Seq Scan on rlp5_default rlp_9 - Filter: (a > 10) - -> Seq Scan on rlp_default_30 rlp_10 - Filter: (a > 10) - -> Seq Scan on rlp_default_default rlp_11 - Filter: (a > 10) -(23 rows) - -explain (costs off) select * from rlp where a < 15; - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a < 15) - -> Seq Scan on rlp2 rlp_2 - Filter: (a < 15) - -> Seq Scan on rlp_default_10 rlp_3 - Filter: (a < 15) - -> Seq Scan on rlp_default_default rlp_4 - Filter: (a < 15) -(9 rows) - -explain (costs off) select * from rlp where a <= 15; - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a <= 15) - -> Seq Scan on rlp2 rlp_2 - Filter: (a <= 15) - -> Seq Scan on rlp3abcd rlp_3 - Filter: (a <= 15) - -> Seq Scan on rlp3efgh rlp_4 - Filter: (a <= 15) - -> Seq Scan on rlp3nullxy rlp_5 - Filter: (a <= 15) - -> Seq Scan on rlp3_default rlp_6 - Filter: (a <= 15) - -> Seq Scan on rlp_default_10 rlp_7 - Filter: (a <= 15) - -> Seq Scan on rlp_default_default rlp_8 - Filter: (a <= 15) -(17 rows) - -explain (costs off) select * from rlp where a > 15 and b = 'ab'; - QUERY PLAN ---------------------------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_1 rlp_2 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_2 rlp_3 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_default rlp_4 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp5_1 rlp_5 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp5_default rlp_6 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_30 rlp_7 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_default rlp_8 - Filter: ((a > 15) AND ((b)::text = 'ab'::text)) -(17 rows) - -explain (costs off) select * from rlp where a = 16; - QUERY PLAN --------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: (a = 16) - -> Seq Scan on rlp3efgh rlp_2 - Filter: (a = 16) - -> Seq Scan on rlp3nullxy rlp_3 - Filter: (a = 16) - -> Seq Scan on rlp3_default rlp_4 - Filter: (a = 16) -(9 rows) - -explain (costs off) select * from rlp where a = 16 and b in ('not', 'in', 'here'); - QUERY PLAN ----------------------------------------------------------------------- - Seq Scan on rlp3_default rlp - Filter: ((a = 16) AND ((b)::text = ANY ('{not,in,here}'::text[]))) -(2 rows) - -explain (costs off) select * from rlp where a = 16 and b < 'ab'; - QUERY PLAN ---------------------------------------------------- - Seq Scan on rlp3_default rlp - Filter: (((b)::text < 'ab'::text) AND (a = 16)) -(2 rows) - -explain (costs off) select * from rlp where a = 16 and b <= 'ab'; - QUERY PLAN ----------------------------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: (((b)::text <= 'ab'::text) AND (a = 16)) - -> Seq Scan on rlp3_default rlp_2 - Filter: (((b)::text <= 'ab'::text) AND (a = 16)) -(5 rows) - -explain (costs off) select * from rlp where a = 16 and b is null; - QUERY PLAN --------------------------------------- - Seq Scan on rlp3nullxy rlp - Filter: ((b IS NULL) AND (a = 16)) -(2 rows) - -explain (costs off) select * from rlp where a = 16 and b is not null; - QUERY PLAN ------------------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: ((b IS NOT NULL) AND (a = 16)) - -> Seq Scan on rlp3efgh rlp_2 - Filter: ((b IS NOT NULL) AND (a = 16)) - -> Seq Scan on rlp3nullxy rlp_3 - Filter: ((b IS NOT NULL) AND (a = 16)) - -> Seq Scan on rlp3_default rlp_4 - Filter: ((b IS NOT NULL) AND (a = 16)) -(9 rows) - -explain (costs off) select * from rlp where a is null; - QUERY PLAN ----------------------------------- - Seq Scan on rlp_default_null rlp - Filter: (a IS NULL) -(2 rows) - -explain (costs off) select * from rlp where a is not null; - QUERY PLAN ----------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp2 rlp_2 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp3abcd rlp_3 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp3efgh rlp_4 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp3nullxy rlp_5 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp3_default rlp_6 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp4_1 rlp_7 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp4_2 rlp_8 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp4_default rlp_9 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp5_1 rlp_10 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp5_default rlp_11 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp_default_10 rlp_12 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp_default_30 rlp_13 - Filter: (a IS NOT NULL) - -> Seq Scan on rlp_default_default rlp_14 - Filter: (a IS NOT NULL) -(29 rows) - -explain (costs off) select * from rlp where a > 30; - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on rlp5_1 rlp_1 - Filter: (a > 30) - -> Seq Scan on rlp5_default rlp_2 - Filter: (a > 30) - -> Seq Scan on rlp_default_default rlp_3 - Filter: (a > 30) -(7 rows) - -explain (costs off) select * from rlp where a = 30; /* only default is scanned */ - QUERY PLAN --------------------------------- - Seq Scan on rlp_default_30 rlp - Filter: (a = 30) -(2 rows) - -explain (costs off) select * from rlp where a <= 31; - QUERY PLAN ----------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: (a <= 31) - -> Seq Scan on rlp2 rlp_2 - Filter: (a <= 31) - -> Seq Scan on rlp3abcd rlp_3 - Filter: (a <= 31) - -> Seq Scan on rlp3efgh rlp_4 - Filter: (a <= 31) - -> Seq Scan on rlp3nullxy rlp_5 - Filter: (a <= 31) - -> Seq Scan on rlp3_default rlp_6 - Filter: (a <= 31) - -> Seq Scan on rlp4_1 rlp_7 - Filter: (a <= 31) - -> Seq Scan on rlp4_2 rlp_8 - Filter: (a <= 31) - -> Seq Scan on rlp4_default rlp_9 - Filter: (a <= 31) - -> Seq Scan on rlp5_1 rlp_10 - Filter: (a <= 31) - -> Seq Scan on rlp_default_10 rlp_11 - Filter: (a <= 31) - -> Seq Scan on rlp_default_30 rlp_12 - Filter: (a <= 31) - -> Seq Scan on rlp_default_default rlp_13 - Filter: (a <= 31) -(27 rows) - -explain (costs off) select * from rlp where a = 1 or a = 7; - QUERY PLAN --------------------------------- - Seq Scan on rlp2 rlp - Filter: ((a = 1) OR (a = 7)) -(2 rows) - -explain (costs off) select * from rlp where a = 1 or b = 'ab'; - QUERY PLAN -------------------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp2 rlp_2 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp3abcd rlp_3 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_1 rlp_4 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_2 rlp_5 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp4_default rlp_6 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp5_1 rlp_7 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp5_default rlp_8 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_10 rlp_9 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_30 rlp_10 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_null rlp_11 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) - -> Seq Scan on rlp_default_default rlp_12 - Filter: ((a = 1) OR ((b)::text = 'ab'::text)) -(25 rows) - -explain (costs off) select * from rlp where a > 20 and a < 27; - QUERY PLAN ------------------------------------------ - Append - -> Seq Scan on rlp4_1 rlp_1 - Filter: ((a > 20) AND (a < 27)) - -> Seq Scan on rlp4_2 rlp_2 - Filter: ((a > 20) AND (a < 27)) -(5 rows) - -explain (costs off) select * from rlp where a = 29; - QUERY PLAN ------------------------------- - Seq Scan on rlp4_default rlp - Filter: (a = 29) -(2 rows) - -explain (costs off) select * from rlp where a >= 29; - QUERY PLAN ---------------------------------------------- - Append - -> Seq Scan on rlp4_default rlp_1 - Filter: (a >= 29) - -> Seq Scan on rlp5_1 rlp_2 - Filter: (a >= 29) - -> Seq Scan on rlp5_default rlp_3 - Filter: (a >= 29) - -> Seq Scan on rlp_default_30 rlp_4 - Filter: (a >= 29) - -> Seq Scan on rlp_default_default rlp_5 - Filter: (a >= 29) -(11 rows) - -explain (costs off) select * from rlp where a < 1 or (a > 20 and a < 25); - QUERY PLAN ------------------------------------------------------- - Append - -> Seq Scan on rlp1 rlp_1 - Filter: ((a < 1) OR ((a > 20) AND (a < 25))) - -> Seq Scan on rlp4_1 rlp_2 - Filter: ((a < 1) OR ((a > 20) AND (a < 25))) -(5 rows) - --- where clause contradicts sub-partition's constraint -explain (costs off) select * from rlp where a = 20 or a = 40; - QUERY PLAN ----------------------------------------- - Append - -> Seq Scan on rlp4_1 rlp_1 - Filter: ((a = 20) OR (a = 40)) - -> Seq Scan on rlp5_default rlp_2 - Filter: ((a = 20) OR (a = 40)) -(5 rows) - -explain (costs off) select * from rlp3 where a = 20; /* empty */ - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - --- redundant clauses are eliminated -explain (costs off) select * from rlp where a > 1 and a = 10; /* only default */ - QUERY PLAN ----------------------------------- - Seq Scan on rlp_default_10 rlp - Filter: ((a > 1) AND (a = 10)) -(2 rows) - -explain (costs off) select * from rlp where a > 1 and a >=15; /* rlp3 onwards, including default */ - QUERY PLAN ----------------------------------------------- - Append - -> Seq Scan on rlp3abcd rlp_1 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp3efgh rlp_2 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp3nullxy rlp_3 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp3_default rlp_4 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp4_1 rlp_5 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp4_2 rlp_6 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp4_default rlp_7 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp5_1 rlp_8 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp5_default rlp_9 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp_default_30 rlp_10 - Filter: ((a > 1) AND (a >= 15)) - -> Seq Scan on rlp_default_default rlp_11 - Filter: ((a > 1) AND (a >= 15)) -(23 rows) - -explain (costs off) select * from rlp where a = 1 and a = 3; /* empty */ - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a = 15); - QUERY PLAN -------------------------------------------------------------------- - Append - -> Seq Scan on rlp2 rlp_1 - Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15))) - -> Seq Scan on rlp3abcd rlp_2 - Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15))) - -> Seq Scan on rlp3efgh rlp_3 - Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15))) - -> Seq Scan on rlp3nullxy rlp_4 - Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15))) - -> Seq Scan on rlp3_default rlp_5 - Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15))) -(11 rows) - --- multi-column keys -create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); -create table mc3p_default partition of mc3p default; -create table mc3p0 partition of mc3p for values from (minvalue, minvalue, minvalue) to (1, 1, 1); -create table mc3p1 partition of mc3p for values from (1, 1, 1) to (10, 5, 10); -create table mc3p2 partition of mc3p for values from (10, 5, 10) to (10, 10, 10); -create table mc3p3 partition of mc3p for values from (10, 10, 10) to (10, 10, 20); -create table mc3p4 partition of mc3p for values from (10, 10, 20) to (10, maxvalue, maxvalue); -create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10); -create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20); -create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); -explain (costs off) select * from mc3p where a = 1; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (a = 1) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (a = 1) - -> Seq Scan on mc3p_default mc3p_3 - Filter: (a = 1) -(7 rows) - -explain (costs off) select * from mc3p where a = 1 and abs(b) < 1; - QUERY PLAN --------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: ((a = 1) AND (abs(b) < 1)) - -> Seq Scan on mc3p_default mc3p_2 - Filter: ((a = 1) AND (abs(b) < 1)) -(5 rows) - -explain (costs off) select * from mc3p where a = 1 and abs(b) = 1; - QUERY PLAN --------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: ((a = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: ((a = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p_default mc3p_3 - Filter: ((a = 1) AND (abs(b) = 1)) -(7 rows) - -explain (costs off) select * from mc3p where a = 1 and abs(b) = 1 and c < 8; - QUERY PLAN --------------------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1)) -(5 rows) - -explain (costs off) select * from mc3p where a = 10 and abs(b) between 5 and 35; - QUERY PLAN ------------------------------------------------------------------ - Append - -> Seq Scan on mc3p1 mc3p_1 - Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35)) - -> Seq Scan on mc3p2 mc3p_2 - Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35)) - -> Seq Scan on mc3p3 mc3p_3 - Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35)) - -> Seq Scan on mc3p4 mc3p_4 - Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35)) - -> Seq Scan on mc3p_default mc3p_5 - Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35)) -(11 rows) - -explain (costs off) select * from mc3p where a > 10; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p5 mc3p_1 - Filter: (a > 10) - -> Seq Scan on mc3p6 mc3p_2 - Filter: (a > 10) - -> Seq Scan on mc3p7 mc3p_3 - Filter: (a > 10) - -> Seq Scan on mc3p_default mc3p_4 - Filter: (a > 10) -(9 rows) - -explain (costs off) select * from mc3p where a >= 10; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p1 mc3p_1 - Filter: (a >= 10) - -> Seq Scan on mc3p2 mc3p_2 - Filter: (a >= 10) - -> Seq Scan on mc3p3 mc3p_3 - Filter: (a >= 10) - -> Seq Scan on mc3p4 mc3p_4 - Filter: (a >= 10) - -> Seq Scan on mc3p5 mc3p_5 - Filter: (a >= 10) - -> Seq Scan on mc3p6 mc3p_6 - Filter: (a >= 10) - -> Seq Scan on mc3p7 mc3p_7 - Filter: (a >= 10) - -> Seq Scan on mc3p_default mc3p_8 - Filter: (a >= 10) -(17 rows) - -explain (costs off) select * from mc3p where a < 10; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (a < 10) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (a < 10) - -> Seq Scan on mc3p_default mc3p_3 - Filter: (a < 10) -(7 rows) - -explain (costs off) select * from mc3p where a <= 10 and abs(b) < 10; - QUERY PLAN ------------------------------------------------ - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: ((a <= 10) AND (abs(b) < 10)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: ((a <= 10) AND (abs(b) < 10)) - -> Seq Scan on mc3p2 mc3p_3 - Filter: ((a <= 10) AND (abs(b) < 10)) - -> Seq Scan on mc3p_default mc3p_4 - Filter: ((a <= 10) AND (abs(b) < 10)) -(9 rows) - -explain (costs off) select * from mc3p where a = 11 and abs(b) = 0; - QUERY PLAN ---------------------------------------- - Seq Scan on mc3p_default mc3p - Filter: ((a = 11) AND (abs(b) = 0)) -(2 rows) - -explain (costs off) select * from mc3p where a = 20 and abs(b) = 10 and c = 100; - QUERY PLAN ------------------------------------------------------- - Seq Scan on mc3p6 mc3p - Filter: ((a = 20) AND (c = 100) AND (abs(b) = 10)) -(2 rows) - -explain (costs off) select * from mc3p where a > 20; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p7 mc3p_1 - Filter: (a > 20) - -> Seq Scan on mc3p_default mc3p_2 - Filter: (a > 20) -(5 rows) - -explain (costs off) select * from mc3p where a >= 20; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc3p5 mc3p_1 - Filter: (a >= 20) - -> Seq Scan on mc3p6 mc3p_2 - Filter: (a >= 20) - -> Seq Scan on mc3p7 mc3p_3 - Filter: (a >= 20) - -> Seq Scan on mc3p_default mc3p_4 - Filter: (a >= 20) -(9 rows) - -explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on mc3p1 mc3p_1 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20))) - -> Seq Scan on mc3p2 mc3p_2 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20))) - -> Seq Scan on mc3p5 mc3p_3 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20))) - -> Seq Scan on mc3p_default mc3p_4 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20))) -(9 rows) - -explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1)) - -> Seq Scan on mc3p2 mc3p_3 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1)) - -> Seq Scan on mc3p5 mc3p_4 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1)) - -> Seq Scan on mc3p_default mc3p_5 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1)) -(11 rows) - -explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1 or a = 1; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1)) - -> Seq Scan on mc3p2 mc3p_3 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1)) - -> Seq Scan on mc3p5 mc3p_4 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1)) - -> Seq Scan on mc3p_default mc3p_5 - Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1)) -(11 rows) - -explain (costs off) select * from mc3p where a = 1 or abs(b) = 1 or c = 1; - QUERY PLAN ------------------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p1 mc3p_2 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p2 mc3p_3 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p3 mc3p_4 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p4 mc3p_5 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p5 mc3p_6 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p6 mc3p_7 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p7 mc3p_8 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) - -> Seq Scan on mc3p_default mc3p_9 - Filter: ((a = 1) OR (abs(b) = 1) OR (c = 1)) -(19 rows) - -explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 10); - QUERY PLAN ------------------------------------------------------------------------------- - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) - -> Seq Scan on mc3p2 mc3p_3 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) - -> Seq Scan on mc3p3 mc3p_4 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) - -> Seq Scan on mc3p4 mc3p_5 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) - -> Seq Scan on mc3p_default mc3p_6 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10))) -(13 rows) - -explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 9); - QUERY PLAN ------------------------------------------------------------------------------ - Append - -> Seq Scan on mc3p0 mc3p_1 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9))) - -> Seq Scan on mc3p1 mc3p_2 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9))) - -> Seq Scan on mc3p2 mc3p_3 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9))) - -> Seq Scan on mc3p_default mc3p_4 - Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9))) -(9 rows) - --- a simpler multi-column keys case -create table mc2p (a int, b int) partition by range (a, b); -create table mc2p_default partition of mc2p default; -create table mc2p0 partition of mc2p for values from (minvalue, minvalue) to (1, minvalue); -create table mc2p1 partition of mc2p for values from (1, minvalue) to (1, 1); -create table mc2p2 partition of mc2p for values from (1, 1) to (2, minvalue); -create table mc2p3 partition of mc2p for values from (2, minvalue) to (2, 1); -create table mc2p4 partition of mc2p for values from (2, 1) to (2, maxvalue); -create table mc2p5 partition of mc2p for values from (2, maxvalue) to (maxvalue, maxvalue); -explain (costs off) select * from mc2p where a < 2; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc2p0 mc2p_1 - Filter: (a < 2) - -> Seq Scan on mc2p1 mc2p_2 - Filter: (a < 2) - -> Seq Scan on mc2p2 mc2p_3 - Filter: (a < 2) - -> Seq Scan on mc2p_default mc2p_4 - Filter: (a < 2) -(9 rows) - -explain (costs off) select * from mc2p where a = 2 and b < 1; - QUERY PLAN ---------------------------------- - Seq Scan on mc2p3 mc2p - Filter: ((b < 1) AND (a = 2)) -(2 rows) - -explain (costs off) select * from mc2p where a > 1; - QUERY PLAN ---------------------------------------- - Append - -> Seq Scan on mc2p2 mc2p_1 - Filter: (a > 1) - -> Seq Scan on mc2p3 mc2p_2 - Filter: (a > 1) - -> Seq Scan on mc2p4 mc2p_3 - Filter: (a > 1) - -> Seq Scan on mc2p5 mc2p_4 - Filter: (a > 1) - -> Seq Scan on mc2p_default mc2p_5 - Filter: (a > 1) -(11 rows) - -explain (costs off) select * from mc2p where a = 1 and b > 1; - QUERY PLAN ---------------------------------- - Seq Scan on mc2p2 mc2p - Filter: ((b > 1) AND (a = 1)) -(2 rows) - --- all partitions but the default one should be pruned -explain (costs off) select * from mc2p where a = 1 and b is null; - QUERY PLAN -------------------------------------- - Seq Scan on mc2p_default mc2p - Filter: ((b IS NULL) AND (a = 1)) -(2 rows) - -explain (costs off) select * from mc2p where a is null and b is null; - QUERY PLAN ------------------------------------------ - Seq Scan on mc2p_default mc2p - Filter: ((a IS NULL) AND (b IS NULL)) -(2 rows) - -explain (costs off) select * from mc2p where a is null and b = 1; - QUERY PLAN -------------------------------------- - Seq Scan on mc2p_default mc2p - Filter: ((a IS NULL) AND (b = 1)) -(2 rows) - -explain (costs off) select * from mc2p where a is null; - QUERY PLAN -------------------------------- - Seq Scan on mc2p_default mc2p - Filter: (a IS NULL) -(2 rows) - -explain (costs off) select * from mc2p where b is null; - QUERY PLAN -------------------------------- - Seq Scan on mc2p_default mc2p - Filter: (b IS NULL) -(2 rows) - --- boolean partitioning -create table boolpart (a bool) partition by list (a); -create table boolpart_default partition of boolpart default; -create table boolpart_t partition of boolpart for values in ('true'); -create table boolpart_f partition of boolpart for values in ('false'); -explain (costs off) select * from boolpart where a in (true, false); - QUERY PLAN ------------------------------------------------- - Append - -> Seq Scan on boolpart_f boolpart_1 - Filter: (a = ANY ('{t,f}'::boolean[])) - -> Seq Scan on boolpart_t boolpart_2 - Filter: (a = ANY ('{t,f}'::boolean[])) -(5 rows) - -explain (costs off) select * from boolpart where a = false; - QUERY PLAN ---------------------------------- - Seq Scan on boolpart_f boolpart - Filter: (NOT a) -(2 rows) - -explain (costs off) select * from boolpart where not a = false; - QUERY PLAN ---------------------------------- - Seq Scan on boolpart_t boolpart - Filter: a -(2 rows) - -explain (costs off) select * from boolpart where a is true or a is not true; - QUERY PLAN --------------------------------------------------- - Append - -> Seq Scan on boolpart_f boolpart_1 - Filter: ((a IS TRUE) OR (a IS NOT TRUE)) - -> Seq Scan on boolpart_t boolpart_2 - Filter: ((a IS TRUE) OR (a IS NOT TRUE)) -(5 rows) - -explain (costs off) select * from boolpart where a is not true; - QUERY PLAN ---------------------------------- - Seq Scan on boolpart_f boolpart - Filter: (a IS NOT TRUE) -(2 rows) - -explain (costs off) select * from boolpart where a is not true and a is not false; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from boolpart where a is unknown; - QUERY PLAN ------------------------------------------------ - Append - -> Seq Scan on boolpart_f boolpart_1 - Filter: (a IS UNKNOWN) - -> Seq Scan on boolpart_t boolpart_2 - Filter: (a IS UNKNOWN) - -> Seq Scan on boolpart_default boolpart_3 - Filter: (a IS UNKNOWN) -(7 rows) - -explain (costs off) select * from boolpart where a is not unknown; - QUERY PLAN ------------------------------------------------ - Append - -> Seq Scan on boolpart_f boolpart_1 - Filter: (a IS NOT UNKNOWN) - -> Seq Scan on boolpart_t boolpart_2 - Filter: (a IS NOT UNKNOWN) - -> Seq Scan on boolpart_default boolpart_3 - Filter: (a IS NOT UNKNOWN) -(7 rows) - -create table boolrangep (a bool, b bool, c int) partition by range (a,b,c); -create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100); -create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100); -create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50); -create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100); --- try a more complex case that's been known to trip up pruning in the past -explain (costs off) select * from boolrangep where not a and not b and c = 25; - QUERY PLAN ----------------------------------------------- - Seq Scan on boolrangep_ff1 boolrangep - Filter: ((NOT a) AND (NOT b) AND (c = 25)) -(2 rows) - --- test scalar-to-array operators -create table coercepart (a varchar) partition by list (a); -create table coercepart_ab partition of coercepart for values in ('ab'); -create table coercepart_bc partition of coercepart for values in ('bc'); -create table coercepart_cd partition of coercepart for values in ('cd'); -explain (costs off) select * from coercepart where a in ('ab', to_char(125, '999')); - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[])) - -> Seq Scan on coercepart_cd coercepart_3 - Filter: ((a)::text = ANY ((ARRAY['ab'::character varying, (to_char(125, '999'::text))::character varying])::text[])) -(7 rows) - -explain (costs off) select * from coercepart where a ~ any ('{ab}'); - QUERY PLAN ----------------------------------------------------- - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text ~ ANY ('{ab}'::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text ~ ANY ('{ab}'::text[])) - -> Seq Scan on coercepart_cd coercepart_3 - Filter: ((a)::text ~ ANY ('{ab}'::text[])) -(7 rows) - -explain (costs off) select * from coercepart where a !~ all ('{ab}'); - QUERY PLAN ------------------------------------------------------ - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text !~ ALL ('{ab}'::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text !~ ALL ('{ab}'::text[])) - -> Seq Scan on coercepart_cd coercepart_3 - Filter: ((a)::text !~ ALL ('{ab}'::text[])) -(7 rows) - -explain (costs off) select * from coercepart where a ~ any ('{ab,bc}'); - QUERY PLAN -------------------------------------------------------- - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text ~ ANY ('{ab,bc}'::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text ~ ANY ('{ab,bc}'::text[])) - -> Seq Scan on coercepart_cd coercepart_3 - Filter: ((a)::text ~ ANY ('{ab,bc}'::text[])) -(7 rows) - -explain (costs off) select * from coercepart where a !~ all ('{ab,bc}'); - QUERY PLAN --------------------------------------------------------- - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text !~ ALL ('{ab,bc}'::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text !~ ALL ('{ab,bc}'::text[])) - -> Seq Scan on coercepart_cd coercepart_3 - Filter: ((a)::text !~ ALL ('{ab,bc}'::text[])) -(7 rows) - -explain (costs off) select * from coercepart where a = any ('{ab,bc}'); - QUERY PLAN -------------------------------------------------------- - Append - -> Seq Scan on coercepart_ab coercepart_1 - Filter: ((a)::text = ANY ('{ab,bc}'::text[])) - -> Seq Scan on coercepart_bc coercepart_2 - Filter: ((a)::text = ANY ('{ab,bc}'::text[])) -(5 rows) - -explain (costs off) select * from coercepart where a = any ('{ab,null}'); - QUERY PLAN ---------------------------------------------------- - Seq Scan on coercepart_ab coercepart - Filter: ((a)::text = ANY ('{ab,NULL}'::text[])) -(2 rows) - -explain (costs off) select * from coercepart where a = any (null::text[]); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from coercepart where a = all ('{ab}'); - QUERY PLAN ----------------------------------------------- - Seq Scan on coercepart_ab coercepart - Filter: ((a)::text = ALL ('{ab}'::text[])) -(2 rows) - -explain (costs off) select * from coercepart where a = all ('{ab,bc}'); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from coercepart where a = all ('{ab,null}'); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from coercepart where a = all (null::text[]); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table coercepart; -CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a); -CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2); -CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a); -CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT; -CREATE TABLE part_rev (b INT, c INT, a INT); -ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail -ERROR: table "part_rev" contains column "c" not found in parent "part" -DETAIL: The new partition may contain only the columns present in parent. -ALTER TABLE part_rev DROP COLUMN c; -ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- now it's ok -INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL); -EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3; - QUERY PLAN ---------------------------------------------------------- - Sort - Sort Key: ((part.tableoid)::regclass), part.a, part.b - -> Seq Scan on part_p2_p1 part - Filter: (a IS NULL) -(4 rows) - -EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x; - QUERY PLAN ------------------------------------------------ - Sort - Output: p.x, p.b - Sort Key: p.x - -> Append - -> Seq Scan on public.part_p1 p_1 - Output: p_1.x, p_1.b - -> Seq Scan on public.part_rev p_2 - Output: p_2.x, p_2.b - -> Seq Scan on public.part_p2_p1 p_3 - Output: p_3.x, p_3.b -(10 rows) - --- --- some more cases --- --- --- pruning for partitioned table appearing inside a sub-query --- --- pruning won't work for mc3p, because some keys are Params -explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = t1.b and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1; - QUERY PLAN ------------------------------------------------------------------------ - Nested Loop - -> Append - -> Seq Scan on mc2p1 t1_1 - Filter: (a = 1) - -> Seq Scan on mc2p2 t1_2 - Filter: (a = 1) - -> Seq Scan on mc2p_default t1_3 - Filter: (a = 1) - -> Aggregate - -> Append - -> Seq Scan on mc3p0 t2_1 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p1 t2_2 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p2 t2_3 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p3 t2_4 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p4 t2_5 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p5 t2_6 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p6 t2_7 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p7 t2_8 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p_default t2_9 - Filter: ((a = t1.b) AND (c = 1) AND (abs(b) = 1)) -(28 rows) - --- pruning should work fine, because values for a prefix of keys (a, b) are --- available -explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.c = t1.b and abs(t2.b) = 1 and t2.a = 1) s where t1.a = 1; - QUERY PLAN ------------------------------------------------------------------------ - Nested Loop - -> Append - -> Seq Scan on mc2p1 t1_1 - Filter: (a = 1) - -> Seq Scan on mc2p2 t1_2 - Filter: (a = 1) - -> Seq Scan on mc2p_default t1_3 - Filter: (a = 1) - -> Aggregate - -> Append - -> Seq Scan on mc3p0 t2_1 - Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p1 t2_2 - Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1)) - -> Seq Scan on mc3p_default t2_3 - Filter: ((c = t1.b) AND (a = 1) AND (abs(b) = 1)) -(16 rows) - --- also here, because values for all keys are provided -explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = 1 and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1; - QUERY PLAN --------------------------------------------------------------- - Nested Loop - -> Aggregate - -> Seq Scan on mc3p1 t2 - Filter: ((a = 1) AND (c = 1) AND (abs(b) = 1)) - -> Append - -> Seq Scan on mc2p1 t1_1 - Filter: (a = 1) - -> Seq Scan on mc2p2 t1_2 - Filter: (a = 1) - -> Seq Scan on mc2p_default t1_3 - Filter: (a = 1) -(11 rows) - --- --- pruning with clauses containing <> operator --- --- doesn't prune range partitions -create table rp (a int) partition by range (a); -create table rp0 partition of rp for values from (minvalue) to (1); -create table rp1 partition of rp for values from (1) to (2); -create table rp2 partition of rp for values from (2) to (maxvalue); -explain (costs off) select * from rp where a <> 1; - QUERY PLAN ----------------------------- - Append - -> Seq Scan on rp0 rp_1 - Filter: (a <> 1) - -> Seq Scan on rp1 rp_2 - Filter: (a <> 1) - -> Seq Scan on rp2 rp_3 - Filter: (a <> 1) -(7 rows) - -explain (costs off) select * from rp where a <> 1 and a <> 2; - QUERY PLAN ------------------------------------------ - Append - -> Seq Scan on rp0 rp_1 - Filter: ((a <> 1) AND (a <> 2)) - -> Seq Scan on rp1 rp_2 - Filter: ((a <> 1) AND (a <> 2)) - -> Seq Scan on rp2 rp_3 - Filter: ((a <> 1) AND (a <> 2)) -(7 rows) - --- null partition should be eliminated due to strict <> clause. -explain (costs off) select * from lp where a <> 'a'; - QUERY PLAN ------------------------------------- - Append - -> Seq Scan on lp_ad lp_1 - Filter: (a <> 'a'::bpchar) - -> Seq Scan on lp_bc lp_2 - Filter: (a <> 'a'::bpchar) - -> Seq Scan on lp_ef lp_3 - Filter: (a <> 'a'::bpchar) - -> Seq Scan on lp_g lp_4 - Filter: (a <> 'a'::bpchar) - -> Seq Scan on lp_default lp_5 - Filter: (a <> 'a'::bpchar) -(11 rows) - --- ensure we detect contradictions in clauses; a can't be NULL and NOT NULL. -explain (costs off) select * from lp where a <> 'a' and a is null; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from lp where (a <> 'a' and a <> 'd') or a is null; - QUERY PLAN ------------------------------------------------------------------------------- - Append - -> Seq Scan on lp_bc lp_1 - Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) - -> Seq Scan on lp_ef lp_2 - Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) - -> Seq Scan on lp_g lp_3 - Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) - -> Seq Scan on lp_null lp_4 - Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) - -> Seq Scan on lp_default lp_5 - Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) -(11 rows) - --- check that it also works for a partitioned table that's not root, --- which in this case are partitions of rlp that are themselves --- list-partitioned on b -explain (costs off) select * from rlp where a = 15 and b <> 'ab' and b <> 'cd' and b <> 'xy' and b is not null; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on rlp3efgh rlp_1 - Filter: ((b IS NOT NULL) AND ((b)::text <> 'ab'::text) AND ((b)::text <> 'cd'::text) AND ((b)::text <> 'xy'::text) AND (a = 15)) - -> Seq Scan on rlp3_default rlp_2 - Filter: ((b IS NOT NULL) AND ((b)::text <> 'ab'::text) AND ((b)::text <> 'cd'::text) AND ((b)::text <> 'xy'::text) AND (a = 15)) -(5 rows) - --- --- different collations for different keys with same expression --- -create table coll_pruning_multi (a text) partition by range (substr(a, 1) collate "POSIX", substr(a, 1) collate "C"); -create table coll_pruning_multi1 partition of coll_pruning_multi for values from ('a', 'a') to ('a', 'e'); -create table coll_pruning_multi2 partition of coll_pruning_multi for values from ('a', 'e') to ('a', 'z'); -create table coll_pruning_multi3 partition of coll_pruning_multi for values from ('b', 'a') to ('b', 'e'); --- no pruning, because no value for the leading key -explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C"; - QUERY PLAN ------------------------------------------------------------- - Append - -> Seq Scan on coll_pruning_multi1 coll_pruning_multi_1 - Filter: (substr(a, 1) = 'e'::text COLLATE "C") - -> Seq Scan on coll_pruning_multi2 coll_pruning_multi_2 - Filter: (substr(a, 1) = 'e'::text COLLATE "C") - -> Seq Scan on coll_pruning_multi3 coll_pruning_multi_3 - Filter: (substr(a, 1) = 'e'::text COLLATE "C") -(7 rows) - --- pruning, with a value provided for the leading key -explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'a' collate "POSIX"; - QUERY PLAN ------------------------------------------------------------- - Append - -> Seq Scan on coll_pruning_multi1 coll_pruning_multi_1 - Filter: (substr(a, 1) = 'a'::text COLLATE "POSIX") - -> Seq Scan on coll_pruning_multi2 coll_pruning_multi_2 - Filter: (substr(a, 1) = 'a'::text COLLATE "POSIX") -(5 rows) - --- pruning, with values provided for both keys -explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C" and substr(a, 1) = 'a' collate "POSIX"; - QUERY PLAN ---------------------------------------------------------------------------------------------------- - Seq Scan on coll_pruning_multi2 coll_pruning_multi - Filter: ((substr(a, 1) = 'e'::text COLLATE "C") AND (substr(a, 1) = 'a'::text COLLATE "POSIX")) -(2 rows) - --- --- LIKE operators don't prune --- -create table like_op_noprune (a text) partition by list (a); -create table like_op_noprune1 partition of like_op_noprune for values in ('ABC'); -create table like_op_noprune2 partition of like_op_noprune for values in ('BCD'); -explain (costs off) select * from like_op_noprune where a like '%BC'; - QUERY PLAN ------------------------------------------------------- - Append - -> Seq Scan on like_op_noprune1 like_op_noprune_1 - Filter: (a ~~ '%BC'::text) - -> Seq Scan on like_op_noprune2 like_op_noprune_2 - Filter: (a ~~ '%BC'::text) -(5 rows) - --- --- tests wherein clause value requires a cross-type comparison function --- -create table lparted_by_int2 (a smallint) partition by list (a); -create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1); -create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384); -explain (costs off) select * from lparted_by_int2 where a = 100000000000000; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -create table rparted_by_int2 (a smallint) partition by range (a); -create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10); -create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384); --- all partitions pruned -explain (costs off) select * from rparted_by_int2 where a > 100000000000000; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue); --- all partitions but rparted_by_int2_maxvalue pruned -explain (costs off) select * from rparted_by_int2 where a > 100000000000000; - QUERY PLAN ------------------------------------------------------- - Seq Scan on rparted_by_int2_maxvalue rparted_by_int2 - Filter: (a > '100000000000000'::bigint) -(2 rows) - -drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; --- --- Test Partition pruning for HASH partitioning --- --- Use hand-rolled hash functions and operator classes to get predictable --- result on different machines. See the definitions of --- part_part_test_int4_ops and part_test_text_ops in insert.sql. --- -create table hp (a int, b text, c int) - partition by hash (a part_test_int4_ops, b part_test_text_ops); -create table hp0 partition of hp for values with (modulus 4, remainder 0); -create table hp3 partition of hp for values with (modulus 4, remainder 3); -create table hp1 partition of hp for values with (modulus 4, remainder 1); -create table hp2 partition of hp for values with (modulus 4, remainder 2); -insert into hp values (null, null, 0); -insert into hp values (1, null, 1); -insert into hp values (1, 'xxx', 2); -insert into hp values (null, 'xxx', 3); -insert into hp values (2, 'xxx', 4); -insert into hp values (1, 'abcde', 5); -select tableoid::regclass, * from hp order by c; - tableoid | a | b | c -----------+---+-------+--- - hp0 | | | 0 - hp1 | 1 | | 1 - hp0 | 1 | xxx | 2 - hp2 | | xxx | 3 - hp3 | 2 | xxx | 4 - hp2 | 1 | abcde | 5 -(6 rows) - --- partial keys won't prune, nor would non-equality conditions -explain (costs off) select * from hp where a = 1; - QUERY PLAN ----------------------------- - Append - -> Seq Scan on hp0 hp_1 - Filter: (a = 1) - -> Seq Scan on hp1 hp_2 - Filter: (a = 1) - -> Seq Scan on hp2 hp_3 - Filter: (a = 1) - -> Seq Scan on hp3 hp_4 - Filter: (a = 1) -(9 rows) - -explain (costs off) select * from hp where b = 'xxx'; - QUERY PLAN ------------------------------------ - Append - -> Seq Scan on hp0 hp_1 - Filter: (b = 'xxx'::text) - -> Seq Scan on hp1 hp_2 - Filter: (b = 'xxx'::text) - -> Seq Scan on hp2 hp_3 - Filter: (b = 'xxx'::text) - -> Seq Scan on hp3 hp_4 - Filter: (b = 'xxx'::text) -(9 rows) - -explain (costs off) select * from hp where a is null; - QUERY PLAN ------------------------------ - Append - -> Seq Scan on hp0 hp_1 - Filter: (a IS NULL) - -> Seq Scan on hp1 hp_2 - Filter: (a IS NULL) - -> Seq Scan on hp2 hp_3 - Filter: (a IS NULL) - -> Seq Scan on hp3 hp_4 - Filter: (a IS NULL) -(9 rows) - -explain (costs off) select * from hp where b is null; - QUERY PLAN ------------------------------ - Append - -> Seq Scan on hp0 hp_1 - Filter: (b IS NULL) - -> Seq Scan on hp1 hp_2 - Filter: (b IS NULL) - -> Seq Scan on hp2 hp_3 - Filter: (b IS NULL) - -> Seq Scan on hp3 hp_4 - Filter: (b IS NULL) -(9 rows) - -explain (costs off) select * from hp where a < 1 and b = 'xxx'; - QUERY PLAN -------------------------------------------------- - Append - -> Seq Scan on hp0 hp_1 - Filter: ((a < 1) AND (b = 'xxx'::text)) - -> Seq Scan on hp1 hp_2 - Filter: ((a < 1) AND (b = 'xxx'::text)) - -> Seq Scan on hp2 hp_3 - Filter: ((a < 1) AND (b = 'xxx'::text)) - -> Seq Scan on hp3 hp_4 - Filter: ((a < 1) AND (b = 'xxx'::text)) -(9 rows) - -explain (costs off) select * from hp where a <> 1 and b = 'yyy'; - QUERY PLAN --------------------------------------------------- - Append - -> Seq Scan on hp0 hp_1 - Filter: ((a <> 1) AND (b = 'yyy'::text)) - -> Seq Scan on hp1 hp_2 - Filter: ((a <> 1) AND (b = 'yyy'::text)) - -> Seq Scan on hp2 hp_3 - Filter: ((a <> 1) AND (b = 'yyy'::text)) - -> Seq Scan on hp3 hp_4 - Filter: ((a <> 1) AND (b = 'yyy'::text)) -(9 rows) - -explain (costs off) select * from hp where a <> 1 and b <> 'xxx'; - QUERY PLAN ---------------------------------------------------- - Append - -> Seq Scan on hp0 hp_1 - Filter: ((a <> 1) AND (b <> 'xxx'::text)) - -> Seq Scan on hp1 hp_2 - Filter: ((a <> 1) AND (b <> 'xxx'::text)) - -> Seq Scan on hp2 hp_3 - Filter: ((a <> 1) AND (b <> 'xxx'::text)) - -> Seq Scan on hp3 hp_4 - Filter: ((a <> 1) AND (b <> 'xxx'::text)) -(9 rows) - --- pruning should work if either a value or a IS NULL clause is provided for --- each of the keys -explain (costs off) select * from hp where a is null and b is null; - QUERY PLAN ------------------------------------------ - Seq Scan on hp0 hp - Filter: ((a IS NULL) AND (b IS NULL)) -(2 rows) - -explain (costs off) select * from hp where a = 1 and b is null; - QUERY PLAN -------------------------------------- - Seq Scan on hp1 hp - Filter: ((b IS NULL) AND (a = 1)) -(2 rows) - -explain (costs off) select * from hp where a = 1 and b = 'xxx'; - QUERY PLAN -------------------------------------------- - Seq Scan on hp0 hp - Filter: ((a = 1) AND (b = 'xxx'::text)) -(2 rows) - -explain (costs off) select * from hp where a is null and b = 'xxx'; - QUERY PLAN ------------------------------------------------ - Seq Scan on hp2 hp - Filter: ((a IS NULL) AND (b = 'xxx'::text)) -(2 rows) - -explain (costs off) select * from hp where a = 2 and b = 'xxx'; - QUERY PLAN -------------------------------------------- - Seq Scan on hp3 hp - Filter: ((a = 2) AND (b = 'xxx'::text)) -(2 rows) - -explain (costs off) select * from hp where a = 1 and b = 'abcde'; - QUERY PLAN ---------------------------------------------- - Seq Scan on hp2 hp - Filter: ((a = 1) AND (b = 'abcde'::text)) -(2 rows) - -explain (costs off) select * from hp where (a = 1 and b = 'abcde') or (a = 2 and b = 'xxx') or (a is null and b is null); - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- - Append - -> Seq Scan on hp0 hp_1 - Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL))) - -> Seq Scan on hp2 hp_2 - Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL))) - -> Seq Scan on hp3 hp_3 - Filter: (((a = 1) AND (b = 'abcde'::text)) OR ((a = 2) AND (b = 'xxx'::text)) OR ((a IS NULL) AND (b IS NULL))) -(7 rows) - --- test pruning when not all the partitions exist -drop table hp1; -drop table hp3; -explain (costs off) select * from hp where a = 1 and b = 'abcde'; - QUERY PLAN ---------------------------------------------- - Seq Scan on hp2 hp - Filter: ((a = 1) AND (b = 'abcde'::text)) -(2 rows) - -explain (costs off) select * from hp where a = 1 and b = 'abcde' and - (c = 2 or c = 3); - QUERY PLAN ----------------------------------------------------------------------- - Seq Scan on hp2 hp - Filter: ((a = 1) AND (b = 'abcde'::text) AND ((c = 2) OR (c = 3))) -(2 rows) - -drop table hp2; -explain (costs off) select * from hp where a = 1 and b = 'abcde' and - (c = 2 or c = 3); - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table hp; --- --- Test runtime partition pruning --- -create table ab (a int not null, b int not null) partition by list (a); -create table ab_a2 partition of ab for values in(2) partition by list (b); -create table ab_a2_b1 partition of ab_a2 for values in (1); -create table ab_a2_b2 partition of ab_a2 for values in (2); -create table ab_a2_b3 partition of ab_a2 for values in (3); -create table ab_a1 partition of ab for values in(1) partition by list (b); -create table ab_a1_b1 partition of ab_a1 for values in (1); -create table ab_a1_b2 partition of ab_a1 for values in (2); -create table ab_a1_b3 partition of ab_a1 for values in (3); -create table ab_a3 partition of ab for values in(3) partition by list (b); -create table ab_a3_b1 partition of ab_a3 for values in (1); -create table ab_a3_b2 partition of ab_a3 for values in (2); -create table ab_a3_b3 partition of ab_a3 for values in (3); --- Disallow index only scans as concurrent transactions may stop visibility --- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE --- output. -set enable_indexonlyscan = off; -prepare ab_q1 (int, int, int) as -select * from ab where a between $1 and $2 and b <= $3; -explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2, 3); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 6 - -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a2_b3 ab_3 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) -(8 rows) - -explain (analyze, costs off, summary off, timing off) execute ab_q1 (1, 2, 3); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 3 - -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a1_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a2_b2 ab_5 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) - -> Seq Scan on ab_a2_b3 ab_6 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b <= $3)) -(14 rows) - -deallocate ab_q1; --- Runtime pruning after optimizer pruning -prepare ab_q1 (int, int) as -select a from ab where a between $1 and $2 and b < 3; -explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 4 - -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) - -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) -(6 rows) - -explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 - -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) - -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) - -> Seq Scan on ab_a3_b1 ab_3 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) - -> Seq Scan on ab_a3_b2 ab_4 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < 3)) -(10 rows) - --- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at --- different levels of partitioning. -prepare ab_q2 (int, int) as -select a from ab where a between $1 and $2 and b < (select 3); -explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 6 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) - -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) - -> Seq Scan on ab_a2_b3 ab_3 (never executed) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) -(10 rows) - --- As above, but swap the PARAM_EXEC Param to the first partition level -prepare ab_q3 (int, int) as -select a from ab where b between $1 and $2 and a < (select 3); -explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); - QUERY PLAN ---------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 6 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) - -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) - -> Seq Scan on ab_a3_b2 ab_3 (never executed) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) -(10 rows) - --- Test a backwards Append scan -create table list_part (a int) partition by list (a); -create table list_part1 partition of list_part for values in (1); -create table list_part2 partition of list_part for values in (2); -create table list_part3 partition of list_part for values in (3); -create table list_part4 partition of list_part for values in (4); -insert into list_part select generate_series(1,4); -begin; --- Don't select an actual value out of the table as the order of the Append's --- subnodes may not be stable. -declare cur SCROLL CURSOR for select 1 from list_part where a > (select 1) and a < (select 4); --- move beyond the final row -move 3 from cur; --- Ensure we get two rows. -fetch backward all from cur; - ?column? ----------- - 1 - 1 -(2 rows) - -commit; -begin; --- Test run-time pruning using stable functions -create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable; --- Ensure pruning works using a stable function containing no Vars -explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1); - QUERY PLAN ------------------------------------------------------------------- - Append (actual rows=1 loops=1) - Subplans Removed: 3 - -> Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1) - Filter: (a = list_part_fn(1)) -(4 rows) - --- Ensure pruning does not take place when the function has a Var parameter -explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a); - QUERY PLAN ------------------------------------------------------------------- - Append (actual rows=4 loops=1) - -> Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1) - Filter: (a = list_part_fn(a)) - -> Seq Scan on list_part2 list_part_2 (actual rows=1 loops=1) - Filter: (a = list_part_fn(a)) - -> Seq Scan on list_part3 list_part_3 (actual rows=1 loops=1) - Filter: (a = list_part_fn(a)) - -> Seq Scan on list_part4 list_part_4 (actual rows=1 loops=1) - Filter: (a = list_part_fn(a)) -(9 rows) - --- Ensure pruning does not take place when the expression contains a Var. -explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a; - QUERY PLAN ------------------------------------------------------------------- - Append (actual rows=0 loops=1) - -> Seq Scan on list_part1 list_part_1 (actual rows=0 loops=1) - Filter: (a = (list_part_fn(1) + a)) - Rows Removed by Filter: 1 - -> Seq Scan on list_part2 list_part_2 (actual rows=0 loops=1) - Filter: (a = (list_part_fn(1) + a)) - Rows Removed by Filter: 1 - -> Seq Scan on list_part3 list_part_3 (actual rows=0 loops=1) - Filter: (a = (list_part_fn(1) + a)) - Rows Removed by Filter: 1 - -> Seq Scan on list_part4 list_part_4 (actual rows=0 loops=1) - Filter: (a = (list_part_fn(1) + a)) - Rows Removed by Filter: 1 -(13 rows) - -rollback; -drop table list_part; --- Parallel append --- Parallel queries won't necessarily get as many workers as the planner --- asked for. This affects not only the "Workers Launched:" field of EXPLAIN --- results, but also row counts and loop counts for parallel scans, Gathers, --- and everything in between. This function filters out the values we can't --- rely on to be stable. --- This removes enough info that you might wonder why bother with EXPLAIN --- ANALYZE at all. The answer is that we need to see '(never executed)' --- notations because that's the only way to verify runtime pruning. -create function explain_parallel_append(text) returns setof text -language plpgsql as -$$ -declare - ln text; -begin - for ln in - execute format('explain (analyze, costs off, summary off, timing off) %s', - $1) - loop - ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N'); - ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N loops=N'); - ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed by Filter: N'); - return next ln; - end loop; -end; -$$; -prepare ab_q4 (int, int) as -select avg(a) from ab where a between $1 and $2 and b < 4; --- Encourage use of parallel plans -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set min_parallel_table_scan_size = 0; -set max_parallel_workers_per_gather = 2; -select explain_parallel_append('execute ab_q4 (2, 2)'); - explain_parallel_append ------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - Subplans Removed: 6 - -> Parallel Seq Scan on ab_a2_b1 ab_1 (actual rows=N loops=N) - Filter: ((a >= $1) AND (a <= $2) AND (b < 4)) - -> Parallel Seq Scan on ab_a2_b2 ab_2 (actual rows=N loops=N) - Filter: ((a >= $1) AND (a <= $2) AND (b < 4)) - -> Parallel Seq Scan on ab_a2_b3 ab_3 (actual rows=N loops=N) - Filter: ((a >= $1) AND (a <= $2) AND (b < 4)) -(13 rows) - --- Test run-time pruning with IN lists. -prepare ab_q5 (int, int, int) as -select avg(a) from ab where a in($1,$2,$3) and b < 4; -select explain_parallel_append('execute ab_q5 (1, 1, 1)'); - explain_parallel_append ------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - Subplans Removed: 6 - -> Parallel Seq Scan on ab_a1_b1 ab_1 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a1_b2 ab_2 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a1_b3 ab_3 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) -(13 rows) - -select explain_parallel_append('execute ab_q5 (2, 3, 3)'); - explain_parallel_append ------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - Subplans Removed: 3 - -> Parallel Seq Scan on ab_a2_b1 ab_1 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a2_b2 ab_2 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a2_b3 ab_3 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a3_b1 ab_4 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a3_b2 ab_5 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) - -> Parallel Seq Scan on ab_a3_b3 ab_6 (actual rows=N loops=N) - Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3]))) -(19 rows) - --- Try some params whose values do not belong to any partition. -select explain_parallel_append('execute ab_q5 (33, 44, 55)'); - explain_parallel_append ------------------------------------------------------------ - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - Subplans Removed: 9 -(7 rows) - --- Test Parallel Append with PARAM_EXEC Params -select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); - explain_parallel_append ------------------------------------------------------------------------------- - Aggregate (actual rows=N loops=N) - InitPlan 1 (returns $0) - -> Result (actual rows=N loops=N) - InitPlan 2 (returns $1) - -> Result (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Params Evaluated: $0, $1 - Workers Launched: N - -> Parallel Append (actual rows=N loops=N) - -> Parallel Seq Scan on ab_a1_b2 ab_1 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) - -> Parallel Seq Scan on ab_a2_b2 ab_2 (never executed) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) - -> Parallel Seq Scan on ab_a3_b2 ab_3 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) -(16 rows) - --- Test pruning during parallel nested loop query -create table lprt_a (a int not null); --- Insert some values we won't find in ab -insert into lprt_a select 0 from generate_series(1,100); --- and insert some values that we should find. -insert into lprt_a values(1),(1); -analyze lprt_a; -create index ab_a2_b1_a_idx on ab_a2_b1 (a); -create index ab_a2_b2_a_idx on ab_a2_b2 (a); -create index ab_a2_b3_a_idx on ab_a2_b3 (a); -create index ab_a1_b1_a_idx on ab_a1_b1 (a); -create index ab_a1_b2_a_idx on ab_a1_b2 (a); -create index ab_a1_b3_a_idx on ab_a1_b3 (a); -create index ab_a3_b1_a_idx on ab_a3_b1 (a); -create index ab_a3_b2_a_idx on ab_a3_b2 (a); -create index ab_a3_b3_a_idx on ab_a3_b3 (a); -set enable_hashjoin = 0; -set enable_mergejoin = 0; -set enable_memoize = 0; -select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)'); - explain_parallel_append --------------------------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 1 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Nested Loop (actual rows=N loops=N) - -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N) - Filter: (a = ANY ('{0,0,1}'::integer[])) - -> Append (actual rows=N loops=N) - -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed) - Index Cond: (a = a.a) -(27 rows) - --- Ensure the same partitions are pruned when we make the nested loop --- parameter an Expr rather than a plain Param. -select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)'); - explain_parallel_append --------------------------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 1 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Nested Loop (actual rows=N loops=N) - -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N) - Filter: (a = ANY ('{0,0,1}'::integer[])) - -> Append (actual rows=N loops=N) - -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed) - Index Cond: (a = (a.a + 0)) - -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed) - Index Cond: (a = (a.a + 0)) -(27 rows) - -insert into lprt_a values(3),(3); -select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)'); - explain_parallel_append --------------------------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 1 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Nested Loop (actual rows=N loops=N) - -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N) - Filter: (a = ANY ('{1,0,3}'::integer[])) - -> Append (actual rows=N loops=N) - -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (actual rows=N loops=N) - Index Cond: (a = a.a) -(27 rows) - -select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); - explain_parallel_append --------------------------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 1 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Nested Loop (actual rows=N loops=N) - -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N) - Filter: (a = ANY ('{1,0,0}'::integer[])) - Rows Removed by Filter: N - -> Append (actual rows=N loops=N) - -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (actual rows=N loops=N) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed) - Index Cond: (a = a.a) -(28 rows) - -delete from lprt_a where a = 1; -select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); - explain_parallel_append -------------------------------------------------------------------------------------------------- - Finalize Aggregate (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 1 - Workers Launched: N - -> Partial Aggregate (actual rows=N loops=N) - -> Nested Loop (actual rows=N loops=N) - -> Parallel Seq Scan on lprt_a a (actual rows=N loops=N) - Filter: (a = ANY ('{1,0,0}'::integer[])) - Rows Removed by Filter: N - -> Append (actual rows=N loops=N) - -> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 ab_1 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 ab_2 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 ab_3 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 ab_4 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 ab_5 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 ab_6 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 ab_7 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 ab_8 (never executed) - Index Cond: (a = a.a) - -> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 ab_9 (never executed) - Index Cond: (a = a.a) -(28 rows) - -reset enable_hashjoin; -reset enable_mergejoin; -reset enable_memoize; -reset parallel_setup_cost; -reset parallel_tuple_cost; -reset min_parallel_table_scan_size; -reset max_parallel_workers_per_gather; --- Test run-time partition pruning with an initplan -explain (analyze, costs off, summary off, timing off) -select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a); - QUERY PLAN -------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Aggregate (actual rows=1 loops=1) - -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 (returns $1) - -> Aggregate (actual rows=1 loops=1) - -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1) - Index Cond: (a = $0) - -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) - -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed) - Index Cond: (a = $0) -(52 rows) - --- Test run-time partition pruning with UNION ALL parents -explain (analyze, costs off, summary off, timing off) -select * from (select * from ab where a = 1 union all select * from ab) ab where b = (select 1); - QUERY PLAN -------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Append (actual rows=0 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = 1) - -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) -(37 rows) - --- A case containing a UNION ALL with a non-partitioned child. -explain (analyze, costs off, summary off, timing off) -select * from (select * from ab where a = 1 union all (values(10,5)) union all select * from ab) ab where b = (select 1); - QUERY PLAN -------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Append (actual rows=0 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) - Recheck Cond: (a = 1) - Filter: (b = $0) - -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = 1) - -> Result (actual rows=0 loops=1) - One-Time Filter: (5 = $0) - -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) -(39 rows) - --- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. -create table xy_1 (x int, y int); -insert into xy_1 values(100,-10); -set enable_bitmapscan = 0; -set enable_indexscan = 0; -prepare ab_q6 as -select * from ( - select tableoid::regclass,a,b from ab -union all - select tableoid::regclass,x,y from xy_1 -union all - select tableoid::regclass,a,b from ab -) ab where a = $1 and b = (select -10); --- Ensure the xy_1 subplan is not pruned. -explain (analyze, costs off, summary off, timing off) execute ab_q6(1); - QUERY PLAN --------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 12 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b1 ab_1 (never executed) - Filter: ((a = $1) AND (b = $0)) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: ((a = $1) AND (b = $0)) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: ((a = $1) AND (b = $0)) - -> Seq Scan on xy_1 (actual rows=0 loops=1) - Filter: ((x = $1) AND (y = $0)) - Rows Removed by Filter: 1 - -> Seq Scan on ab_a1_b1 ab_4 (never executed) - Filter: ((a = $1) AND (b = $0)) - -> Seq Scan on ab_a1_b2 ab_5 (never executed) - Filter: ((a = $1) AND (b = $0)) - -> Seq Scan on ab_a1_b3 ab_6 (never executed) - Filter: ((a = $1) AND (b = $0)) -(19 rows) - --- Ensure we see just the xy_1 row. -execute ab_q6(100); - tableoid | a | b -----------+-----+----- - xy_1 | 100 | -10 -(1 row) - -reset enable_bitmapscan; -reset enable_indexscan; -deallocate ab_q1; -deallocate ab_q2; -deallocate ab_q3; -deallocate ab_q4; -deallocate ab_q5; -deallocate ab_q6; --- UPDATE on a partition subtree has been seen to have problems. -insert into ab values (1,2); -explain (analyze, costs off, summary off, timing off) -update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; - QUERY PLAN -------------------------------------------------------------------------------------------- - Update on ab_a1 (actual rows=0 loops=1) - Update on ab_a1_b1 ab_a1_1 - Update on ab_a1_b2 ab_a1_2 - Update on ab_a1_b3 ab_a1_3 - -> Nested Loop (actual rows=1 loops=1) - -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1) - Recheck Cond: (a = 1) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_a1_3 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Materialize (actual rows=1 loops=1) - -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1) - Recheck Cond: (a = 1) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) -(34 rows) - -table ab; - a | b ----+--- - 1 | 3 -(1 row) - --- Test UPDATE where source relation has run-time pruning enabled -truncate ab; -insert into ab values (1, 1), (1, 2), (1, 3), (2, 1); -explain (analyze, costs off, summary off, timing off) -update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); - QUERY PLAN ------------------------------------------------------------------------------- - Update on ab_a1 (actual rows=0 loops=1) - Update on ab_a1_b1 ab_a1_1 - Update on ab_a1_b2 ab_a1_2 - Update on ab_a1_b3 ab_a1_3 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Nested Loop (actual rows=3 loops=1) - -> Append (actual rows=3 loops=1) - -> Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1) - -> Materialize (actual rows=1 loops=3) - -> Append (actual rows=1 loops=1) - -> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) -(19 rows) - -select tableoid::regclass, * from ab; - tableoid | a | b -----------+---+--- - ab_a1_b3 | 1 | 3 - ab_a1_b3 | 1 | 3 - ab_a1_b3 | 1 | 3 - ab_a2_b1 | 2 | 1 -(4 rows) - -drop table ab, lprt_a; --- Join -create table tbl1(col1 int); -insert into tbl1 values (501), (505); --- Basic table -create table tprt (col1 int) partition by range (col1); -create table tprt_1 partition of tprt for values from (1) to (501); -create table tprt_2 partition of tprt for values from (501) to (1001); -create table tprt_3 partition of tprt for values from (1001) to (2001); -create table tprt_4 partition of tprt for values from (2001) to (3001); -create table tprt_5 partition of tprt for values from (3001) to (4001); -create table tprt_6 partition of tprt for values from (4001) to (5001); -create index tprt1_idx on tprt_1 (col1); -create index tprt2_idx on tprt_2 (col1); -create index tprt3_idx on tprt_3 (col1); -create index tprt4_idx on tprt_4 (col1); -create index tprt5_idx on tprt_5 (col1); -create index tprt6_idx on tprt_6 (col1); -insert into tprt values (10), (20), (501), (502), (505), (1001), (4500); -set enable_hashjoin = off; -set enable_mergejoin = off; -explain (analyze, costs off, summary off, timing off) -select * from tbl1 join tprt on tbl1.col1 > tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop (actual rows=6 loops=1) - -> Seq Scan on tbl1 (actual rows=2 loops=1) - -> Append (actual rows=3 loops=2) - -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (never executed) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (never executed) - Index Cond: (col1 < tbl1.col1) -(15 rows) - -explain (analyze, costs off, summary off, timing off) -select * from tbl1 join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop (actual rows=2 loops=1) - -> Seq Scan on tbl1 (actual rows=2 loops=1) - -> Append (actual rows=1 loops=2) - -> Index Scan using tprt1_idx on tprt_1 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (actual rows=1 loops=2) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (never executed) - Index Cond: (col1 = tbl1.col1) -(15 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 > tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ - 501 | 10 - 501 | 20 - 505 | 10 - 505 | 20 - 505 | 501 - 505 | 502 -(6 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 = tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ - 501 | 501 - 505 | 505 -(2 rows) - --- Multiple partitions -insert into tbl1 values (1001), (1010), (1011); -explain (analyze, costs off, summary off, timing off) -select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop (actual rows=23 loops=1) - -> Seq Scan on tbl1 (actual rows=5 loops=1) - -> Append (actual rows=5 loops=5) - -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (never executed) - Index Cond: (col1 < tbl1.col1) -(15 rows) - -explain (analyze, costs off, summary off, timing off) -select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop (actual rows=3 loops=1) - -> Seq Scan on tbl1 (actual rows=5 loops=1) - -> Append (actual rows=1 loops=5) - -> Index Scan using tprt1_idx on tprt_1 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (actual rows=1 loops=2) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (actual rows=0 loops=3) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (never executed) - Index Cond: (col1 = tbl1.col1) -(15 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 > tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ - 501 | 10 - 501 | 20 - 505 | 10 - 505 | 20 - 505 | 501 - 505 | 502 - 1001 | 10 - 1001 | 20 - 1001 | 501 - 1001 | 502 - 1001 | 505 - 1010 | 10 - 1010 | 20 - 1010 | 501 - 1010 | 502 - 1010 | 505 - 1010 | 1001 - 1011 | 10 - 1011 | 20 - 1011 | 501 - 1011 | 502 - 1011 | 505 - 1011 | 1001 -(23 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 = tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ - 501 | 501 - 505 | 505 - 1001 | 1001 -(3 rows) - --- Last partition -delete from tbl1; -insert into tbl1 values (4400); -explain (analyze, costs off, summary off, timing off) -select * from tbl1 join tprt on tbl1.col1 < tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop (actual rows=1 loops=1) - -> Seq Scan on tbl1 (actual rows=1 loops=1) - -> Append (actual rows=1 loops=1) - -> Index Scan using tprt1_idx on tprt_1 (never executed) - Index Cond: (col1 > tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (never executed) - Index Cond: (col1 > tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (never executed) - Index Cond: (col1 > tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 > tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 > tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1) - Index Cond: (col1 > tbl1.col1) -(15 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 < tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ - 4400 | 4500 -(1 row) - --- No matching partition -delete from tbl1; -insert into tbl1 values (10000); -explain (analyze, costs off, summary off, timing off) -select * from tbl1 join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN -------------------------------------------------------------------- - Nested Loop (actual rows=0 loops=1) - -> Seq Scan on tbl1 (actual rows=1 loops=1) - -> Append (actual rows=0 loops=1) - -> Index Scan using tprt1_idx on tprt_1 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt4_idx on tprt_4 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt5_idx on tprt_5 (never executed) - Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt6_idx on tprt_6 (never executed) - Index Cond: (col1 = tbl1.col1) -(15 rows) - -select tbl1.col1, tprt.col1 from tbl1 -inner join tprt on tbl1.col1 = tprt.col1 -order by tbl1.col1, tprt.col1; - col1 | col1 -------+------ -(0 rows) - -drop table tbl1, tprt; --- Test with columns defined in varying orders between each level -create table part_abc (a int not null, b int not null, c int not null) partition by list (a); -create table part_bac (b int not null, a int not null, c int not null) partition by list (b); -create table part_cab (c int not null, a int not null, b int not null) partition by list (c); -create table part_abc_p1 (a int not null, b int not null, c int not null); -alter table part_abc attach partition part_bac for values in(1); -alter table part_bac attach partition part_cab for values in(2); -alter table part_cab attach partition part_abc_p1 for values in(3); -prepare part_abc_q1 (int, int, int) as -select * from part_abc where a = $1 and b = $2 and c = $3; --- Single partition should be scanned. -explain (analyze, costs off, summary off, timing off) execute part_abc_q1 (1, 2, 3); - QUERY PLAN ----------------------------------------------------------- - Seq Scan on part_abc_p1 part_abc (actual rows=0 loops=1) - Filter: ((a = $1) AND (b = $2) AND (c = $3)) -(2 rows) - -deallocate part_abc_q1; -drop table part_abc; --- Ensure that an Append node properly handles a sub-partitioned table --- matching without any of its leaf partitions matching the clause. -create table listp (a int, b int) partition by list (a); -create table listp_1 partition of listp for values in(1) partition by list (b); -create table listp_1_1 partition of listp_1 for values in(1); -create table listp_2 partition of listp for values in(2) partition by list (b); -create table listp_2_1 partition of listp_2 for values in(2); -select * from listp where b = 1; - a | b ----+--- -(0 rows) - --- Ensure that an Append node properly can handle selection of all first level --- partitions before finally detecting the correct set of 2nd level partitions --- which match the given parameter. -prepare q1 (int,int) as select * from listp where b in ($1,$2); -explain (analyze, costs off, summary off, timing off) execute q1 (1,1); - QUERY PLAN -------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 1 - -> Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$1, $2])) -(4 rows) - -explain (analyze, costs off, summary off, timing off) execute q1 (2,2); - QUERY PLAN -------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 1 - -> Seq Scan on listp_2_1 listp_1 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$1, $2])) -(4 rows) - --- Try with no matching partitions. -explain (analyze, costs off, summary off, timing off) execute q1 (0,0); - QUERY PLAN --------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 -(2 rows) - -deallocate q1; --- Test more complex cases where a not-equal condition further eliminates partitions. -prepare q1 (int,int,int,int) as select * from listp where b in($1,$2) and $3 <> b and $4 <> b; --- Both partitions allowed by IN clause, but one disallowed by <> clause -explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0); - QUERY PLAN -------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 1 - -> Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1) - Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b)) -(4 rows) - --- Both partitions allowed by IN clause, then both excluded again by <> clauses. -explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1); - QUERY PLAN --------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 -(2 rows) - --- Ensure Params that evaluate to NULL properly prune away all partitions -explain (analyze, costs off, summary off, timing off) -select * from listp where a = (select null::int); - QUERY PLAN ------------------------------------------------------- - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on listp_1_1 listp_1 (never executed) - Filter: (a = $0) - -> Seq Scan on listp_2_1 listp_2 (never executed) - Filter: (a = $0) -(7 rows) - -drop table listp; --- --- check that stable query clauses are only used in run-time pruning --- -create table stable_qual_pruning (a timestamp) partition by range (a); -create table stable_qual_pruning1 partition of stable_qual_pruning - for values from ('2000-01-01') to ('2000-02-01'); -create table stable_qual_pruning2 partition of stable_qual_pruning - for values from ('2000-02-01') to ('2000-03-01'); -create table stable_qual_pruning3 partition of stable_qual_pruning - for values from ('3000-02-01') to ('3000-03-01'); --- comparison against a stable value requires run-time pruning -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning where a < localtimestamp; - QUERY PLAN --------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 1 - -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a < LOCALTIMESTAMP) - -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1) - Filter: (a < LOCALTIMESTAMP) -(6 rows) - --- timestamp < timestamptz comparison is only stable, not immutable -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning where a < '2000-02-01'::timestamptz; - QUERY PLAN --------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 - -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) -(4 rows) - --- check ScalarArrayOp cases -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(array['2010-02-01', '2020-01-01']::timestamp[]); - QUERY PLAN --------------------------------- - Result (actual rows=0 loops=1) - One-Time Filter: false -(2 rows) - -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(array['2000-02-01', '2010-01-01']::timestamp[]); - QUERY PLAN ----------------------------------------------------------------------------------------------------------------- - Seq Scan on stable_qual_pruning2 stable_qual_pruning (actual rows=0 loops=1) - Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Fri Jan 01 00:00:00 2010"}'::timestamp without time zone[])) -(2 rows) - -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(array['2000-02-01', localtimestamp]::timestamp[]); - QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 - -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a = ANY (ARRAY['Tue Feb 01 00:00:00 2000'::timestamp without time zone, LOCALTIMESTAMP])) -(4 rows) - -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(array['2010-02-01', '2020-01-01']::timestamptz[]); - QUERY PLAN --------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 3 -(2 rows) - -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(array['2000-02-01', '2010-01-01']::timestamptz[]); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - Subplans Removed: 2 - -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000 PST","Fri Jan 01 00:00:00 2010 PST"}'::timestamp with time zone[])) -(4 rows) - -explain (analyze, costs off, summary off, timing off) -select * from stable_qual_pruning - where a = any(null::timestamptz[]); - QUERY PLAN --------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a = ANY (NULL::timestamp with time zone[])) - -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1) - Filter: (a = ANY (NULL::timestamp with time zone[])) - -> Seq Scan on stable_qual_pruning3 stable_qual_pruning_3 (actual rows=0 loops=1) - Filter: (a = ANY (NULL::timestamp with time zone[])) -(7 rows) - -drop table stable_qual_pruning; --- --- Check that pruning with composite range partitioning works correctly when --- it must ignore clauses for trailing keys once it has seen a clause with --- non-inclusive operator for an earlier key --- -create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); -create table mc3p0 partition of mc3p - for values from (0, 0, 0) to (0, maxvalue, maxvalue); -create table mc3p1 partition of mc3p - for values from (1, 1, 1) to (2, minvalue, minvalue); -create table mc3p2 partition of mc3p - for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue); -insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1); -explain (analyze, costs off, summary off, timing off) -select * from mc3p where a < 3 and abs(b) = 1; - QUERY PLAN --------------------------------------------------------- - Append (actual rows=3 loops=1) - -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a < 3) AND (abs(b) = 1)) - -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a < 3) AND (abs(b) = 1)) - -> Seq Scan on mc3p2 mc3p_3 (actual rows=1 loops=1) - Filter: ((a < 3) AND (abs(b) = 1)) -(7 rows) - --- --- Check that pruning with composite range partitioning works correctly when --- a combination of runtime parameters is specified, not all of whose values --- are available at the same time --- -prepare ps1 as - select * from mc3p where a = $1 and abs(b) < (select 3); -explain (analyze, costs off, summary off, timing off) -execute ps1(1); - QUERY PLAN --------------------------------------------------------- - Append (actual rows=1 loops=1) - Subplans Removed: 2 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) - Filter: ((a = $1) AND (abs(b) < $0)) -(6 rows) - -deallocate ps1; -prepare ps2 as - select * from mc3p where a <= $1 and abs(b) < (select 3); -explain (analyze, costs off, summary off, timing off) -execute ps2(1); - QUERY PLAN --------------------------------------------------------- - Append (actual rows=2 loops=1) - Subplans Removed: 1 - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) - -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) -(8 rows) - -deallocate ps2; -drop table mc3p; --- Ensure runtime pruning works with initplans params with boolean types -create table boolvalues (value bool not null); -insert into boolvalues values('t'),('f'); -create table boolp (a bool) partition by list (a); -create table boolp_t partition of boolp for values in('t'); -create table boolp_f partition of boolp for values in('f'); -explain (analyze, costs off, summary off, timing off) -select * from boolp where a = (select value from boolvalues where value); - QUERY PLAN ------------------------------------------------------------ - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Seq Scan on boolvalues (actual rows=1 loops=1) - Filter: value - Rows Removed by Filter: 1 - -> Seq Scan on boolp_f boolp_1 (never executed) - Filter: (a = $0) - -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1) - Filter: (a = $0) -(9 rows) - -explain (analyze, costs off, summary off, timing off) -select * from boolp where a = (select value from boolvalues where not value); - QUERY PLAN ------------------------------------------------------------ - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Seq Scan on boolvalues (actual rows=1 loops=1) - Filter: (NOT value) - Rows Removed by Filter: 1 - -> Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1) - Filter: (a = $0) - -> Seq Scan on boolp_t boolp_2 (never executed) - Filter: (a = $0) -(9 rows) - -drop table boolp; --- --- Test run-time pruning of MergeAppend subnodes --- -set enable_seqscan = off; -set enable_sort = off; -create table ma_test (a int, b int) partition by range (a); -create table ma_test_p1 partition of ma_test for values from (0) to (10); -create table ma_test_p2 partition of ma_test for values from (10) to (20); -create table ma_test_p3 partition of ma_test for values from (20) to (30); -insert into ma_test select x,x from generate_series(0,29) t(x); -create index on ma_test (b); -analyze ma_test; -prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b; -explain (analyze, costs off, summary off, timing off) execute mt_q1(15); - QUERY PLAN ------------------------------------------------------------------------------------------ - Merge Append (actual rows=2 loops=1) - Sort Key: ma_test.b - Subplans Removed: 1 - -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_1 (actual rows=1 loops=1) - Filter: ((a >= $1) AND ((a % 10) = 5)) - Rows Removed by Filter: 9 - -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_2 (actual rows=1 loops=1) - Filter: ((a >= $1) AND ((a % 10) = 5)) - Rows Removed by Filter: 9 -(9 rows) - -execute mt_q1(15); - a ----- - 15 - 25 -(2 rows) - -explain (analyze, costs off, summary off, timing off) execute mt_q1(25); - QUERY PLAN ------------------------------------------------------------------------------------------ - Merge Append (actual rows=1 loops=1) - Sort Key: ma_test.b - Subplans Removed: 2 - -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_1 (actual rows=1 loops=1) - Filter: ((a >= $1) AND ((a % 10) = 5)) - Rows Removed by Filter: 9 -(6 rows) - -execute mt_q1(25); - a ----- - 25 -(1 row) - --- Ensure MergeAppend behaves correctly when no subplans match -explain (analyze, costs off, summary off, timing off) execute mt_q1(35); - QUERY PLAN --------------------------------------- - Merge Append (actual rows=0 loops=1) - Sort Key: ma_test.b - Subplans Removed: 3 -(3 rows) - -execute mt_q1(35); - a ---- -(0 rows) - -deallocate mt_q1; -prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1; --- Ensure output list looks sane when the MergeAppend has no subplans. -explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35); - QUERY PLAN --------------------------------------------- - Limit (actual rows=0 loops=1) - Output: ma_test.a, ma_test.b - -> Merge Append (actual rows=0 loops=1) - Sort Key: ma_test.b - Subplans Removed: 3 -(5 rows) - -deallocate mt_q2; --- ensure initplan params properly prune partitions -explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b; - QUERY PLAN ------------------------------------------------------------------------------------------------ - Merge Append (actual rows=20 loops=1) - Sort Key: ma_test.b - InitPlan 2 (returns $1) - -> Result (actual rows=1 loops=1) - InitPlan 1 (returns $0) - -> Limit (actual rows=1 loops=1) - -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1) - Index Cond: (b IS NOT NULL) - -> Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed) - Filter: (a >= $1) - -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1) - Filter: (a >= $1) - -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1) - Filter: (a >= $1) -(14 rows) - -reset enable_seqscan; -reset enable_sort; -drop table ma_test; -reset enable_indexonlyscan; --- --- check that pruning works properly when the partition key is of a --- pseudotype --- --- array type list partition key -create table pp_arrpart (a int[]) partition by list (a); -create table pp_arrpart1 partition of pp_arrpart for values in ('{1}'); -create table pp_arrpart2 partition of pp_arrpart for values in ('{2, 3}', '{4, 5}'); -explain (costs off) select * from pp_arrpart where a = '{1}'; - QUERY PLAN ------------------------------------- - Seq Scan on pp_arrpart1 pp_arrpart - Filter: (a = '{1}'::integer[]) -(2 rows) - -explain (costs off) select * from pp_arrpart where a = '{1, 2}'; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from pp_arrpart where a in ('{4, 5}', '{1}'); - QUERY PLAN ----------------------------------------------------------------------- - Append - -> Seq Scan on pp_arrpart1 pp_arrpart_1 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - -> Seq Scan on pp_arrpart2 pp_arrpart_2 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) -(5 rows) - -explain (costs off) update pp_arrpart set a = a where a = '{1}'; - QUERY PLAN --------------------------------------------- - Update on pp_arrpart - Update on pp_arrpart1 pp_arrpart_1 - -> Seq Scan on pp_arrpart1 pp_arrpart_1 - Filter: (a = '{1}'::integer[]) -(4 rows) - -explain (costs off) delete from pp_arrpart where a = '{1}'; - QUERY PLAN --------------------------------------------- - Delete on pp_arrpart - Delete on pp_arrpart1 pp_arrpart_1 - -> Seq Scan on pp_arrpart1 pp_arrpart_1 - Filter: (a = '{1}'::integer[]) -(4 rows) - -drop table pp_arrpart; --- array type hash partition key -create table pph_arrpart (a int[]) partition by hash (a); -create table pph_arrpart1 partition of pph_arrpart for values with (modulus 2, remainder 0); -create table pph_arrpart2 partition of pph_arrpart for values with (modulus 2, remainder 1); -insert into pph_arrpart values ('{1}'), ('{1, 2}'), ('{4, 5}'); -select tableoid::regclass, * from pph_arrpart order by 1; - tableoid | a ---------------+------- - pph_arrpart1 | {1,2} - pph_arrpart1 | {4,5} - pph_arrpart2 | {1} -(3 rows) - -explain (costs off) select * from pph_arrpart where a = '{1}'; - QUERY PLAN --------------------------------------- - Seq Scan on pph_arrpart2 pph_arrpart - Filter: (a = '{1}'::integer[]) -(2 rows) - -explain (costs off) select * from pph_arrpart where a = '{1, 2}'; - QUERY PLAN --------------------------------------- - Seq Scan on pph_arrpart1 pph_arrpart - Filter: (a = '{1,2}'::integer[]) -(2 rows) - -explain (costs off) select * from pph_arrpart where a in ('{4, 5}', '{1}'); - QUERY PLAN ----------------------------------------------------------------------- - Append - -> Seq Scan on pph_arrpart1 pph_arrpart_1 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - -> Seq Scan on pph_arrpart2 pph_arrpart_2 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) -(5 rows) - -drop table pph_arrpart; --- enum type list partition key -create type pp_colors as enum ('green', 'blue', 'black'); -create table pp_enumpart (a pp_colors) partition by list (a); -create table pp_enumpart_green partition of pp_enumpart for values in ('green'); -create table pp_enumpart_blue partition of pp_enumpart for values in ('blue'); -explain (costs off) select * from pp_enumpart where a = 'blue'; - QUERY PLAN ------------------------------------------- - Seq Scan on pp_enumpart_blue pp_enumpart - Filter: (a = 'blue'::pp_colors) -(2 rows) - -explain (costs off) select * from pp_enumpart where a = 'black'; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table pp_enumpart; -drop type pp_colors; --- record type as partition key -create type pp_rectype as (a int, b int); -create table pp_recpart (a pp_rectype) partition by list (a); -create table pp_recpart_11 partition of pp_recpart for values in ('(1,1)'); -create table pp_recpart_23 partition of pp_recpart for values in ('(2,3)'); -explain (costs off) select * from pp_recpart where a = '(1,1)'::pp_rectype; - QUERY PLAN --------------------------------------- - Seq Scan on pp_recpart_11 pp_recpart - Filter: (a = '(1,1)'::pp_rectype) -(2 rows) - -explain (costs off) select * from pp_recpart where a = '(1,2)'::pp_rectype; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table pp_recpart; -drop type pp_rectype; --- range type partition key -create table pp_intrangepart (a int4range) partition by list (a); -create table pp_intrangepart12 partition of pp_intrangepart for values in ('[1,2]'); -create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2,)'); -explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range; - QUERY PLAN ------------------------------------------------ - Seq Scan on pp_intrangepart12 pp_intrangepart - Filter: (a = '[1,3)'::int4range) -(2 rows) - -explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table pp_intrangepart; --- --- Ensure the enable_partition_prune GUC properly disables partition pruning. --- -create table pp_lp (a int, value int) partition by list (a); -create table pp_lp1 partition of pp_lp for values in(1); -create table pp_lp2 partition of pp_lp for values in(2); -explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN --------------------------- - Seq Scan on pp_lp1 pp_lp - Filter: (a = 1) -(2 rows) - -explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN ----------------------------------- - Update on pp_lp - Update on pp_lp1 pp_lp_1 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) -(4 rows) - -explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN ----------------------------------- - Delete on pp_lp - Delete on pp_lp1 pp_lp_1 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) -(4 rows) - -set enable_partition_pruning = off; -set constraint_exclusion = 'partition'; -- this should not affect the result. -explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN ----------------------------------- - Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(5 rows) - -explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN ----------------------------------------- - Update on pp_lp - Update on pp_lp1 pp_lp_1 - Update on pp_lp2 pp_lp_2 - -> Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(8 rows) - -explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN ----------------------------------------- - Delete on pp_lp - Delete on pp_lp1 pp_lp_1 - Delete on pp_lp2 pp_lp_2 - -> Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(8 rows) - -set constraint_exclusion = 'off'; -- this should not affect the result. -explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN ----------------------------------- - Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(5 rows) - -explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN ----------------------------------------- - Update on pp_lp - Update on pp_lp1 pp_lp_1 - Update on pp_lp2 pp_lp_2 - -> Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(8 rows) - -explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN ----------------------------------------- - Delete on pp_lp - Delete on pp_lp1 pp_lp_1 - Delete on pp_lp2 pp_lp_2 - -> Append - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(8 rows) - -drop table pp_lp; --- Ensure enable_partition_prune does not affect non-partitioned tables. -create table inh_lp (a int, value int); -create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp); -NOTICE: merging column "a" with inherited definition -NOTICE: merging column "value" with inherited definition -create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp); -NOTICE: merging column "a" with inherited definition -NOTICE: merging column "value" with inherited definition -set constraint_exclusion = 'partition'; --- inh_lp2 should be removed in the following 3 cases. -explain (costs off) select * from inh_lp where a = 1; - QUERY PLAN ------------------------------------- - Append - -> Seq Scan on inh_lp inh_lp_1 - Filter: (a = 1) - -> Seq Scan on inh_lp1 inh_lp_2 - Filter: (a = 1) -(5 rows) - -explain (costs off) update inh_lp set value = 10 where a = 1; - QUERY PLAN ------------------------------------------------- - Update on inh_lp - Update on inh_lp inh_lp_1 - Update on inh_lp1 inh_lp_2 - -> Result - -> Append - -> Seq Scan on inh_lp inh_lp_1 - Filter: (a = 1) - -> Seq Scan on inh_lp1 inh_lp_2 - Filter: (a = 1) -(9 rows) - -explain (costs off) delete from inh_lp where a = 1; - QUERY PLAN ------------------------------------------- - Delete on inh_lp - Delete on inh_lp inh_lp_1 - Delete on inh_lp1 inh_lp_2 - -> Append - -> Seq Scan on inh_lp inh_lp_1 - Filter: (a = 1) - -> Seq Scan on inh_lp1 inh_lp_2 - Filter: (a = 1) -(8 rows) - --- Ensure we don't exclude normal relations when we only expect to exclude --- inheritance children -explain (costs off) update inh_lp1 set value = 10 where a = 2; - QUERY PLAN ---------------------------- - Update on inh_lp1 - -> Seq Scan on inh_lp1 - Filter: (a = 2) -(3 rows) - -drop table inh_lp cascade; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table inh_lp1 -drop cascades to table inh_lp2 -reset enable_partition_pruning; -reset constraint_exclusion; --- Check pruning for a partition tree containing only temporary relations -create temp table pp_temp_parent (a int) partition by list (a); -create temp table pp_temp_part_1 partition of pp_temp_parent for values in (1); -create temp table pp_temp_part_def partition of pp_temp_parent default; -explain (costs off) select * from pp_temp_parent where true; - QUERY PLAN ------------------------------------------------------ - Append - -> Seq Scan on pp_temp_part_1 pp_temp_parent_1 - -> Seq Scan on pp_temp_part_def pp_temp_parent_2 -(3 rows) - -explain (costs off) select * from pp_temp_parent where a = 2; - QUERY PLAN ---------------------------------------------- - Seq Scan on pp_temp_part_def pp_temp_parent - Filter: (a = 2) -(2 rows) - -drop table pp_temp_parent; --- Stress run-time partition pruning a bit more, per bug reports -create temp table p (a int, b int, c int) partition by list (a); -create temp table p1 partition of p for values in (1); -create temp table p2 partition of p for values in (2); -create temp table q (a int, b int, c int) partition by list (a); -create temp table q1 partition of q for values in (1) partition by list (b); -create temp table q11 partition of q1 for values in (1) partition by list (c); -create temp table q111 partition of q11 for values in (1); -create temp table q2 partition of q for values in (2) partition by list (b); -create temp table q21 partition of q2 for values in (1); -create temp table q22 partition of q2 for values in (2); -insert into q22 values (2, 2, 3); -explain (costs off) -select * -from ( - select * from p - union all - select * from q1 - union all - select 1, 1, 1 - ) s(a, b, c) -where s.a = 1 and s.b = 1 and s.c = (select 1); - QUERY PLAN ----------------------------------------------------- - Append - InitPlan 1 (returns $0) - -> Result - -> Seq Scan on p1 p - Filter: ((a = 1) AND (b = 1) AND (c = $0)) - -> Seq Scan on q111 q1 - Filter: ((a = 1) AND (b = 1) AND (c = $0)) - -> Result - One-Time Filter: (1 = $0) -(9 rows) - -select * -from ( - select * from p - union all - select * from q1 - union all - select 1, 1, 1 - ) s(a, b, c) -where s.a = 1 and s.b = 1 and s.c = (select 1); - a | b | c ----+---+--- - 1 | 1 | 1 -(1 row) - -prepare q (int, int) as -select * -from ( - select * from p - union all - select * from q1 - union all - select 1, 1, 1 - ) s(a, b, c) -where s.a = $1 and s.b = $2 and s.c = (select 1); -explain (costs off) execute q (1, 1); - QUERY PLAN ---------------------------------------------------------------- - Append - Subplans Removed: 1 - InitPlan 1 (returns $0) - -> Result - -> Seq Scan on p1 p - Filter: ((a = $1) AND (b = $2) AND (c = $0)) - -> Seq Scan on q111 q1 - Filter: ((a = $1) AND (b = $2) AND (c = $0)) - -> Result - One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) -(10 rows) - -execute q (1, 1); - a | b | c ----+---+--- - 1 | 1 | 1 -(1 row) - -drop table p, q; --- Ensure run-time pruning works correctly when we match a partitioned table --- on the first level but find no matching partitions on the second level. -create table listp (a int, b int) partition by list (a); -create table listp1 partition of listp for values in(1); -create table listp2 partition of listp for values in(2) partition by list(b); -create table listp2_10 partition of listp2 for values in (10); -explain (analyze, costs off, summary off, timing off) -select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- - Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) - InitPlan 1 (returns $0) - -> Result (never executed) -(4 rows) - --- --- check that a partition directly accessed in a query is excluded with --- constraint_exclusion = on --- --- turn off partition pruning, so that it doesn't interfere -set enable_partition_pruning to off; --- setting constraint_exclusion to 'partition' disables exclusion -set constraint_exclusion to 'partition'; -explain (costs off) select * from listp1 where a = 2; - QUERY PLAN --------------------- - Seq Scan on listp1 - Filter: (a = 2) -(2 rows) - -explain (costs off) update listp1 set a = 1 where a = 2; - QUERY PLAN --------------------------- - Update on listp1 - -> Seq Scan on listp1 - Filter: (a = 2) -(3 rows) - --- constraint exclusion enabled -set constraint_exclusion to 'on'; -explain (costs off) select * from listp1 where a = 2; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) update listp1 set a = 1 where a = 2; - QUERY PLAN --------------------------------- - Update on listp1 - -> Result - One-Time Filter: false -(3 rows) - -reset constraint_exclusion; -reset enable_partition_pruning; -drop table listp; --- Ensure run-time pruning works correctly for nested Append nodes -set parallel_setup_cost to 0; -set parallel_tuple_cost to 0; -create table listp (a int) partition by list(a); -create table listp_12 partition of listp for values in(1,2) partition by list(a); -create table listp_12_1 partition of listp_12 for values in(1); -create table listp_12_2 partition of listp_12 for values in(2); --- Force the 2nd subnode of the Append to be non-parallel. This results in --- a nested Append node because the mixed parallel / non-parallel paths cannot --- be pulled into the top-level Append. -alter table listp_12_1 set (parallel_workers = 0); --- Ensure that listp_12_2 is not scanned. (The nested Append is not seen in --- the plan as it's pulled in setref.c due to having just a single subnode). -select explain_parallel_append('select * from listp where a = (select 1);'); - explain_parallel_append ----------------------------------------------------------------------- - Gather (actual rows=N loops=N) - Workers Planned: 2 - Params Evaluated: $0 - Workers Launched: N - InitPlan 1 (returns $0) - -> Result (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) - -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) -(11 rows) - --- Like the above but throw some more complexity at the planner by adding --- a UNION ALL. We expect both sides of the union not to scan the --- non-required partitions. -select explain_parallel_append( -'select * from listp where a = (select 1) - union all -select * from listp where a = (select 2);'); - explain_parallel_append ------------------------------------------------------------------------------------ - Append (actual rows=N loops=N) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Params Evaluated: $0 - Workers Launched: N - InitPlan 1 (returns $0) - -> Result (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) - -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) - -> Gather (actual rows=N loops=N) - Workers Planned: 2 - Params Evaluated: $1 - Workers Launched: N - InitPlan 2 (returns $1) - -> Result (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_4 (never executed) - Filter: (a = $1) - -> Parallel Seq Scan on listp_12_2 listp_5 (actual rows=N loops=N) - Filter: (a = $1) -(23 rows) - -drop table listp; -reset parallel_tuple_cost; -reset parallel_setup_cost; --- Test case for run-time pruning with a nested Merge Append -set enable_sort to 0; -create table rangep (a int, b int) partition by range (a); -create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b); --- We need 3 sub-partitions. 1 to validate pruning worked and another two --- because a single remaining partition would be pulled up to the main Append. -create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1); -create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2); -create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3); -create table rangep_100_to_200 partition of rangep for values from (100) to (200); -create index on rangep (a); --- Ensure run-time pruning works on the nested Merge Append -explain (analyze on, costs off, timing off, summary off) -select * from rangep where b IN((select 1),(select 2)) order by a; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) - -> Result (actual rows=1 loops=1) - InitPlan 2 (returns $1) - -> Result (actual rows=1 loops=1) - -> Merge Append (actual rows=0 loops=1) - Sort Key: rangep_2.a - -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) - -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) - -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) - Filter: (b = ANY (ARRAY[$0, $1])) - -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) -(15 rows) - -reset enable_sort; -drop table rangep; --- --- Check that gen_prune_steps_from_opexps() works well for various cases of --- clauses for different partition keys --- -create table rp_prefix_test1 (a int, b varchar) partition by range(a, b); -create table rp_prefix_test1_p1 partition of rp_prefix_test1 for values from (1, 'a') to (1, 'b'); -create table rp_prefix_test1_p2 partition of rp_prefix_test1 for values from (2, 'a') to (2, 'b'); --- Don't call get_steps_using_prefix() with the last partition key b plus --- an empty prefix -explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a'; - QUERY PLAN --------------------------------------------------- - Seq Scan on rp_prefix_test1_p1 rp_prefix_test1 - Filter: ((a <= 1) AND ((b)::text = 'a'::text)) -(2 rows) - -create table rp_prefix_test2 (a int, b int, c int) partition by range(a, b, c); -create table rp_prefix_test2_p1 partition of rp_prefix_test2 for values from (1, 1, 0) to (1, 1, 10); -create table rp_prefix_test2_p2 partition of rp_prefix_test2 for values from (2, 2, 0) to (2, 2, 10); --- Don't call get_steps_using_prefix() with the last partition key c plus --- an invalid prefix (ie, b = 1) -explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c >= 0; - QUERY PLAN ------------------------------------------------- - Seq Scan on rp_prefix_test2_p1 rp_prefix_test2 - Filter: ((a <= 1) AND (c >= 0) AND (b = 1)) -(2 rows) - -create table rp_prefix_test3 (a int, b int, c int, d int) partition by range(a, b, c, d); -create table rp_prefix_test3_p1 partition of rp_prefix_test3 for values from (1, 1, 1, 0) to (1, 1, 1, 10); -create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2, 2, 2, 0) to (2, 2, 2, 10); --- Test that get_steps_using_prefix() handles a prefix that contains multiple --- clauses for the partition key b (ie, b >= 1 and b >= 2) -explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0; - QUERY PLAN --------------------------------------------------------------------------- - Seq Scan on rp_prefix_test3_p2 rp_prefix_test3 - Filter: ((a >= 1) AND (b >= 1) AND (b >= 2) AND (c >= 2) AND (d >= 0)) -(2 rows) - --- Test that get_steps_using_prefix() handles a prefix that contains multiple --- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests --- that the caller arranges clauses in that prefix in the required order) -explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0; - QUERY PLAN ------------------------------------------------------------------------- - Seq Scan on rp_prefix_test3_p2 rp_prefix_test3 - Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2)) -(2 rows) - -create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; - QUERY PLAN -------------------------------------------------------------- - Seq Scan on hp_prefix_test_p1 hp_prefix_test - Filter: ((b IS NULL) AND (a = 1) AND (c = 1) AND (d = 1)) -(2 rows) - -drop table rp_prefix_test1; -drop table rp_prefix_test2; -drop table rp_prefix_test3; -drop table hp_prefix_test; --- --- Check that gen_partprune_steps() detects self-contradiction from clauses --- regardless of the order of the clauses (Here we use a custom operator to --- prevent the equivclass.c machinery from reordering the clauses) --- -create operator === ( - leftarg = int4, - rightarg = int4, - procedure = int4eq, - commutator = ===, - hashes -); -create operator class part_test_int4_ops2 -for type int4 -using hash as -operator 1 ===, -function 2 part_hashint4_noop(int4, int8); -create table hp_contradict_test (a int, b int) partition by hash (a part_test_int4_ops2, b part_test_int4_ops2); -create table hp_contradict_test_p1 partition of hp_contradict_test for values with (modulus 2, remainder 0); -create table hp_contradict_test_p2 partition of hp_contradict_test for values with (modulus 2, remainder 1); -explain (costs off) select * from hp_contradict_test where a is null and a === 1 and b === 1; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 and a is null; - QUERY PLAN --------------------------- - Result - One-Time Filter: false -(2 rows) - -drop table hp_contradict_test; -drop operator class part_test_int4_ops2 using hash; -drop operator ===(int4, int4); +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/reloptions.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/reloptions.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/reloptions.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/reloptions.out 2021-10-30 22:09:26 -0600 @@ -1,226 +1,2 @@ --- Simple create -CREATE TABLE reloptions_test(i INT) WITH (FiLLFaCToR=30, - autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions ------------------------------------------------------------------------------- - {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2} -(1 row) - --- Fail min/max values check -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2); -ERROR: value 2 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110); -ERROR: value 110 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0); -ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor" -DETAIL: Valid values are between "0.000000" and "100.000000". -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0); -ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor" -DETAIL: Valid values are between "0.000000" and "100.000000". --- Fail when option and namespace do not exist -CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2); -ERROR: unrecognized parameter "not_existing_option" -CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2); -ERROR: unrecognized parameter namespace "not_existing_namespace" --- Fail while setting improper values -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=-30.1); -ERROR: value -30.1 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string'); -ERROR: invalid value for integer option "fillfactor": string -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true); -ERROR: invalid value for integer option "fillfactor": true -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12); -ERROR: invalid value for boolean option "autovacuum_enabled": 12 -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5); -ERROR: invalid value for boolean option "autovacuum_enabled": 30.5 -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string'); -ERROR: invalid value for boolean option "autovacuum_enabled": string -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string'); -ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string -CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true); -ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true --- Fail if option is specified twice -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30, fillfactor=40); -ERROR: parameter "fillfactor" specified more than once --- Specifying name only for a non-Boolean option should fail -CREATE TABLE reloptions_test2(i INT) WITH (fillfactor); -ERROR: invalid value for integer option "fillfactor": true --- Simple ALTER TABLE -ALTER TABLE reloptions_test SET (fillfactor=31, - autovacuum_analyze_scale_factor = 0.3); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions ------------------------------------------------------------------------------- - {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3} -(1 row) - --- Set boolean option to true without specifying value -ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions ------------------------------------------------------------------------------ - {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32} -(1 row) - --- Check that RESET works well -ALTER TABLE reloptions_test RESET (fillfactor); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions ---------------------------------------------------------------- - {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true} -(1 row) - --- Resetting all values causes the column to become null -ALTER TABLE reloptions_test RESET (autovacuum_enabled, - autovacuum_analyze_scale_factor); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND - reloptions IS NULL; - reloptions ------------- - -(1 row) - --- RESET fails if a value is specified -ALTER TABLE reloptions_test RESET (fillfactor=12); -ERROR: RESET must not include values for parameters --- Test vacuum_truncate option -DROP TABLE reloptions_test; -CREATE TABLE reloptions_test(i INT NOT NULL, j text) - WITH (vacuum_truncate=false, - toast.vacuum_truncate=false, - autovacuum_enabled=false); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions --------------------------------------------------- - {vacuum_truncate=false,autovacuum_enabled=false} -(1 row) - -INSERT INTO reloptions_test VALUES (1, NULL), (NULL, NULL); -ERROR: null value in column "i" of relation "reloptions_test" violates not-null constraint -DETAIL: Failing row contains (null, null). --- Do an aggressive vacuum to prevent page-skipping. -VACUUM FREEZE reloptions_test; -SELECT pg_relation_size('reloptions_test') > 0; - ?column? ----------- - t -(1 row) - -SELECT reloptions FROM pg_class WHERE oid = - (SELECT reltoastrelid FROM pg_class - WHERE oid = 'reloptions_test'::regclass); - reloptions -------------------------- - {vacuum_truncate=false} -(1 row) - -ALTER TABLE reloptions_test RESET (vacuum_truncate); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions ----------------------------- - {autovacuum_enabled=false} -(1 row) - -INSERT INTO reloptions_test VALUES (1, NULL), (NULL, NULL); -ERROR: null value in column "i" of relation "reloptions_test" violates not-null constraint -DETAIL: Failing row contains (null, null). --- Do an aggressive vacuum to prevent page-skipping. -VACUUM FREEZE reloptions_test; -SELECT pg_relation_size('reloptions_test') = 0; - ?column? ----------- - t -(1 row) - --- Test toast.* options -DROP TABLE reloptions_test; -CREATE TABLE reloptions_test (s VARCHAR) - WITH (toast.autovacuum_vacuum_cost_delay = 23); -SELECT reltoastrelid as toast_oid - FROM pg_class WHERE oid = 'reloptions_test'::regclass \gset -SELECT reloptions FROM pg_class WHERE oid = :toast_oid; - reloptions ------------------------------------ - {autovacuum_vacuum_cost_delay=23} -(1 row) - -ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24); -SELECT reloptions FROM pg_class WHERE oid = :toast_oid; - reloptions ------------------------------------ - {autovacuum_vacuum_cost_delay=24} -(1 row) - -ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay); -SELECT reloptions FROM pg_class WHERE oid = :toast_oid; - reloptions ------------- - -(1 row) - --- Fail on non-existent options in toast namespace -CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); -ERROR: unrecognized parameter "not_existing_option" --- Mix TOAST & heap -DROP TABLE reloptions_test; -CREATE TABLE reloptions_test (s VARCHAR) WITH - (toast.autovacuum_vacuum_cost_delay = 23, - autovacuum_vacuum_cost_delay = 24, fillfactor = 40); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; - reloptions -------------------------------------------------- - {autovacuum_vacuum_cost_delay=24,fillfactor=40} -(1 row) - -SELECT reloptions FROM pg_class WHERE oid = ( - SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass); - reloptions ------------------------------------ - {autovacuum_vacuum_cost_delay=23} -(1 row) - --- --- CREATE INDEX, ALTER INDEX for btrees --- -CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; - reloptions ------------------ - {fillfactor=30} -(1 row) - --- Fail when option and namespace do not exist -CREATE INDEX reloptions_test_idx ON reloptions_test (s) - WITH (not_existing_option=2); -ERROR: unrecognized parameter "not_existing_option" -CREATE INDEX reloptions_test_idx ON reloptions_test (s) - WITH (not_existing_ns.fillfactor=2); -ERROR: unrecognized parameter namespace "not_existing_ns" --- Check allowed ranges -CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1); -ERROR: value 1 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". -CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130); -ERROR: value 130 out of bounds for option "fillfactor" -DETAIL: Valid values are between "10" and "100". --- Check ALTER -ALTER INDEX reloptions_test_idx SET (fillfactor=40); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; - reloptions ------------------ - {fillfactor=40} -(1 row) - --- Check ALTER on empty reloption list -CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); -ALTER INDEX reloptions_test_idx3 SET (fillfactor=40); -SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass; - reloptions ------------------ - {fillfactor=40} -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/hash_part.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/hash_part.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/hash_part.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/hash_part.out 2021-10-30 22:09:26 -0600 @@ -1,114 +1,2 @@ --- --- Hash partitioning. --- --- Use hand-rolled hash functions and operator classes to get predictable --- result on different machines. See the definitions of --- part_part_test_int4_ops and part_test_text_ops in insert.sql. -CREATE TABLE mchash (a int, b text, c jsonb) - PARTITION BY HASH (a part_test_int4_ops, b part_test_text_ops); -CREATE TABLE mchash1 - PARTITION OF mchash FOR VALUES WITH (MODULUS 4, REMAINDER 0); --- invalid OID, no such table -SELECT satisfies_hash_partition(0, 4, 0, NULL); -ERROR: could not open relation with OID 0 --- not partitioned -SELECT satisfies_hash_partition('tenk1'::regclass, 4, 0, NULL); -ERROR: "tenk1" is not a hash partitioned table --- partition rather than the parent -SELECT satisfies_hash_partition('mchash1'::regclass, 4, 0, NULL); -ERROR: "mchash1" is not a hash partitioned table --- invalid modulus -SELECT satisfies_hash_partition('mchash'::regclass, 0, 0, NULL); -ERROR: modulus for hash partition must be an integer value greater than zero --- remainder too small -SELECT satisfies_hash_partition('mchash'::regclass, 1, -1, NULL); -ERROR: remainder for hash partition must be an integer value greater than or equal to zero --- remainder too large -SELECT satisfies_hash_partition('mchash'::regclass, 1, 1, NULL); -ERROR: remainder for hash partition must be less than modulus --- modulus is null -SELECT satisfies_hash_partition('mchash'::regclass, NULL, 0, NULL); - satisfies_hash_partition --------------------------- - f -(1 row) - --- remainder is null -SELECT satisfies_hash_partition('mchash'::regclass, 4, NULL, NULL); - satisfies_hash_partition --------------------------- - f -(1 row) - --- too many arguments -SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, NULL::int, NULL::text, NULL::json); -ERROR: number of partitioning columns (2) does not match number of partition keys provided (3) --- too few arguments -SELECT satisfies_hash_partition('mchash'::regclass, 3, 1, NULL::int); -ERROR: number of partitioning columns (2) does not match number of partition keys provided (1) --- wrong argument type -SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, NULL::int, NULL::int); -ERROR: column 2 of the partition key has type text, but supplied value is of type integer --- ok, should be false -SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 0, ''::text); - satisfies_hash_partition --------------------------- - f -(1 row) - --- ok, should be true -SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 2, ''::text); - satisfies_hash_partition --------------------------- - t -(1 row) - --- argument via variadic syntax, should fail because not all partitioning --- columns are of the correct type -SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, - variadic array[1,2]::int[]); -ERROR: column 2 of the partition key has type "text", but supplied value is of type "integer" --- multiple partitioning columns of the same type -CREATE TABLE mcinthash (a int, b int, c jsonb) - PARTITION BY HASH (a part_test_int4_ops, b part_test_int4_ops); --- now variadic should work, should be false -SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, - variadic array[0, 0]); - satisfies_hash_partition --------------------------- - f -(1 row) - --- should be true -SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, - variadic array[0, 1]); - satisfies_hash_partition --------------------------- - t -(1 row) - --- wrong length -SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, - variadic array[]::int[]); -ERROR: number of partitioning columns (2) does not match number of partition keys provided (0) --- wrong type -SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, - variadic array[now(), now()]); -ERROR: column 1 of the partition key has type "integer", but supplied value is of type "timestamp with time zone" --- check satisfies_hash_partition passes correct collation -create table text_hashp (a text) partition by hash (a); -create table text_hashp0 partition of text_hashp for values with (modulus 2, remainder 0); -create table text_hashp1 partition of text_hashp for values with (modulus 2, remainder 1); --- The result here should always be true, because 'xxx' must belong to --- one of the two defined partitions -select satisfies_hash_partition('text_hashp'::regclass, 2, 0, 'xxx'::text) OR - satisfies_hash_partition('text_hashp'::regclass, 2, 1, 'xxx'::text) AS satisfies; - satisfies ------------ - t -(1 row) - --- cleanup -DROP TABLE mchash; -DROP TABLE mcinthash; -DROP TABLE text_hashp; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/indexing.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/indexing.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/indexing.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/indexing.out 2021-10-30 22:09:26 -0600 @@ -1,1359 +1,2 @@ --- Creating an index on a partitioned table makes the partitions --- automatically get the index -create table idxpart (a int, b int, c text) partition by range (a); --- relhassubclass of a partitioned index is false before creating any partition. --- It will be set after the first partition is created. -create index idxpart_idx on idxpart (a); -select relhassubclass from pg_class where relname = 'idxpart_idx'; - relhassubclass ----------------- - f -(1 row) - --- Check that partitioned indexes are present in pg_indexes. -select indexdef from pg_indexes where indexname like 'idxpart_idx%'; - indexdef ------------------------------------------------------------------ - CREATE INDEX idxpart_idx ON ONLY public.idxpart USING btree (a) -(1 row) - -drop index idxpart_idx; -create table idxpart1 partition of idxpart for values from (0) to (10); -create table idxpart2 partition of idxpart for values from (10) to (100) - partition by range (b); -create table idxpart21 partition of idxpart2 for values from (0) to (100); --- Even with partitions, relhassubclass should not be set if a partitioned --- index is created only on the parent. -create index idxpart_idx on only idxpart(a); -select relhassubclass from pg_class where relname = 'idxpart_idx'; - relhassubclass ----------------- - f -(1 row) - -drop index idxpart_idx; -create index on idxpart (a); -select relname, relkind, relhassubclass, inhparent::regclass - from pg_class left join pg_index ix on (indexrelid = oid) - left join pg_inherits on (ix.indexrelid = inhrelid) - where relname like 'idxpart%' order by relname; - relname | relkind | relhassubclass | inhparent ------------------+---------+----------------+---------------- - idxpart | p | t | - idxpart1 | r | f | - idxpart1_a_idx | i | f | idxpart_a_idx - idxpart2 | p | t | - idxpart21 | r | f | - idxpart21_a_idx | i | f | idxpart2_a_idx - idxpart2_a_idx | I | t | idxpart_a_idx - idxpart_a_idx | I | t | -(8 rows) - -drop table idxpart; --- Some unsupported features -create table idxpart (a int, b int, c text) partition by range (a); -create table idxpart1 partition of idxpart for values from (0) to (10); -create index concurrently on idxpart (a); -ERROR: cannot create index on partitioned table "idxpart" concurrently -drop table idxpart; --- Verify bugfix with query on indexed partitioned table with no partitions --- https://postgr.es/m/20180124162006.pmapfiznhgngwtjf@alvherre.pgsql -CREATE TABLE idxpart (col1 INT) PARTITION BY RANGE (col1); -CREATE INDEX ON idxpart (col1); -CREATE TABLE idxpart_two (col2 INT); -SELECT col2 FROM idxpart_two fk LEFT OUTER JOIN idxpart pk ON (col1 = col2); - col2 ------- -(0 rows) - -DROP table idxpart, idxpart_two; --- Verify bugfix with index rewrite on ALTER TABLE / SET DATA TYPE --- https://postgr.es/m/CAKcux6mxNCGsgATwf5CGMF8g4WSupCXicCVMeKUTuWbyxHOMsQ@mail.gmail.com -CREATE TABLE idxpart (a INT, b TEXT, c INT) PARTITION BY RANGE(a); -CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (MINVALUE) TO (MAXVALUE); -CREATE INDEX partidx_abc_idx ON idxpart (a, b, c); -INSERT INTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i; -ALTER TABLE idxpart ALTER COLUMN c TYPE numeric; -DROP TABLE idxpart; --- If a table without index is attached as partition to a table with --- an index, the index is automatically created -create table idxpart (a int, b int, c text) partition by range (a); -create index idxparti on idxpart (a); -create index idxparti2 on idxpart (b, c); -create table idxpart1 (like idxpart); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | text | | | - -alter table idxpart attach partition idxpart1 for values from (0) to (10); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | text | | | -Partition of: idxpart FOR VALUES FROM (0) TO (10) -Indexes: - "idxpart1_a_idx" btree (a) - "idxpart1_b_c_idx" btree (b, c) - -\d+ idxpart1_a_idx - Index "public.idxpart1_a_idx" - Column | Type | Key? | Definition | Storage | Stats target ---------+---------+------+------------+---------+-------------- - a | integer | yes | a | plain | -Partition of: idxparti -No partition constraint -btree, for table "public.idxpart1" - -\d+ idxpart1_b_c_idx - Index "public.idxpart1_b_c_idx" - Column | Type | Key? | Definition | Storage | Stats target ---------+---------+------+------------+----------+-------------- - b | integer | yes | b | plain | - c | text | yes | c | extended | -Partition of: idxparti2 -No partition constraint -btree, for table "public.idxpart1" - --- Forbid ALTER TABLE when attaching or detaching an index to a partition. -create index idxpart_c on only idxpart (c); -create index idxpart1_c on idxpart1 (c); -alter table idxpart_c attach partition idxpart1_c for values from (10) to (20); -ERROR: "idxpart_c" is not a partitioned table -alter index idxpart_c attach partition idxpart1_c; -select relname, relpartbound from pg_class - where relname in ('idxpart_c', 'idxpart1_c') - order by relname; - relname | relpartbound -------------+-------------- - idxpart1_c | - idxpart_c | -(2 rows) - -alter table idxpart_c detach partition idxpart1_c; -ERROR: ALTER action DETACH PARTITION cannot be performed on relation "idxpart_c" -DETAIL: This operation is not supported for partitioned indexes. -drop table idxpart; --- If a partition already has an index, don't create a duplicative one -create table idxpart (a int, b int) partition by range (a, b); -create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10); -create index on idxpart1 (a, b); -create index on idxpart (a, b); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition of: idxpart FOR VALUES FROM (0, 0) TO (10, 10) -Indexes: - "idxpart1_a_b_idx" btree (a, b) - -select relname, relkind, relhassubclass, inhparent::regclass - from pg_class left join pg_index ix on (indexrelid = oid) - left join pg_inherits on (ix.indexrelid = inhrelid) - where relname like 'idxpart%' order by relname; - relname | relkind | relhassubclass | inhparent -------------------+---------+----------------+----------------- - idxpart | p | t | - idxpart1 | r | f | - idxpart1_a_b_idx | i | f | idxpart_a_b_idx - idxpart_a_b_idx | I | t | -(4 rows) - -drop table idxpart; --- DROP behavior for partitioned indexes -create table idxpart (a int) partition by range (a); -create index on idxpart (a); -create table idxpart1 partition of idxpart for values from (0) to (10); -drop index idxpart1_a_idx; -- no way -ERROR: cannot drop index idxpart1_a_idx because index idxpart_a_idx requires it -HINT: You can drop index idxpart_a_idx instead. -drop index concurrently idxpart_a_idx; -- unsupported -ERROR: cannot drop partitioned index "idxpart_a_idx" concurrently -drop index idxpart_a_idx; -- both indexes go away -select relname, relkind from pg_class - where relname like 'idxpart%' order by relname; - relname | relkind -----------+--------- - idxpart | p - idxpart1 | r -(2 rows) - -create index on idxpart (a); -drop table idxpart1; -- the index on partition goes away too -select relname, relkind from pg_class - where relname like 'idxpart%' order by relname; - relname | relkind ----------------+--------- - idxpart | p - idxpart_a_idx | I -(2 rows) - -drop table idxpart; --- DROP behavior with temporary partitioned indexes -create temp table idxpart_temp (a int) partition by range (a); -create index on idxpart_temp(a); -create temp table idxpart1_temp partition of idxpart_temp - for values from (0) to (10); -drop index idxpart1_temp_a_idx; -- error -ERROR: cannot drop index idxpart1_temp_a_idx because index idxpart_temp_a_idx requires it -HINT: You can drop index idxpart_temp_a_idx instead. --- non-concurrent drop is enforced here, so it is a valid case. -drop index concurrently idxpart_temp_a_idx; -select relname, relkind from pg_class - where relname like 'idxpart_temp%' order by relname; - relname | relkind ---------------+--------- - idxpart_temp | p -(1 row) - -drop table idxpart_temp; --- ALTER INDEX .. ATTACH, error cases -create table idxpart (a int, b int) partition by range (a, b); -create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10); -create index idxpart_a_b_idx on only idxpart (a, b); -create index idxpart1_a_b_idx on idxpart1 (a, b); -create index idxpart1_tst1 on idxpart1 (b, a); -create index idxpart1_tst2 on idxpart1 using hash (a); -create index idxpart1_tst3 on idxpart1 (a, b) where a > 10; -alter index idxpart attach partition idxpart1; -ERROR: "idxpart" is not an index -alter index idxpart_a_b_idx attach partition idxpart1; -ERROR: "idxpart1" is not an index -alter index idxpart_a_b_idx attach partition idxpart_a_b_idx; -ERROR: cannot attach index "idxpart_a_b_idx" as a partition of index "idxpart_a_b_idx" -DETAIL: Index "idxpart_a_b_idx" is not an index on any partition of table "idxpart". -alter index idxpart_a_b_idx attach partition idxpart1_b_idx; -ERROR: relation "idxpart1_b_idx" does not exist -alter index idxpart_a_b_idx attach partition idxpart1_tst1; -ERROR: cannot attach index "idxpart1_tst1" as a partition of index "idxpart_a_b_idx" -DETAIL: The index definitions do not match. -alter index idxpart_a_b_idx attach partition idxpart1_tst2; -ERROR: cannot attach index "idxpart1_tst2" as a partition of index "idxpart_a_b_idx" -DETAIL: The index definitions do not match. -alter index idxpart_a_b_idx attach partition idxpart1_tst3; -ERROR: cannot attach index "idxpart1_tst3" as a partition of index "idxpart_a_b_idx" -DETAIL: The index definitions do not match. --- OK -alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx; -alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx; -- quiet --- reject dupe -create index idxpart1_2_a_b on idxpart1 (a, b); -alter index idxpart_a_b_idx attach partition idxpart1_2_a_b; -ERROR: cannot attach index "idxpart1_2_a_b" as a partition of index "idxpart_a_b_idx" -DETAIL: Another index is already attached for partition "idxpart1". -drop table idxpart; --- make sure everything's gone -select indexrelid::regclass, indrelid::regclass - from pg_index where indexrelid::regclass::text like 'idxpart%'; - indexrelid | indrelid -------------+---------- -(0 rows) - --- Don't auto-attach incompatible indexes -create table idxpart (a int, b int) partition by range (a); -create table idxpart1 (a int, b int); -create index on idxpart1 using hash (a); -create index on idxpart1 (a) where b > 1; -create index on idxpart1 ((a + 0)); -create index on idxpart1 (a, a); -create index on idxpart (a); -alter table idxpart attach partition idxpart1 for values from (0) to (1000); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition of: idxpart FOR VALUES FROM (0) TO (1000) -Indexes: - "idxpart1_a_a1_idx" btree (a, a) - "idxpart1_a_idx" hash (a) - "idxpart1_a_idx1" btree (a) WHERE b > 1 - "idxpart1_a_idx2" btree (a) - "idxpart1_expr_idx" btree ((a + 0)) - -drop table idxpart; --- If CREATE INDEX ONLY, don't create indexes on partitions; and existing --- indexes on partitions don't change parent. ALTER INDEX ATTACH can change --- the parent after the fact. -create table idxpart (a int) partition by range (a); -create table idxpart1 partition of idxpart for values from (0) to (100); -create table idxpart2 partition of idxpart for values from (100) to (1000) - partition by range (a); -create table idxpart21 partition of idxpart2 for values from (100) to (200); -create table idxpart22 partition of idxpart2 for values from (200) to (300); -create index on idxpart22 (a); -create index on only idxpart2 (a); -create index on idxpart (a); --- Here we expect that idxpart1 and idxpart2 have a new index, but idxpart21 --- does not; also, idxpart22 is not attached. -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Partition of: idxpart FOR VALUES FROM (0) TO (100) -Indexes: - "idxpart1_a_idx" btree (a) - -\d idxpart2 - Partitioned table "public.idxpart2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Partition of: idxpart FOR VALUES FROM (100) TO (1000) -Partition key: RANGE (a) -Indexes: - "idxpart2_a_idx" btree (a) INVALID -Number of partitions: 2 (Use \d+ to list them.) - -\d idxpart21 - Table "public.idxpart21" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Partition of: idxpart2 FOR VALUES FROM (100) TO (200) - -select indexrelid::regclass, indrelid::regclass, inhparent::regclass - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) -where indexrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indexrelid | indrelid | inhparent ------------------+-----------+--------------- - idxpart1_a_idx | idxpart1 | idxpart_a_idx - idxpart22_a_idx | idxpart22 | - idxpart2_a_idx | idxpart2 | idxpart_a_idx - idxpart_a_idx | idxpart | -(4 rows) - -alter index idxpart2_a_idx attach partition idxpart22_a_idx; -select indexrelid::regclass, indrelid::regclass, inhparent::regclass - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) -where indexrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indexrelid | indrelid | inhparent ------------------+-----------+---------------- - idxpart1_a_idx | idxpart1 | idxpart_a_idx - idxpart22_a_idx | idxpart22 | idxpart2_a_idx - idxpart2_a_idx | idxpart2 | idxpart_a_idx - idxpart_a_idx | idxpart | -(4 rows) - --- attaching idxpart22 is not enough to set idxpart22_a_idx valid ... -alter index idxpart2_a_idx attach partition idxpart22_a_idx; -\d idxpart2 - Partitioned table "public.idxpart2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Partition of: idxpart FOR VALUES FROM (100) TO (1000) -Partition key: RANGE (a) -Indexes: - "idxpart2_a_idx" btree (a) INVALID -Number of partitions: 2 (Use \d+ to list them.) - --- ... but this one is. -create index on idxpart21 (a); -alter index idxpart2_a_idx attach partition idxpart21_a_idx; -\d idxpart2 - Partitioned table "public.idxpart2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | -Partition of: idxpart FOR VALUES FROM (100) TO (1000) -Partition key: RANGE (a) -Indexes: - "idxpart2_a_idx" btree (a) -Number of partitions: 2 (Use \d+ to list them.) - -drop table idxpart; --- When a table is attached a partition and it already has an index, a --- duplicate index should not get created, but rather the index becomes --- attached to the parent's index. -create table idxpart (a int, b int, c text) partition by range (a); -create index idxparti on idxpart (a); -create index idxparti2 on idxpart (b, c); -create table idxpart1 (like idxpart including indexes); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | text | | | -Indexes: - "idxpart1_a_idx" btree (a) - "idxpart1_b_c_idx" btree (b, c) - -select relname, relkind, inhparent::regclass - from pg_class left join pg_index ix on (indexrelid = oid) - left join pg_inherits on (ix.indexrelid = inhrelid) - where relname like 'idxpart%' order by relname; - relname | relkind | inhparent -------------------+---------+----------- - idxpart | p | - idxpart1 | r | - idxpart1_a_idx | i | - idxpart1_b_c_idx | i | - idxparti | I | - idxparti2 | I | -(6 rows) - -alter table idxpart attach partition idxpart1 for values from (0) to (10); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | text | | | -Partition of: idxpart FOR VALUES FROM (0) TO (10) -Indexes: - "idxpart1_a_idx" btree (a) - "idxpart1_b_c_idx" btree (b, c) - -select relname, relkind, inhparent::regclass - from pg_class left join pg_index ix on (indexrelid = oid) - left join pg_inherits on (ix.indexrelid = inhrelid) - where relname like 'idxpart%' order by relname; - relname | relkind | inhparent -------------------+---------+----------- - idxpart | p | - idxpart1 | r | - idxpart1_a_idx | i | idxparti - idxpart1_b_c_idx | i | idxparti2 - idxparti | I | - idxparti2 | I | -(6 rows) - -drop table idxpart; --- Verify that attaching an invalid index does not mark the parent index valid. --- On the other hand, attaching a valid index marks not only its direct --- ancestor valid, but also any indirect ancestor that was only missing the one --- that was just made valid -create table idxpart (a int, b int) partition by range (a); -create table idxpart1 partition of idxpart for values from (1) to (1000) partition by range (a); -create table idxpart11 partition of idxpart1 for values from (1) to (100); -create index on only idxpart1 (a); -create index on only idxpart (a); --- this results in two invalid indexes: -select relname, indisvalid from pg_class join pg_index on indexrelid = oid - where relname like 'idxpart%' order by relname; - relname | indisvalid -----------------+------------ - idxpart1_a_idx | f - idxpart_a_idx | f -(2 rows) - --- idxpart1_a_idx is not valid, so idxpart_a_idx should not become valid: -alter index idxpart_a_idx attach partition idxpart1_a_idx; -select relname, indisvalid from pg_class join pg_index on indexrelid = oid - where relname like 'idxpart%' order by relname; - relname | indisvalid -----------------+------------ - idxpart1_a_idx | f - idxpart_a_idx | f -(2 rows) - --- after creating and attaching this, both idxpart1_a_idx and idxpart_a_idx --- should become valid -create index on idxpart11 (a); -alter index idxpart1_a_idx attach partition idxpart11_a_idx; -select relname, indisvalid from pg_class join pg_index on indexrelid = oid - where relname like 'idxpart%' order by relname; - relname | indisvalid ------------------+------------ - idxpart11_a_idx | t - idxpart1_a_idx | t - idxpart_a_idx | t -(3 rows) - -drop table idxpart; --- verify dependency handling during ALTER TABLE DETACH PARTITION -create table idxpart (a int) partition by range (a); -create table idxpart1 (like idxpart); -create index on idxpart1 (a); -create index on idxpart (a); -create table idxpart2 (like idxpart); -alter table idxpart attach partition idxpart1 for values from (0000) to (1000); -alter table idxpart attach partition idxpart2 for values from (1000) to (2000); -create table idxpart3 partition of idxpart for values from (2000) to (3000); -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind -----------------+--------- - idxpart | p - idxpart1 | r - idxpart1_a_idx | i - idxpart2 | r - idxpart2_a_idx | i - idxpart3 | r - idxpart3_a_idx | i - idxpart_a_idx | I -(8 rows) - --- a) after detaching partitions, the indexes can be dropped independently -alter table idxpart detach partition idxpart1; -alter table idxpart detach partition idxpart2; -alter table idxpart detach partition idxpart3; -drop index idxpart1_a_idx; -drop index idxpart2_a_idx; -drop index idxpart3_a_idx; -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind ----------------+--------- - idxpart | p - idxpart1 | r - idxpart2 | r - idxpart3 | r - idxpart_a_idx | I -(5 rows) - -drop table idxpart, idxpart1, idxpart2, idxpart3; -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind ----------+--------- -(0 rows) - -create table idxpart (a int) partition by range (a); -create table idxpart1 (like idxpart); -create index on idxpart1 (a); -create index on idxpart (a); -create table idxpart2 (like idxpart); -alter table idxpart attach partition idxpart1 for values from (0000) to (1000); -alter table idxpart attach partition idxpart2 for values from (1000) to (2000); -create table idxpart3 partition of idxpart for values from (2000) to (3000); --- b) after detaching, dropping the index on parent does not remove the others -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind -----------------+--------- - idxpart | p - idxpart1 | r - idxpart1_a_idx | i - idxpart2 | r - idxpart2_a_idx | i - idxpart3 | r - idxpart3_a_idx | i - idxpart_a_idx | I -(8 rows) - -alter table idxpart detach partition idxpart1; -alter table idxpart detach partition idxpart2; -alter table idxpart detach partition idxpart3; -drop index idxpart_a_idx; -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind -----------------+--------- - idxpart | p - idxpart1 | r - idxpart1_a_idx | i - idxpart2 | r - idxpart2_a_idx | i - idxpart3 | r - idxpart3_a_idx | i -(7 rows) - -drop table idxpart, idxpart1, idxpart2, idxpart3; -select relname, relkind from pg_class where relname like 'idxpart%' order by relname; - relname | relkind ----------+--------- -(0 rows) - -create table idxpart (a int, b int, c int) partition by range(a); -create index on idxpart(c); -create table idxpart1 partition of idxpart for values from (0) to (250); -create table idxpart2 partition of idxpart for values from (250) to (500); -alter table idxpart detach partition idxpart2; -\d idxpart2 - Table "public.idxpart2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - c | integer | | | -Indexes: - "idxpart2_c_idx" btree (c) - -alter table idxpart2 drop column c; -\d idxpart2 - Table "public.idxpart2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | - -drop table idxpart, idxpart2; --- Verify that expression indexes inherit correctly -create table idxpart (a int, b int) partition by range (a); -create table idxpart1 (like idxpart); -create index on idxpart1 ((a + b)); -create index on idxpart ((a + b)); -create table idxpart2 (like idxpart); -alter table idxpart attach partition idxpart1 for values from (0000) to (1000); -alter table idxpart attach partition idxpart2 for values from (1000) to (2000); -create table idxpart3 partition of idxpart for values from (2000) to (3000); -select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef - from pg_class join pg_inherits on inhrelid = oid, - lateral pg_get_indexdef(pg_class.oid) - where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; - child | parent | childdef --------------------+------------------+--------------------------------------------------------------------------- - idxpart1_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((a + b))) - idxpart2_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((a + b))) - idxpart3_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart3_expr_idx ON public.idxpart3 USING btree (((a + b))) -(3 rows) - -drop table idxpart; --- Verify behavior for collation (mis)matches -create table idxpart (a text) partition by range (a); -create table idxpart1 (like idxpart); -create table idxpart2 (like idxpart); -create index on idxpart2 (a collate "POSIX"); -create index on idxpart2 (a); -create index on idxpart2 (a collate "C"); -alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb'); -alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc'); -create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd'); -create index on idxpart (a collate "C"); -create table idxpart4 partition of idxpart for values from ('ddd') to ('eee'); -select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef - from pg_class left join pg_inherits on inhrelid = oid, - lateral pg_get_indexdef(pg_class.oid) - where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; - child | parent | childdef ------------------+---------------+-------------------------------------------------------------------------------- - idxpart1_a_idx | idxpart_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a COLLATE "C") - idxpart2_a_idx | | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a COLLATE "POSIX") - idxpart2_a_idx1 | | CREATE INDEX idxpart2_a_idx1 ON public.idxpart2 USING btree (a) - idxpart2_a_idx2 | idxpart_a_idx | CREATE INDEX idxpart2_a_idx2 ON public.idxpart2 USING btree (a COLLATE "C") - idxpart3_a_idx | idxpart_a_idx | CREATE INDEX idxpart3_a_idx ON public.idxpart3 USING btree (a COLLATE "C") - idxpart4_a_idx | idxpart_a_idx | CREATE INDEX idxpart4_a_idx ON public.idxpart4 USING btree (a COLLATE "C") - idxpart_a_idx | | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a COLLATE "C") -(7 rows) - -drop table idxpart; --- Verify behavior for opclass (mis)matches -create table idxpart (a text) partition by range (a); -create table idxpart1 (like idxpart); -create table idxpart2 (like idxpart); -create index on idxpart2 (a); -alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb'); -alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc'); -create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd'); -create index on idxpart (a text_pattern_ops); -create table idxpart4 partition of idxpart for values from ('ddd') to ('eee'); --- must *not* have attached the index we created on idxpart2 -select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef - from pg_class left join pg_inherits on inhrelid = oid, - lateral pg_get_indexdef(pg_class.oid) - where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; - child | parent | childdef ------------------+---------------+------------------------------------------------------------------------------------ - idxpart1_a_idx | idxpart_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a text_pattern_ops) - idxpart2_a_idx | | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a) - idxpart2_a_idx1 | idxpart_a_idx | CREATE INDEX idxpart2_a_idx1 ON public.idxpart2 USING btree (a text_pattern_ops) - idxpart3_a_idx | idxpart_a_idx | CREATE INDEX idxpart3_a_idx ON public.idxpart3 USING btree (a text_pattern_ops) - idxpart4_a_idx | idxpart_a_idx | CREATE INDEX idxpart4_a_idx ON public.idxpart4 USING btree (a text_pattern_ops) - idxpart_a_idx | | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a text_pattern_ops) -(6 rows) - -drop index idxpart_a_idx; -create index on only idxpart (a text_pattern_ops); --- must reject -alter index idxpart_a_idx attach partition idxpart2_a_idx; -ERROR: cannot attach index "idxpart2_a_idx" as a partition of index "idxpart_a_idx" -DETAIL: The index definitions do not match. -drop table idxpart; --- Verify that attaching indexes maps attribute numbers correctly -create table idxpart (col1 int, a int, col2 int, b int) partition by range (a); -create table idxpart1 (b int, col1 int, col2 int, col3 int, a int); -alter table idxpart drop column col1, drop column col2; -alter table idxpart1 drop column col1, drop column col2, drop column col3; -alter table idxpart attach partition idxpart1 for values from (0) to (1000); -create index idxpart_1_idx on only idxpart (b, a); -create index idxpart1_1_idx on idxpart1 (b, a); -create index idxpart1_1b_idx on idxpart1 (b); --- test expressions and partial-index predicate, too -create index idxpart_2_idx on only idxpart ((b + a)) where a > 1; -create index idxpart1_2_idx on idxpart1 ((b + a)) where a > 1; -create index idxpart1_2b_idx on idxpart1 ((a + b)) where a > 1; -create index idxpart1_2c_idx on idxpart1 ((b + a)) where b > 1; -alter index idxpart_1_idx attach partition idxpart1_1b_idx; -- fail -ERROR: cannot attach index "idxpart1_1b_idx" as a partition of index "idxpart_1_idx" -DETAIL: The index definitions do not match. -alter index idxpart_1_idx attach partition idxpart1_1_idx; -alter index idxpart_2_idx attach partition idxpart1_2b_idx; -- fail -ERROR: cannot attach index "idxpart1_2b_idx" as a partition of index "idxpart_2_idx" -DETAIL: The index definitions do not match. -alter index idxpart_2_idx attach partition idxpart1_2c_idx; -- fail -ERROR: cannot attach index "idxpart1_2c_idx" as a partition of index "idxpart_2_idx" -DETAIL: The index definitions do not match. -alter index idxpart_2_idx attach partition idxpart1_2_idx; -- ok -select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef - from pg_class left join pg_inherits on inhrelid = oid, - lateral pg_get_indexdef(pg_class.oid) - where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; - child | parent | childdef ------------------+---------------+----------------------------------------------------------------------------------------- - idxpart1_1_idx | idxpart_1_idx | CREATE INDEX idxpart1_1_idx ON public.idxpart1 USING btree (b, a) - idxpart1_1b_idx | | CREATE INDEX idxpart1_1b_idx ON public.idxpart1 USING btree (b) - idxpart1_2_idx | idxpart_2_idx | CREATE INDEX idxpart1_2_idx ON public.idxpart1 USING btree (((b + a))) WHERE (a > 1) - idxpart1_2b_idx | | CREATE INDEX idxpart1_2b_idx ON public.idxpart1 USING btree (((a + b))) WHERE (a > 1) - idxpart1_2c_idx | | CREATE INDEX idxpart1_2c_idx ON public.idxpart1 USING btree (((b + a))) WHERE (b > 1) - idxpart_1_idx | | CREATE INDEX idxpart_1_idx ON ONLY public.idxpart USING btree (b, a) - idxpart_2_idx | | CREATE INDEX idxpart_2_idx ON ONLY public.idxpart USING btree (((b + a))) WHERE (a > 1) -(7 rows) - -drop table idxpart; --- Make sure the partition columns are mapped correctly -create table idxpart (a int, b int, c text) partition by range (a); -create index idxparti on idxpart (a); -create index idxparti2 on idxpart (c, b); -create table idxpart1 (c text, a int, b int); -alter table idxpart attach partition idxpart1 for values from (0) to (10); -create table idxpart2 (c text, a int, b int); -create index on idxpart2 (a); -create index on idxpart2 (c, b); -alter table idxpart attach partition idxpart2 for values from (10) to (20); -select c.relname, pg_get_indexdef(indexrelid) - from pg_class c join pg_index i on c.oid = i.indexrelid - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - relname | pg_get_indexdef -------------------+--------------------------------------------------------------------- - idxpart1_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a) - idxpart1_c_b_idx | CREATE INDEX idxpart1_c_b_idx ON public.idxpart1 USING btree (c, b) - idxpart2_a_idx | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a) - idxpart2_c_b_idx | CREATE INDEX idxpart2_c_b_idx ON public.idxpart2 USING btree (c, b) - idxparti | CREATE INDEX idxparti ON ONLY public.idxpart USING btree (a) - idxparti2 | CREATE INDEX idxparti2 ON ONLY public.idxpart USING btree (c, b) -(6 rows) - -drop table idxpart; --- Verify that columns are mapped correctly in expression indexes -create table idxpart (col1 int, col2 int, a int, b int) partition by range (a); -create table idxpart1 (col2 int, b int, col1 int, a int); -create table idxpart2 (col1 int, col2 int, b int, a int); -alter table idxpart drop column col1, drop column col2; -alter table idxpart1 drop column col1, drop column col2; -alter table idxpart2 drop column col1, drop column col2; -create index on idxpart2 (abs(b)); -alter table idxpart attach partition idxpart2 for values from (0) to (1); -create index on idxpart (abs(b)); -create index on idxpart ((b + 1)); -alter table idxpart attach partition idxpart1 for values from (1) to (2); -select c.relname, pg_get_indexdef(indexrelid) - from pg_class c join pg_index i on c.oid = i.indexrelid - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - relname | pg_get_indexdef --------------------+------------------------------------------------------------------------------ - idxpart1_abs_idx | CREATE INDEX idxpart1_abs_idx ON public.idxpart1 USING btree (abs(b)) - idxpart1_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((b + 1))) - idxpart2_abs_idx | CREATE INDEX idxpart2_abs_idx ON public.idxpart2 USING btree (abs(b)) - idxpart2_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((b + 1))) - idxpart_abs_idx | CREATE INDEX idxpart_abs_idx ON ONLY public.idxpart USING btree (abs(b)) - idxpart_expr_idx | CREATE INDEX idxpart_expr_idx ON ONLY public.idxpart USING btree (((b + 1))) -(6 rows) - -drop table idxpart; --- Verify that columns are mapped correctly for WHERE in a partial index -create table idxpart (col1 int, a int, col3 int, b int) partition by range (a); -alter table idxpart drop column col1, drop column col3; -create table idxpart1 (col1 int, col2 int, col3 int, col4 int, b int, a int); -alter table idxpart1 drop column col1, drop column col2, drop column col3, drop column col4; -alter table idxpart attach partition idxpart1 for values from (0) to (1000); -create table idxpart2 (col1 int, col2 int, b int, a int); -create index on idxpart2 (a) where b > 1000; -alter table idxpart2 drop column col1, drop column col2; -alter table idxpart attach partition idxpart2 for values from (1000) to (2000); -create index on idxpart (a) where b > 1000; -select c.relname, pg_get_indexdef(indexrelid) - from pg_class c join pg_index i on c.oid = i.indexrelid - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - relname | pg_get_indexdef -----------------+------------------------------------------------------------------------------------ - idxpart1_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a) WHERE (b > 1000) - idxpart2_a_idx | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a) WHERE (b > 1000) - idxpart_a_idx | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a) WHERE (b > 1000) -(3 rows) - -drop table idxpart; --- Column number mapping: dropped columns in the partition -create table idxpart1 (drop_1 int, drop_2 int, col_keep int, drop_3 int); -alter table idxpart1 drop column drop_1; -alter table idxpart1 drop column drop_2; -alter table idxpart1 drop column drop_3; -create index on idxpart1 (col_keep); -create table idxpart (col_keep int) partition by range (col_keep); -create index on idxpart (col_keep); -alter table idxpart attach partition idxpart1 for values from (0) to (1000); -\d idxpart - Partitioned table "public.idxpart" - Column | Type | Collation | Nullable | Default -----------+---------+-----------+----------+--------- - col_keep | integer | | | -Partition key: RANGE (col_keep) -Indexes: - "idxpart_col_keep_idx" btree (col_keep) -Number of partitions: 1 (Use \d+ to list them.) - -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default -----------+---------+-----------+----------+--------- - col_keep | integer | | | -Partition of: idxpart FOR VALUES FROM (0) TO (1000) -Indexes: - "idxpart1_col_keep_idx" btree (col_keep) - -select attrelid::regclass, attname, attnum from pg_attribute - where attrelid::regclass::text like 'idxpart%' and attnum > 0 - order by attrelid::regclass, attnum; - attrelid | attname | attnum ------------------------+------------------------------+-------- - idxpart1 | ........pg.dropped.1........ | 1 - idxpart1 | ........pg.dropped.2........ | 2 - idxpart1 | col_keep | 3 - idxpart1 | ........pg.dropped.4........ | 4 - idxpart1_col_keep_idx | col_keep | 1 - idxpart | col_keep | 1 - idxpart_col_keep_idx | col_keep | 1 -(7 rows) - -drop table idxpart; --- Column number mapping: dropped columns in the parent table -create table idxpart(drop_1 int, drop_2 int, col_keep int, drop_3 int) partition by range (col_keep); -alter table idxpart drop column drop_1; -alter table idxpart drop column drop_2; -alter table idxpart drop column drop_3; -create table idxpart1 (col_keep int); -create index on idxpart1 (col_keep); -create index on idxpart (col_keep); -alter table idxpart attach partition idxpart1 for values from (0) to (1000); -\d idxpart - Partitioned table "public.idxpart" - Column | Type | Collation | Nullable | Default -----------+---------+-----------+----------+--------- - col_keep | integer | | | -Partition key: RANGE (col_keep) -Indexes: - "idxpart_col_keep_idx" btree (col_keep) -Number of partitions: 1 (Use \d+ to list them.) - -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default -----------+---------+-----------+----------+--------- - col_keep | integer | | | -Partition of: idxpart FOR VALUES FROM (0) TO (1000) -Indexes: - "idxpart1_col_keep_idx" btree (col_keep) - -select attrelid::regclass, attname, attnum from pg_attribute - where attrelid::regclass::text like 'idxpart%' and attnum > 0 - order by attrelid::regclass, attnum; - attrelid | attname | attnum ------------------------+------------------------------+-------- - idxpart | ........pg.dropped.1........ | 1 - idxpart | ........pg.dropped.2........ | 2 - idxpart | col_keep | 3 - idxpart | ........pg.dropped.4........ | 4 - idxpart1 | col_keep | 1 - idxpart1_col_keep_idx | col_keep | 1 - idxpart_col_keep_idx | col_keep | 1 -(7 rows) - -drop table idxpart; --- --- Constraint-related indexes --- --- Verify that it works to add primary key / unique to partitioned tables -create table idxpart (a int primary key, b int) partition by range (a); -\d idxpart - Partitioned table "public.idxpart" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | - b | integer | | | -Partition key: RANGE (a) -Indexes: - "idxpart_pkey" PRIMARY KEY, btree (a) -Number of partitions: 0 - --- multiple primary key on child should fail -create table failpart partition of idxpart (b primary key) for values from (0) to (100); -ERROR: multiple primary keys for table "failpart" are not allowed -drop table idxpart; --- primary key on child is okay if there's no PK in the parent, though -create table idxpart (a int) partition by range (a); -create table idxpart1pk partition of idxpart (a primary key) for values from (0) to (100); -\d idxpart1pk - Table "public.idxpart1pk" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | -Partition of: idxpart FOR VALUES FROM (0) TO (100) -Indexes: - "idxpart1pk_pkey" PRIMARY KEY, btree (a) - -drop table idxpart; --- Failing to use the full partition key is not allowed -create table idxpart (a int unique, b int) partition by range (a, b); -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: UNIQUE constraint on table "idxpart" lacks column "b" which is part of the partition key. -create table idxpart (a int, b int unique) partition by range (a, b); -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: UNIQUE constraint on table "idxpart" lacks column "a" which is part of the partition key. -create table idxpart (a int primary key, b int) partition by range (b, a); -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "b" which is part of the partition key. -create table idxpart (a int, b int primary key) partition by range (b, a); -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "a" which is part of the partition key. --- OK if you use them in some other order -create table idxpart (a int, b int, c text, primary key (a, b, c)) partition by range (b, c, a); -drop table idxpart; --- not other types of index-based constraints -create table idxpart (a int, exclude (a with = )) partition by range (a); -ERROR: exclusion constraints are not supported on partitioned tables -LINE 1: create table idxpart (a int, exclude (a with = )) partition ... - ^ --- no expressions in partition key for PK/UNIQUE -create table idxpart (a int primary key, b int) partition by range ((b + a)); -ERROR: unsupported PRIMARY KEY constraint with partition key definition -DETAIL: PRIMARY KEY constraints cannot be used when partition keys include expressions. -create table idxpart (a int unique, b int) partition by range ((b + a)); -ERROR: unsupported UNIQUE constraint with partition key definition -DETAIL: UNIQUE constraints cannot be used when partition keys include expressions. --- use ALTER TABLE to add a primary key -create table idxpart (a int, b int, c text) partition by range (a, b); -alter table idxpart add primary key (a); -- not an incomplete one though -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "b" which is part of the partition key. -alter table idxpart add primary key (a, b); -- this works -\d idxpart - Partitioned table "public.idxpart" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | - b | integer | | not null | - c | text | | | -Partition key: RANGE (a, b) -Indexes: - "idxpart_pkey" PRIMARY KEY, btree (a, b) -Number of partitions: 0 - -create table idxpart1 partition of idxpart for values from (0, 0) to (1000, 1000); -\d idxpart1 - Table "public.idxpart1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | not null | - b | integer | | not null | - c | text | | | -Partition of: idxpart FOR VALUES FROM (0, 0) TO (1000, 1000) -Indexes: - "idxpart1_pkey" PRIMARY KEY, btree (a, b) - -drop table idxpart; --- use ALTER TABLE to add a unique constraint -create table idxpart (a int, b int) partition by range (a, b); -alter table idxpart add unique (a); -- not an incomplete one though -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: UNIQUE constraint on table "idxpart" lacks column "b" which is part of the partition key. -alter table idxpart add unique (b, a); -- this works -\d idxpart - Partitioned table "public.idxpart" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition key: RANGE (a, b) -Indexes: - "idxpart_b_a_key" UNIQUE CONSTRAINT, btree (b, a) -Number of partitions: 0 - -drop table idxpart; --- Exclusion constraints cannot be added -create table idxpart (a int, b int) partition by range (a); -alter table idxpart add exclude (a with =); -ERROR: exclusion constraints are not supported on partitioned tables -LINE 1: alter table idxpart add exclude (a with =); - ^ -drop table idxpart; --- When (sub)partitions are created, they also contain the constraint -create table idxpart (a int, b int, primary key (a, b)) partition by range (a, b); -create table idxpart1 partition of idxpart for values from (1, 1) to (10, 10); -create table idxpart2 partition of idxpart for values from (10, 10) to (20, 20) - partition by range (b); -create table idxpart21 partition of idxpart2 for values from (10) to (15); -create table idxpart22 partition of idxpart2 for values from (15) to (20); -create table idxpart3 (b int not null, a int not null); -alter table idxpart attach partition idxpart3 for values from (20, 20) to (30, 30); -select conname, contype, conrelid::regclass, conindid::regclass, conkey - from pg_constraint where conrelid::regclass::text like 'idxpart%' - order by conname; - conname | contype | conrelid | conindid | conkey -----------------+---------+-----------+----------------+-------- - idxpart1_pkey | p | idxpart1 | idxpart1_pkey | {1,2} - idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2} - idxpart22_pkey | p | idxpart22 | idxpart22_pkey | {1,2} - idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2} - idxpart3_pkey | p | idxpart3 | idxpart3_pkey | {2,1} - idxpart_pkey | p | idxpart | idxpart_pkey | {1,2} -(6 rows) - -drop table idxpart; --- Verify that multi-layer partitioning honors the requirement that all --- columns in the partition key must appear in primary/unique key -create table idxpart (a int, b int, primary key (a)) partition by range (a); -create table idxpart2 partition of idxpart -for values from (0) to (1000) partition by range (b); -- fail -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: PRIMARY KEY constraint on table "idxpart2" lacks column "b" which is part of the partition key. -drop table idxpart; --- Ditto for the ATTACH PARTITION case -create table idxpart (a int unique, b int) partition by range (a); -create table idxpart1 (a int not null, b int, unique (a, b)) - partition by range (a, b); -alter table idxpart attach partition idxpart1 for values from (1) to (1000); -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: UNIQUE constraint on table "idxpart1" lacks column "b" which is part of the partition key. -DROP TABLE idxpart, idxpart1; --- Multi-layer partitioning works correctly in this case: -create table idxpart (a int, b int, primary key (a, b)) partition by range (a); -create table idxpart2 partition of idxpart for values from (0) to (1000) partition by range (b); -create table idxpart21 partition of idxpart2 for values from (0) to (1000); -select conname, contype, conrelid::regclass, conindid::regclass, conkey - from pg_constraint where conrelid::regclass::text like 'idxpart%' - order by conname; - conname | contype | conrelid | conindid | conkey -----------------+---------+-----------+----------------+-------- - idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2} - idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2} - idxpart_pkey | p | idxpart | idxpart_pkey | {1,2} -(3 rows) - -drop table idxpart; --- If a partitioned table has a unique/PK constraint, then it's not possible --- to drop the corresponding constraint in the children; nor it's possible --- to drop the indexes individually. Dropping the constraint in the parent --- gets rid of the lot. -create table idxpart (i int) partition by hash (i); -create table idxpart0 partition of idxpart (i) for values with (modulus 2, remainder 0); -create table idxpart1 partition of idxpart (i) for values with (modulus 2, remainder 1); -alter table idxpart0 add primary key(i); -alter table idxpart add primary key(i); -select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, - conname, conislocal, coninhcount, connoinherit, convalidated - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) - left join pg_constraint con on (idx.indexrelid = con.conindid) - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated -----------+---------------+--------------+------------+---------------+------------+-------------+--------------+-------------- - idxpart0 | idxpart0_pkey | idxpart_pkey | t | idxpart0_pkey | f | 1 | t | t - idxpart1 | idxpart1_pkey | idxpart_pkey | t | idxpart1_pkey | f | 1 | f | t - idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t -(3 rows) - -drop index idxpart0_pkey; -- fail -ERROR: cannot drop index idxpart0_pkey because index idxpart_pkey requires it -HINT: You can drop index idxpart_pkey instead. -drop index idxpart1_pkey; -- fail -ERROR: cannot drop index idxpart1_pkey because index idxpart_pkey requires it -HINT: You can drop index idxpart_pkey instead. -alter table idxpart0 drop constraint idxpart0_pkey; -- fail -ERROR: cannot drop inherited constraint "idxpart0_pkey" of relation "idxpart0" -alter table idxpart1 drop constraint idxpart1_pkey; -- fail -ERROR: cannot drop inherited constraint "idxpart1_pkey" of relation "idxpart1" -alter table idxpart drop constraint idxpart_pkey; -- ok -select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, - conname, conislocal, coninhcount, connoinherit, convalidated - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) - left join pg_constraint con on (idx.indexrelid = con.conindid) - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated -----------+------------+-----------+------------+---------+------------+-------------+--------------+-------------- -(0 rows) - -drop table idxpart; --- If the partition to be attached already has a primary key, fail if --- it doesn't match the parent's PK. -CREATE TABLE idxpart (c1 INT PRIMARY KEY, c2 INT, c3 VARCHAR(10)) PARTITION BY RANGE(c1); -CREATE TABLE idxpart1 (LIKE idxpart); -ALTER TABLE idxpart1 ADD PRIMARY KEY (c1, c2); -ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (100) TO (200); -ERROR: multiple primary keys for table "idxpart1" are not allowed -DROP TABLE idxpart, idxpart1; --- Ditto if there is some distance between the PKs (subpartitioning) -create table idxpart (a int, b int, primary key (a)) partition by range (a); -create table idxpart1 (a int not null, b int) partition by range (a); -create table idxpart11 (a int not null, b int primary key); -alter table idxpart1 attach partition idxpart11 for values from (0) to (1000); -alter table idxpart attach partition idxpart1 for values from (0) to (10000); -ERROR: multiple primary keys for table "idxpart11" are not allowed -drop table idxpart, idxpart1, idxpart11; --- If a partitioned table has a constraint whose index is not valid, --- attaching a missing partition makes it valid. -create table idxpart (a int) partition by range (a); -create table idxpart0 (like idxpart); -alter table idxpart0 add primary key (a); -alter table idxpart attach partition idxpart0 for values from (0) to (1000); -alter table only idxpart add primary key (a); -select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, - conname, conislocal, coninhcount, connoinherit, convalidated - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) - left join pg_constraint con on (idx.indexrelid = con.conindid) - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated -----------+---------------+-----------+------------+---------------+------------+-------------+--------------+-------------- - idxpart0 | idxpart0_pkey | | t | idxpart0_pkey | t | 0 | t | t - idxpart | idxpart_pkey | | f | idxpart_pkey | t | 0 | t | t -(2 rows) - -alter index idxpart_pkey attach partition idxpart0_pkey; -select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, - conname, conislocal, coninhcount, connoinherit, convalidated - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) - left join pg_constraint con on (idx.indexrelid = con.conindid) - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated -----------+---------------+--------------+------------+---------------+------------+-------------+--------------+-------------- - idxpart0 | idxpart0_pkey | idxpart_pkey | t | idxpart0_pkey | f | 1 | t | t - idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t -(2 rows) - -drop table idxpart; --- Related to the above scenario: ADD PRIMARY KEY on the parent mustn't --- automatically propagate NOT NULL to child columns. -create table idxpart (a int) partition by range (a); -create table idxpart0 (like idxpart); -alter table idxpart0 add unique (a); -alter table idxpart attach partition idxpart0 default; -alter table only idxpart add primary key (a); -- fail, no NOT NULL constraint -ERROR: constraint must be added to child tables too -DETAIL: Column "a" of relation "idxpart0" is not already NOT NULL. -HINT: Do not specify the ONLY keyword. -alter table idxpart0 alter column a set not null; -alter table only idxpart add primary key (a); -- now it works -alter table idxpart0 alter column a drop not null; -- fail, pkey needs it -ERROR: column "a" is marked NOT NULL in parent table -drop table idxpart; --- if a partition has a unique index without a constraint, does not attach --- automatically; creates a new index instead. -create table idxpart (a int, b int) partition by range (a); -create table idxpart1 (a int not null, b int); -create unique index on idxpart1 (a); -alter table idxpart add primary key (a); -alter table idxpart attach partition idxpart1 for values from (1) to (1000); -select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, - conname, conislocal, coninhcount, connoinherit, convalidated - from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) - left join pg_constraint con on (idx.indexrelid = con.conindid) - where indrelid::regclass::text like 'idxpart%' - order by indexrelid::regclass::text collate "C"; - indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated -----------+----------------+--------------+------------+---------------+------------+-------------+--------------+-------------- - idxpart1 | idxpart1_a_idx | | t | | | | | - idxpart1 | idxpart1_pkey | idxpart_pkey | t | idxpart1_pkey | f | 1 | f | t - idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t -(3 rows) - -drop table idxpart; --- Can't attach an index without a corresponding constraint -create table idxpart (a int, b int) partition by range (a); -create table idxpart1 (a int not null, b int); -create unique index on idxpart1 (a); -alter table idxpart attach partition idxpart1 for values from (1) to (1000); -alter table only idxpart add primary key (a); -alter index idxpart_pkey attach partition idxpart1_a_idx; -- fail -ERROR: cannot attach index "idxpart1_a_idx" as a partition of index "idxpart_pkey" -DETAIL: The index "idxpart_pkey" belongs to a constraint in table "idxpart" but no constraint exists for index "idxpart1_a_idx". -drop table idxpart; --- Test that unique constraints are working -create table idxpart (a int, b text, primary key (a, b)) partition by range (a); -create table idxpart1 partition of idxpart for values from (0) to (100000); -create table idxpart2 (c int, like idxpart); -insert into idxpart2 (c, a, b) values (42, 572814, 'inserted first'); -alter table idxpart2 drop column c; -create unique index on idxpart (a); -alter table idxpart attach partition idxpart2 for values from (100000) to (1000000); -insert into idxpart values (0, 'zero'), (42, 'life'), (2^16, 'sixteen'); -insert into idxpart select 2^g, format('two to power of %s', g) from generate_series(15, 17) g; -ERROR: duplicate key value violates unique constraint "idxpart1_a_idx" -DETAIL: Key (a)=(65536) already exists. -insert into idxpart values (16, 'sixteen'); -insert into idxpart (b, a) values ('one', 142857), ('two', 285714); -insert into idxpart select a * 2, b || b from idxpart where a between 2^16 and 2^19; -ERROR: duplicate key value violates unique constraint "idxpart2_a_idx" -DETAIL: Key (a)=(285714) already exists. -insert into idxpart values (572814, 'five'); -ERROR: duplicate key value violates unique constraint "idxpart2_a_idx" -DETAIL: Key (a)=(572814) already exists. -insert into idxpart values (857142, 'six'); -select tableoid::regclass, * from idxpart order by a; - tableoid | a | b -----------+--------+---------------- - idxpart1 | 0 | zero - idxpart1 | 16 | sixteen - idxpart1 | 42 | life - idxpart1 | 65536 | sixteen - idxpart2 | 142857 | one - idxpart2 | 285714 | two - idxpart2 | 572814 | inserted first - idxpart2 | 857142 | six -(8 rows) - -drop table idxpart; --- intentionally leave some objects around -create table idxpart (a int) partition by range (a); -create table idxpart1 partition of idxpart for values from (0) to (100); -create table idxpart2 partition of idxpart for values from (100) to (1000) - partition by range (a); -create table idxpart21 partition of idxpart2 for values from (100) to (200); -create table idxpart22 partition of idxpart2 for values from (200) to (300); -create index on idxpart22 (a); -create index on only idxpart2 (a); -alter index idxpart2_a_idx attach partition idxpart22_a_idx; -create index on idxpart (a); -create table idxpart_another (a int, b int, primary key (a, b)) partition by range (a); -create table idxpart_another_1 partition of idxpart_another for values from (0) to (100); -create table idxpart3 (c int, b int, a int) partition by range (a); -alter table idxpart3 drop column b, drop column c; -create table idxpart31 partition of idxpart3 for values from (1000) to (1200); -create table idxpart32 partition of idxpart3 for values from (1200) to (1400); -alter table idxpart attach partition idxpart3 for values from (1000) to (2000); --- More objects intentionally left behind, to verify some pg_dump/pg_upgrade --- behavior; see https://postgr.es/m/20190321204928.GA17535@alvherre.pgsql -create schema regress_indexing; -set search_path to regress_indexing; -create table pk (a int primary key) partition by range (a); -create table pk1 partition of pk for values from (0) to (1000); -create table pk2 (b int, a int); -alter table pk2 drop column b; -alter table pk2 alter a set not null; -alter table pk attach partition pk2 for values from (1000) to (2000); -create table pk3 partition of pk for values from (2000) to (3000); -create table pk4 (like pk); -alter table pk attach partition pk4 for values from (3000) to (4000); -create table pk5 (like pk) partition by range (a); -create table pk51 partition of pk5 for values from (4000) to (4500); -create table pk52 partition of pk5 for values from (4500) to (5000); -alter table pk attach partition pk5 for values from (4000) to (5000); -reset search_path; --- Test that covering partitioned indexes work in various cases -create table covidxpart (a int, b int) partition by list (a); -create unique index on covidxpart (a) include (b); -create table covidxpart1 partition of covidxpart for values in (1); -create table covidxpart2 partition of covidxpart for values in (2); -insert into covidxpart values (1, 1); -insert into covidxpart values (1, 1); -ERROR: duplicate key value violates unique constraint "covidxpart1_a_b_idx" -DETAIL: Key (a)=(1) already exists. -create table covidxpart3 (b int, c int, a int); -alter table covidxpart3 drop c; -alter table covidxpart attach partition covidxpart3 for values in (3); -insert into covidxpart values (3, 1); -insert into covidxpart values (3, 1); -ERROR: duplicate key value violates unique constraint "covidxpart3_a_b_idx" -DETAIL: Key (a)=(3) already exists. -create table covidxpart4 (b int, a int); -create unique index on covidxpart4 (a) include (b); -create unique index on covidxpart4 (a); -alter table covidxpart attach partition covidxpart4 for values in (4); -insert into covidxpart values (4, 1); -insert into covidxpart values (4, 1); -ERROR: duplicate key value violates unique constraint "covidxpart4_a_b_idx" -DETAIL: Key (a)=(4) already exists. -create unique index on covidxpart (b) include (a); -- should fail -ERROR: unique constraint on partitioned table must include all partitioning columns -DETAIL: UNIQUE constraint on table "covidxpart" lacks column "a" which is part of the partition key. --- check that detaching a partition also detaches the primary key constraint -create table parted_pk_detach_test (a int primary key) partition by list (a); -create table parted_pk_detach_test1 partition of parted_pk_detach_test for values in (1); -alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey; -- should fail -ERROR: cannot drop inherited constraint "parted_pk_detach_test1_pkey" of relation "parted_pk_detach_test1" -alter table parted_pk_detach_test detach partition parted_pk_detach_test1; -alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey; -drop table parted_pk_detach_test, parted_pk_detach_test1; -create table parted_uniq_detach_test (a int unique) partition by list (a); -create table parted_uniq_detach_test1 partition of parted_uniq_detach_test for values in (1); -alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key; -- should fail -ERROR: cannot drop inherited constraint "parted_uniq_detach_test1_a_key" of relation "parted_uniq_detach_test1" -alter table parted_uniq_detach_test detach partition parted_uniq_detach_test1; -alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key; -drop table parted_uniq_detach_test, parted_uniq_detach_test1; --- check that dropping a column takes with it any partitioned indexes --- depending on it. -create table parted_index_col_drop(a int, b int, c int) - partition by list (a); -create table parted_index_col_drop1 partition of parted_index_col_drop - for values in (1) partition by list (a); --- leave this partition without children. -create table parted_index_col_drop2 partition of parted_index_col_drop - for values in (2) partition by list (a); -create table parted_index_col_drop11 partition of parted_index_col_drop1 - for values in (1); -create index on parted_index_col_drop (b); -create index on parted_index_col_drop (c); -create index on parted_index_col_drop (b, c); -alter table parted_index_col_drop drop column c; -\d parted_index_col_drop - Partitioned table "public.parted_index_col_drop" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition key: LIST (a) -Indexes: - "parted_index_col_drop_b_idx" btree (b) -Number of partitions: 2 (Use \d+ to list them.) - -\d parted_index_col_drop1 - Partitioned table "public.parted_index_col_drop1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition of: parted_index_col_drop FOR VALUES IN (1) -Partition key: LIST (a) -Indexes: - "parted_index_col_drop1_b_idx" btree (b) -Number of partitions: 1 (Use \d+ to list them.) - -\d parted_index_col_drop2 - Partitioned table "public.parted_index_col_drop2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition of: parted_index_col_drop FOR VALUES IN (2) -Partition key: LIST (a) -Indexes: - "parted_index_col_drop2_b_idx" btree (b) -Number of partitions: 0 - -\d parted_index_col_drop11 - Table "public.parted_index_col_drop11" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - b | integer | | | -Partition of: parted_index_col_drop1 FOR VALUES IN (1) -Indexes: - "parted_index_col_drop11_b_idx" btree (b) - -drop table parted_index_col_drop; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_aggregate.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_aggregate.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_aggregate.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_aggregate.out 2021-10-30 22:09:26 -0600 @@ -1,1519 +1,2 @@ --- --- PARTITION_AGGREGATE --- Test partitionwise aggregation on partitioned tables --- --- Note: to ensure plan stability, it's a good idea to make the partitions of --- any one partitioned table in this test all have different numbers of rows. --- --- Enable partitionwise aggregate, which by default is disabled. -SET enable_partitionwise_aggregate TO true; --- Enable partitionwise join, which by default is disabled. -SET enable_partitionwise_join TO true; --- Disable parallel plans. -SET max_parallel_workers_per_gather TO 0; --- Disable incremental sort, which can influence selected plans due to fuzz factor. -SET enable_incremental_sort TO off; --- --- Tests for list partitioned tables. --- -CREATE TABLE pagg_tab (a int, b int, c text, d int) PARTITION BY LIST(c); -CREATE TABLE pagg_tab_p1 PARTITION OF pagg_tab FOR VALUES IN ('0000', '0001', '0002', '0003', '0004'); -CREATE TABLE pagg_tab_p2 PARTITION OF pagg_tab FOR VALUES IN ('0005', '0006', '0007', '0008'); -CREATE TABLE pagg_tab_p3 PARTITION OF pagg_tab FOR VALUES IN ('0009', '0010', '0011'); -INSERT INTO pagg_tab SELECT i % 20, i % 30, to_char(i % 12, 'FM0000'), i % 30 FROM generate_series(0, 2999) i; -ANALYZE pagg_tab; --- When GROUP BY clause matches; full aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: pagg_tab.c, (sum(pagg_tab.a)), (avg(pagg_tab.b)) - -> Append - -> HashAggregate - Group Key: pagg_tab.c - Filter: (avg(pagg_tab.d) < '15'::numeric) - -> Seq Scan on pagg_tab_p1 pagg_tab - -> HashAggregate - Group Key: pagg_tab_1.c - Filter: (avg(pagg_tab_1.d) < '15'::numeric) - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> HashAggregate - Group Key: pagg_tab_2.c - Filter: (avg(pagg_tab_2.d) < '15'::numeric) - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(15 rows) - -SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; - c | sum | avg | count | min | max -------+------+---------------------+-------+-----+----- - 0000 | 2000 | 12.0000000000000000 | 250 | 0 | 24 - 0001 | 2250 | 13.0000000000000000 | 250 | 1 | 25 - 0002 | 2500 | 14.0000000000000000 | 250 | 2 | 26 - 0006 | 2500 | 12.0000000000000000 | 250 | 2 | 24 - 0007 | 2750 | 13.0000000000000000 | 250 | 3 | 25 - 0008 | 2000 | 14.0000000000000000 | 250 | 0 | 26 -(6 rows) - --- When GROUP BY clause does not match; partial aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: pagg_tab.a, (sum(pagg_tab.b)), (avg(pagg_tab.b)) - -> Finalize HashAggregate - Group Key: pagg_tab.a - Filter: (avg(pagg_tab.d) < '15'::numeric) - -> Append - -> Partial HashAggregate - Group Key: pagg_tab.a - -> Seq Scan on pagg_tab_p1 pagg_tab - -> Partial HashAggregate - Group Key: pagg_tab_1.a - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> Partial HashAggregate - Group Key: pagg_tab_2.a - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(15 rows) - -SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; - a | sum | avg | count | min | max -----+------+---------------------+-------+-----+----- - 0 | 1500 | 10.0000000000000000 | 150 | 0 | 20 - 1 | 1650 | 11.0000000000000000 | 150 | 1 | 21 - 2 | 1800 | 12.0000000000000000 | 150 | 2 | 22 - 3 | 1950 | 13.0000000000000000 | 150 | 3 | 23 - 4 | 2100 | 14.0000000000000000 | 150 | 4 | 24 - 10 | 1500 | 10.0000000000000000 | 150 | 10 | 20 - 11 | 1650 | 11.0000000000000000 | 150 | 11 | 21 - 12 | 1800 | 12.0000000000000000 | 150 | 12 | 22 - 13 | 1950 | 13.0000000000000000 | 150 | 13 | 23 - 14 | 2100 | 14.0000000000000000 | 150 | 14 | 24 -(10 rows) - --- Check with multiple columns in GROUP BY -EXPLAIN (COSTS OFF) -SELECT a, c, count(*) FROM pagg_tab GROUP BY a, c; - QUERY PLAN ------------------------------------------------- - Append - -> HashAggregate - Group Key: pagg_tab.a, pagg_tab.c - -> Seq Scan on pagg_tab_p1 pagg_tab - -> HashAggregate - Group Key: pagg_tab_1.a, pagg_tab_1.c - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> HashAggregate - Group Key: pagg_tab_2.a, pagg_tab_2.c - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(10 rows) - --- Check with multiple columns in GROUP BY, order in GROUP BY is reversed -EXPLAIN (COSTS OFF) -SELECT a, c, count(*) FROM pagg_tab GROUP BY c, a; - QUERY PLAN ------------------------------------------------- - Append - -> HashAggregate - Group Key: pagg_tab.c, pagg_tab.a - -> Seq Scan on pagg_tab_p1 pagg_tab - -> HashAggregate - Group Key: pagg_tab_1.c, pagg_tab_1.a - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> HashAggregate - Group Key: pagg_tab_2.c, pagg_tab_2.a - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(10 rows) - --- Check with multiple columns in GROUP BY, order in target-list is reversed -EXPLAIN (COSTS OFF) -SELECT c, a, count(*) FROM pagg_tab GROUP BY a, c; - QUERY PLAN ------------------------------------------------- - Append - -> HashAggregate - Group Key: pagg_tab.a, pagg_tab.c - -> Seq Scan on pagg_tab_p1 pagg_tab - -> HashAggregate - Group Key: pagg_tab_1.a, pagg_tab_1.c - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> HashAggregate - Group Key: pagg_tab_2.a, pagg_tab_2.c - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(10 rows) - --- Test when input relation for grouping is dummy -EXPLAIN (COSTS OFF) -SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; - QUERY PLAN --------------------------------- - HashAggregate - Group Key: c - -> Result - One-Time Filter: false -(4 rows) - -SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; - c | sum ----+----- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; - QUERY PLAN --------------------------------- - GroupAggregate - Group Key: c - -> Result - One-Time Filter: false -(4 rows) - -SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; - c | sum ----+----- -(0 rows) - --- Test GroupAggregate paths by disabling hash aggregates. -SET enable_hashagg TO false; --- When GROUP BY clause matches full aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: pagg_tab.c, (sum(pagg_tab.a)), (avg(pagg_tab.b)) - -> Append - -> GroupAggregate - Group Key: pagg_tab.c - Filter: (avg(pagg_tab.d) < '15'::numeric) - -> Sort - Sort Key: pagg_tab.c - -> Seq Scan on pagg_tab_p1 pagg_tab - -> GroupAggregate - Group Key: pagg_tab_1.c - Filter: (avg(pagg_tab_1.d) < '15'::numeric) - -> Sort - Sort Key: pagg_tab_1.c - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> GroupAggregate - Group Key: pagg_tab_2.c - Filter: (avg(pagg_tab_2.d) < '15'::numeric) - -> Sort - Sort Key: pagg_tab_2.c - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(21 rows) - -SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; - c | sum | avg | count -------+------+---------------------+------- - 0000 | 2000 | 12.0000000000000000 | 250 - 0001 | 2250 | 13.0000000000000000 | 250 - 0002 | 2500 | 14.0000000000000000 | 250 - 0006 | 2500 | 12.0000000000000000 | 250 - 0007 | 2750 | 13.0000000000000000 | 250 - 0008 | 2000 | 14.0000000000000000 | 250 -(6 rows) - --- When GROUP BY clause does not match; partial aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: pagg_tab.a, (sum(pagg_tab.b)), (avg(pagg_tab.b)) - -> Finalize GroupAggregate - Group Key: pagg_tab.a - Filter: (avg(pagg_tab.d) < '15'::numeric) - -> Merge Append - Sort Key: pagg_tab.a - -> Partial GroupAggregate - Group Key: pagg_tab.a - -> Sort - Sort Key: pagg_tab.a - -> Seq Scan on pagg_tab_p1 pagg_tab - -> Partial GroupAggregate - Group Key: pagg_tab_1.a - -> Sort - Sort Key: pagg_tab_1.a - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> Partial GroupAggregate - Group Key: pagg_tab_2.a - -> Sort - Sort Key: pagg_tab_2.a - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(22 rows) - -SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; - a | sum | avg | count -----+------+---------------------+------- - 0 | 1500 | 10.0000000000000000 | 150 - 1 | 1650 | 11.0000000000000000 | 150 - 2 | 1800 | 12.0000000000000000 | 150 - 3 | 1950 | 13.0000000000000000 | 150 - 4 | 2100 | 14.0000000000000000 | 150 - 10 | 1500 | 10.0000000000000000 | 150 - 11 | 1650 | 11.0000000000000000 | 150 - 12 | 1800 | 12.0000000000000000 | 150 - 13 | 1950 | 13.0000000000000000 | 150 - 14 | 2100 | 14.0000000000000000 | 150 -(10 rows) - --- Test partitionwise grouping without any aggregates -EXPLAIN (COSTS OFF) -SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; - QUERY PLAN ------------------------------------------------------- - Merge Append - Sort Key: pagg_tab.c - -> Group - Group Key: pagg_tab.c - -> Sort - Sort Key: pagg_tab.c - -> Seq Scan on pagg_tab_p1 pagg_tab - -> Group - Group Key: pagg_tab_1.c - -> Sort - Sort Key: pagg_tab_1.c - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> Group - Group Key: pagg_tab_2.c - -> Sort - Sort Key: pagg_tab_2.c - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(17 rows) - -SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; - c ------- - 0000 - 0001 - 0002 - 0003 - 0004 - 0005 - 0006 - 0007 - 0008 - 0009 - 0010 - 0011 -(12 rows) - -EXPLAIN (COSTS OFF) -SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; - QUERY PLAN ------------------------------------------------------------- - Group - Group Key: pagg_tab.a - -> Merge Append - Sort Key: pagg_tab.a - -> Group - Group Key: pagg_tab.a - -> Sort - Sort Key: pagg_tab.a - -> Seq Scan on pagg_tab_p1 pagg_tab - Filter: (a < 3) - -> Group - Group Key: pagg_tab_1.a - -> Sort - Sort Key: pagg_tab_1.a - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - Filter: (a < 3) - -> Group - Group Key: pagg_tab_2.a - -> Sort - Sort Key: pagg_tab_2.a - -> Seq Scan on pagg_tab_p3 pagg_tab_2 - Filter: (a < 3) -(22 rows) - -SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; - a ---- - 0 - 1 - 2 -(3 rows) - -RESET enable_hashagg; --- ROLLUP, partitionwise aggregation does not apply -EXPLAIN (COSTS OFF) -SELECT c, sum(a) FROM pagg_tab GROUP BY rollup(c) ORDER BY 1, 2; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: pagg_tab.c, (sum(pagg_tab.a)) - -> MixedAggregate - Hash Key: pagg_tab.c - Group Key: () - -> Append - -> Seq Scan on pagg_tab_p1 pagg_tab_1 - -> Seq Scan on pagg_tab_p2 pagg_tab_2 - -> Seq Scan on pagg_tab_p3 pagg_tab_3 -(9 rows) - --- ORDERED SET within the aggregate. --- Full aggregation; since all the rows that belong to the same group come --- from the same partition, having an ORDER BY within the aggregate doesn't --- make any difference. -EXPLAIN (COSTS OFF) -SELECT c, sum(b order by a) FROM pagg_tab GROUP BY c ORDER BY 1, 2; - QUERY PLAN ---------------------------------------------------------------- - Sort - Sort Key: pagg_tab.c, (sum(pagg_tab.b ORDER BY pagg_tab.a)) - -> Append - -> GroupAggregate - Group Key: pagg_tab.c - -> Sort - Sort Key: pagg_tab.c - -> Seq Scan on pagg_tab_p1 pagg_tab - -> GroupAggregate - Group Key: pagg_tab_1.c - -> Sort - Sort Key: pagg_tab_1.c - -> Seq Scan on pagg_tab_p2 pagg_tab_1 - -> GroupAggregate - Group Key: pagg_tab_2.c - -> Sort - Sort Key: pagg_tab_2.c - -> Seq Scan on pagg_tab_p3 pagg_tab_2 -(18 rows) - --- Since GROUP BY clause does not match with PARTITION KEY; we need to do --- partial aggregation. However, ORDERED SET are not partial safe and thus --- partitionwise aggregation plan is not generated. -EXPLAIN (COSTS OFF) -SELECT a, sum(b order by a) FROM pagg_tab GROUP BY a ORDER BY 1, 2; - QUERY PLAN ---------------------------------------------------------------- - Sort - Sort Key: pagg_tab.a, (sum(pagg_tab.b ORDER BY pagg_tab.a)) - -> GroupAggregate - Group Key: pagg_tab.a - -> Sort - Sort Key: pagg_tab.a - -> Append - -> Seq Scan on pagg_tab_p1 pagg_tab_1 - -> Seq Scan on pagg_tab_p2 pagg_tab_2 - -> Seq Scan on pagg_tab_p3 pagg_tab_3 -(10 rows) - --- JOIN query -CREATE TABLE pagg_tab1(x int, y int) PARTITION BY RANGE(x); -CREATE TABLE pagg_tab1_p1 PARTITION OF pagg_tab1 FOR VALUES FROM (0) TO (10); -CREATE TABLE pagg_tab1_p2 PARTITION OF pagg_tab1 FOR VALUES FROM (10) TO (20); -CREATE TABLE pagg_tab1_p3 PARTITION OF pagg_tab1 FOR VALUES FROM (20) TO (30); -CREATE TABLE pagg_tab2(x int, y int) PARTITION BY RANGE(y); -CREATE TABLE pagg_tab2_p1 PARTITION OF pagg_tab2 FOR VALUES FROM (0) TO (10); -CREATE TABLE pagg_tab2_p2 PARTITION OF pagg_tab2 FOR VALUES FROM (10) TO (20); -CREATE TABLE pagg_tab2_p3 PARTITION OF pagg_tab2 FOR VALUES FROM (20) TO (30); -INSERT INTO pagg_tab1 SELECT i % 30, i % 20 FROM generate_series(0, 299, 2) i; -INSERT INTO pagg_tab2 SELECT i % 20, i % 30 FROM generate_series(0, 299, 3) i; -ANALYZE pagg_tab1; -ANALYZE pagg_tab2; --- When GROUP BY clause matches; full aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------- - Sort - Sort Key: t1.x, (sum(t1.y)), (count(*)) - -> Append - -> HashAggregate - Group Key: t1.x - -> Hash Join - Hash Cond: (t1.x = t2.y) - -> Seq Scan on pagg_tab1_p1 t1 - -> Hash - -> Seq Scan on pagg_tab2_p1 t2 - -> HashAggregate - Group Key: t1_1.x - -> Hash Join - Hash Cond: (t1_1.x = t2_1.y) - -> Seq Scan on pagg_tab1_p2 t1_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 t2_1 - -> HashAggregate - Group Key: t1_2.x - -> Hash Join - Hash Cond: (t2_2.y = t1_2.x) - -> Seq Scan on pagg_tab2_p3 t2_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 t1_2 -(24 rows) - -SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; - x | sum | count -----+------+------- - 0 | 500 | 100 - 6 | 1100 | 100 - 12 | 700 | 100 - 18 | 1300 | 100 - 24 | 900 | 100 -(5 rows) - --- Check with whole-row reference; partitionwise aggregation does not apply -EXPLAIN (COSTS OFF) -SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------- - Sort - Sort Key: t1.x, (sum(t1.y)), (count(((t1.*)::pagg_tab1))) - -> HashAggregate - Group Key: t1.x - -> Hash Join - Hash Cond: (t1.x = t2.y) - -> Append - -> Seq Scan on pagg_tab1_p1 t1_1 - -> Seq Scan on pagg_tab1_p2 t1_2 - -> Seq Scan on pagg_tab1_p3 t1_3 - -> Hash - -> Append - -> Seq Scan on pagg_tab2_p1 t2_1 - -> Seq Scan on pagg_tab2_p2 t2_2 - -> Seq Scan on pagg_tab2_p3 t2_3 -(15 rows) - -SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; - x | sum | count -----+------+------- - 0 | 500 | 100 - 6 | 1100 | 100 - 12 | 700 | 100 - 18 | 1300 | 100 - 24 | 900 | 100 -(5 rows) - --- GROUP BY having other matching key -EXPLAIN (COSTS OFF) -SELECT t2.y, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t2.y ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------- - Sort - Sort Key: t2.y, (sum(t1.y)), (count(*)) - -> Append - -> HashAggregate - Group Key: t2.y - -> Hash Join - Hash Cond: (t1.x = t2.y) - -> Seq Scan on pagg_tab1_p1 t1 - -> Hash - -> Seq Scan on pagg_tab2_p1 t2 - -> HashAggregate - Group Key: t2_1.y - -> Hash Join - Hash Cond: (t1_1.x = t2_1.y) - -> Seq Scan on pagg_tab1_p2 t1_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 t2_1 - -> HashAggregate - Group Key: t2_2.y - -> Hash Join - Hash Cond: (t2_2.y = t1_2.x) - -> Seq Scan on pagg_tab2_p3 t2_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 t1_2 -(24 rows) - --- When GROUP BY clause does not match; partial aggregation is performed for each partition. --- Also test GroupAggregate paths by disabling hash aggregates. -SET enable_hashagg TO false; -EXPLAIN (COSTS OFF) -SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------------------- - Sort - Sort Key: t1.y, (sum(t1.x)), (count(*)) - -> Finalize GroupAggregate - Group Key: t1.y - Filter: (avg(t1.x) > '10'::numeric) - -> Merge Append - Sort Key: t1.y - -> Partial GroupAggregate - Group Key: t1.y - -> Sort - Sort Key: t1.y - -> Hash Join - Hash Cond: (t1.x = t2.y) - -> Seq Scan on pagg_tab1_p1 t1 - -> Hash - -> Seq Scan on pagg_tab2_p1 t2 - -> Partial GroupAggregate - Group Key: t1_1.y - -> Sort - Sort Key: t1_1.y - -> Hash Join - Hash Cond: (t1_1.x = t2_1.y) - -> Seq Scan on pagg_tab1_p2 t1_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 t2_1 - -> Partial GroupAggregate - Group Key: t1_2.y - -> Sort - Sort Key: t1_2.y - -> Hash Join - Hash Cond: (t2_2.y = t1_2.x) - -> Seq Scan on pagg_tab2_p3 t2_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 t1_2 -(34 rows) - -SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; - y | sum | count -----+------+------- - 2 | 600 | 50 - 4 | 1200 | 50 - 8 | 900 | 50 - 12 | 600 | 50 - 14 | 1200 | 50 - 18 | 900 | 50 -(6 rows) - -RESET enable_hashagg; --- Check with LEFT/RIGHT/FULL OUTER JOINs which produces NULL values for --- aggregation --- LEFT JOIN, should produce partial partitionwise aggregation plan as --- GROUP BY is on nullable column -EXPLAIN (COSTS OFF) -SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; - QUERY PLAN ------------------------------------------------------------------- - Finalize GroupAggregate - Group Key: b.y - -> Sort - Sort Key: b.y - -> Append - -> Partial HashAggregate - Group Key: b.y - -> Hash Left Join - Hash Cond: (a.x = b.y) - -> Seq Scan on pagg_tab1_p1 a - -> Hash - -> Seq Scan on pagg_tab2_p1 b - -> Partial HashAggregate - Group Key: b_1.y - -> Hash Left Join - Hash Cond: (a_1.x = b_1.y) - -> Seq Scan on pagg_tab1_p2 a_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 b_1 - -> Partial HashAggregate - Group Key: b_2.y - -> Hash Right Join - Hash Cond: (b_2.y = a_2.x) - -> Seq Scan on pagg_tab2_p3 b_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 a_2 -(26 rows) - -SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; - y | sum -----+------ - 0 | 500 - 6 | 1100 - 12 | 700 - 18 | 1300 - 24 | 900 - | 900 -(6 rows) - --- RIGHT JOIN, should produce full partitionwise aggregation plan as --- GROUP BY is on non-nullable column -EXPLAIN (COSTS OFF) -SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; - QUERY PLAN ------------------------------------------------------------- - Sort - Sort Key: b.y - -> Append - -> HashAggregate - Group Key: b.y - -> Hash Right Join - Hash Cond: (a.x = b.y) - -> Seq Scan on pagg_tab1_p1 a - -> Hash - -> Seq Scan on pagg_tab2_p1 b - -> HashAggregate - Group Key: b_1.y - -> Hash Right Join - Hash Cond: (a_1.x = b_1.y) - -> Seq Scan on pagg_tab1_p2 a_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 b_1 - -> HashAggregate - Group Key: b_2.y - -> Hash Left Join - Hash Cond: (b_2.y = a_2.x) - -> Seq Scan on pagg_tab2_p3 b_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 a_2 -(24 rows) - -SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; - y | sum -----+------ - 0 | 500 - 3 | - 6 | 1100 - 9 | - 12 | 700 - 15 | - 18 | 1300 - 21 | - 24 | 900 - 27 | -(10 rows) - --- FULL JOIN, should produce partial partitionwise aggregation plan as --- GROUP BY is on nullable column -EXPLAIN (COSTS OFF) -SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; - QUERY PLAN ------------------------------------------------------------------- - Finalize GroupAggregate - Group Key: a.x - -> Sort - Sort Key: a.x - -> Append - -> Partial HashAggregate - Group Key: a.x - -> Hash Full Join - Hash Cond: (a.x = b.y) - -> Seq Scan on pagg_tab1_p1 a - -> Hash - -> Seq Scan on pagg_tab2_p1 b - -> Partial HashAggregate - Group Key: a_1.x - -> Hash Full Join - Hash Cond: (a_1.x = b_1.y) - -> Seq Scan on pagg_tab1_p2 a_1 - -> Hash - -> Seq Scan on pagg_tab2_p2 b_1 - -> Partial HashAggregate - Group Key: a_2.x - -> Hash Full Join - Hash Cond: (b_2.y = a_2.x) - -> Seq Scan on pagg_tab2_p3 b_2 - -> Hash - -> Seq Scan on pagg_tab1_p3 a_2 -(26 rows) - -SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; - x | sum -----+------ - 0 | 500 - 2 | - 4 | - 6 | 1100 - 8 | - 10 | - 12 | 700 - 14 | - 16 | - 18 | 1300 - 20 | - 22 | - 24 | 900 - 26 | - 28 | - | 500 -(16 rows) - --- LEFT JOIN, with dummy relation on right side, ideally --- should produce full partitionwise aggregation plan as GROUP BY is on --- non-nullable columns. --- But right now we are unable to do partitionwise join in this case. -EXPLAIN (COSTS OFF) -SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: pagg_tab1.x, pagg_tab2.y - -> HashAggregate - Group Key: pagg_tab1.x, pagg_tab2.y - -> Hash Left Join - Hash Cond: (pagg_tab1.x = pagg_tab2.y) - Filter: ((pagg_tab1.x > 5) OR (pagg_tab2.y < 20)) - -> Append - -> Seq Scan on pagg_tab1_p1 pagg_tab1_1 - Filter: (x < 20) - -> Seq Scan on pagg_tab1_p2 pagg_tab1_2 - Filter: (x < 20) - -> Hash - -> Append - -> Seq Scan on pagg_tab2_p2 pagg_tab2_1 - Filter: (y > 10) - -> Seq Scan on pagg_tab2_p3 pagg_tab2_2 - Filter: (y > 10) -(18 rows) - -SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; - x | y | count -----+----+------- - 6 | | 10 - 8 | | 10 - 10 | | 10 - 12 | 12 | 100 - 14 | | 10 - 16 | | 10 - 18 | 18 | 100 -(7 rows) - --- FULL JOIN, with dummy relations on both sides, ideally --- should produce partial partitionwise aggregation plan as GROUP BY is on --- nullable columns. --- But right now we are unable to do partitionwise join in this case. -EXPLAIN (COSTS OFF) -SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: pagg_tab1.x, pagg_tab2.y - -> HashAggregate - Group Key: pagg_tab1.x, pagg_tab2.y - -> Hash Full Join - Hash Cond: (pagg_tab1.x = pagg_tab2.y) - Filter: ((pagg_tab1.x > 5) OR (pagg_tab2.y < 20)) - -> Append - -> Seq Scan on pagg_tab1_p1 pagg_tab1_1 - Filter: (x < 20) - -> Seq Scan on pagg_tab1_p2 pagg_tab1_2 - Filter: (x < 20) - -> Hash - -> Append - -> Seq Scan on pagg_tab2_p2 pagg_tab2_1 - Filter: (y > 10) - -> Seq Scan on pagg_tab2_p3 pagg_tab2_2 - Filter: (y > 10) -(18 rows) - -SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; - x | y | count -----+----+------- - 6 | | 10 - 8 | | 10 - 10 | | 10 - 12 | 12 | 100 - 14 | | 10 - 16 | | 10 - 18 | 18 | 100 - | 15 | 10 -(8 rows) - --- Empty join relation because of empty outer side, no partitionwise agg plan -EXPLAIN (COSTS OFF) -SELECT a.x, a.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; - QUERY PLAN ---------------------------------------- - GroupAggregate - Group Key: pagg_tab1.x, pagg_tab1.y - -> Sort - Sort Key: pagg_tab1.y - -> Result - One-Time Filter: false -(6 rows) - -SELECT a.x, a.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; - x | y | count ----+---+------- -(0 rows) - --- Partition by multiple columns -CREATE TABLE pagg_tab_m (a int, b int, c int) PARTITION BY RANGE(a, ((a+b)/2)); -CREATE TABLE pagg_tab_m_p1 PARTITION OF pagg_tab_m FOR VALUES FROM (0, 0) TO (12, 12); -CREATE TABLE pagg_tab_m_p2 PARTITION OF pagg_tab_m FOR VALUES FROM (12, 12) TO (22, 22); -CREATE TABLE pagg_tab_m_p3 PARTITION OF pagg_tab_m FOR VALUES FROM (22, 22) TO (30, 30); -INSERT INTO pagg_tab_m SELECT i % 30, i % 40, i % 50 FROM generate_series(0, 2999) i; -ANALYZE pagg_tab_m; --- Partial aggregation as GROUP BY clause does not match with PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_m.a, (sum(pagg_tab_m.b)), (avg(pagg_tab_m.c)) - -> Finalize HashAggregate - Group Key: pagg_tab_m.a - Filter: (avg(pagg_tab_m.c) < '22'::numeric) - -> Append - -> Partial HashAggregate - Group Key: pagg_tab_m.a - -> Seq Scan on pagg_tab_m_p1 pagg_tab_m - -> Partial HashAggregate - Group Key: pagg_tab_m_1.a - -> Seq Scan on pagg_tab_m_p2 pagg_tab_m_1 - -> Partial HashAggregate - Group Key: pagg_tab_m_2.a - -> Seq Scan on pagg_tab_m_p3 pagg_tab_m_2 -(15 rows) - -SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; - a | sum | avg | count -----+------+---------------------+------- - 0 | 1500 | 20.0000000000000000 | 100 - 1 | 1600 | 21.0000000000000000 | 100 - 10 | 1500 | 20.0000000000000000 | 100 - 11 | 1600 | 21.0000000000000000 | 100 - 20 | 1500 | 20.0000000000000000 | 100 - 21 | 1600 | 21.0000000000000000 | 100 -(6 rows) - --- Full aggregation as GROUP BY clause matches with PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a+b)/2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; - QUERY PLAN ----------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_m.a, (sum(pagg_tab_m.b)), (avg(pagg_tab_m.c)) - -> Append - -> HashAggregate - Group Key: pagg_tab_m.a, ((pagg_tab_m.a + pagg_tab_m.b) / 2) - Filter: (sum(pagg_tab_m.b) < 50) - -> Seq Scan on pagg_tab_m_p1 pagg_tab_m - -> HashAggregate - Group Key: pagg_tab_m_1.a, ((pagg_tab_m_1.a + pagg_tab_m_1.b) / 2) - Filter: (sum(pagg_tab_m_1.b) < 50) - -> Seq Scan on pagg_tab_m_p2 pagg_tab_m_1 - -> HashAggregate - Group Key: pagg_tab_m_2.a, ((pagg_tab_m_2.a + pagg_tab_m_2.b) / 2) - Filter: (sum(pagg_tab_m_2.b) < 50) - -> Seq Scan on pagg_tab_m_p3 pagg_tab_m_2 -(15 rows) - -SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a+b)/2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; - a | sum | avg | count -----+-----+---------------------+------- - 0 | 0 | 20.0000000000000000 | 25 - 1 | 25 | 21.0000000000000000 | 25 - 10 | 0 | 20.0000000000000000 | 25 - 11 | 25 | 21.0000000000000000 | 25 - 20 | 0 | 20.0000000000000000 | 25 - 21 | 25 | 21.0000000000000000 | 25 -(6 rows) - --- Full aggregation as PARTITION KEY is part of GROUP BY clause -EXPLAIN (COSTS OFF) -SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a+b)/2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_m.a, pagg_tab_m.c, (sum(pagg_tab_m.b)) - -> Append - -> HashAggregate - Group Key: ((pagg_tab_m.a + pagg_tab_m.b) / 2), pagg_tab_m.c, pagg_tab_m.a - Filter: ((sum(pagg_tab_m.b) = 50) AND (avg(pagg_tab_m.c) > '25'::numeric)) - -> Seq Scan on pagg_tab_m_p1 pagg_tab_m - -> HashAggregate - Group Key: ((pagg_tab_m_1.a + pagg_tab_m_1.b) / 2), pagg_tab_m_1.c, pagg_tab_m_1.a - Filter: ((sum(pagg_tab_m_1.b) = 50) AND (avg(pagg_tab_m_1.c) > '25'::numeric)) - -> Seq Scan on pagg_tab_m_p2 pagg_tab_m_1 - -> HashAggregate - Group Key: ((pagg_tab_m_2.a + pagg_tab_m_2.b) / 2), pagg_tab_m_2.c, pagg_tab_m_2.a - Filter: ((sum(pagg_tab_m_2.b) = 50) AND (avg(pagg_tab_m_2.c) > '25'::numeric)) - -> Seq Scan on pagg_tab_m_p3 pagg_tab_m_2 -(15 rows) - -SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a+b)/2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; - a | c | sum | avg | count -----+----+-----+---------------------+------- - 0 | 30 | 50 | 30.0000000000000000 | 5 - 0 | 40 | 50 | 40.0000000000000000 | 5 - 10 | 30 | 50 | 30.0000000000000000 | 5 - 10 | 40 | 50 | 40.0000000000000000 | 5 - 20 | 30 | 50 | 30.0000000000000000 | 5 - 20 | 40 | 50 | 40.0000000000000000 | 5 -(6 rows) - --- Test with multi-level partitioning scheme -CREATE TABLE pagg_tab_ml (a int, b int, c text) PARTITION BY RANGE(a); -CREATE TABLE pagg_tab_ml_p1 PARTITION OF pagg_tab_ml FOR VALUES FROM (0) TO (12); -CREATE TABLE pagg_tab_ml_p2 PARTITION OF pagg_tab_ml FOR VALUES FROM (12) TO (20) PARTITION BY LIST (c); -CREATE TABLE pagg_tab_ml_p2_s1 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0000', '0001', '0002'); -CREATE TABLE pagg_tab_ml_p2_s2 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0003'); --- This level of partitioning has different column positions than the parent -CREATE TABLE pagg_tab_ml_p3(b int, c text, a int) PARTITION BY RANGE (b); -CREATE TABLE pagg_tab_ml_p3_s1(c text, a int, b int); -CREATE TABLE pagg_tab_ml_p3_s2 PARTITION OF pagg_tab_ml_p3 FOR VALUES FROM (7) TO (10); -ALTER TABLE pagg_tab_ml_p3 ATTACH PARTITION pagg_tab_ml_p3_s1 FOR VALUES FROM (0) TO (7); -ALTER TABLE pagg_tab_ml ATTACH PARTITION pagg_tab_ml_p3 FOR VALUES FROM (20) TO (30); -INSERT INTO pagg_tab_ml SELECT i % 30, i % 10, to_char(i % 4, 'FM0000') FROM generate_series(0, 29999) i; -ANALYZE pagg_tab_ml; --- For Parallel Append -SET max_parallel_workers_per_gather TO 2; --- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY --- for level 1 only. For subpartitions, GROUP BY clause does not match with --- PARTITION KEY, but still we do not see a partial aggregation as array_agg() --- is not partial agg safe. -EXPLAIN (COSTS OFF) -SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - QUERY PLAN --------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.a, (sum(pagg_tab_ml.b)), (array_agg(DISTINCT pagg_tab_ml.c)) - -> Gather - Workers Planned: 2 - -> Parallel Append - -> GroupAggregate - Group Key: pagg_tab_ml.a - Filter: (avg(pagg_tab_ml.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml.a - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> GroupAggregate - Group Key: pagg_tab_ml_5.a - Filter: (avg(pagg_tab_ml_5.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_5.a - -> Append - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_5 - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_6 - -> GroupAggregate - Group Key: pagg_tab_ml_2.a - Filter: (avg(pagg_tab_ml_2.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_2.a - -> Append - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_2 - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_3 -(27 rows) - -SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - a | sum | array_agg | count -----+------+-------------+------- - 0 | 0 | {0000,0002} | 1000 - 1 | 1000 | {0001,0003} | 1000 - 2 | 2000 | {0000,0002} | 1000 - 10 | 0 | {0000,0002} | 1000 - 11 | 1000 | {0001,0003} | 1000 - 12 | 2000 | {0000,0002} | 1000 - 20 | 0 | {0000,0002} | 1000 - 21 | 1000 | {0001,0003} | 1000 - 22 | 2000 | {0000,0002} | 1000 -(9 rows) - --- Without ORDER BY clause, to test Gather at top-most path -EXPLAIN (COSTS OFF) -SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3; - QUERY PLAN ---------------------------------------------------------------------------- - Gather - Workers Planned: 2 - -> Parallel Append - -> GroupAggregate - Group Key: pagg_tab_ml.a - Filter: (avg(pagg_tab_ml.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml.a - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> GroupAggregate - Group Key: pagg_tab_ml_5.a - Filter: (avg(pagg_tab_ml_5.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_5.a - -> Append - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_5 - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_6 - -> GroupAggregate - Group Key: pagg_tab_ml_2.a - Filter: (avg(pagg_tab_ml_2.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_2.a - -> Append - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_2 - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_3 -(25 rows) - --- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY --- for level 1 only. For subpartitions, GROUP BY clause does not match with --- PARTITION KEY, thus we will have a partial aggregation for them. -EXPLAIN (COSTS OFF) -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.a, (sum(pagg_tab_ml.b)), (count(*)) - -> Append - -> HashAggregate - Group Key: pagg_tab_ml.a - Filter: (avg(pagg_tab_ml.b) < '3'::numeric) - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> Finalize GroupAggregate - Group Key: pagg_tab_ml_2.a - Filter: (avg(pagg_tab_ml_2.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_2.a - -> Append - -> Partial HashAggregate - Group Key: pagg_tab_ml_2.a - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_2 - -> Partial HashAggregate - Group Key: pagg_tab_ml_3.a - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_3 - -> Finalize GroupAggregate - Group Key: pagg_tab_ml_5.a - Filter: (avg(pagg_tab_ml_5.b) < '3'::numeric) - -> Sort - Sort Key: pagg_tab_ml_5.a - -> Append - -> Partial HashAggregate - Group Key: pagg_tab_ml_5.a - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_5 - -> Partial HashAggregate - Group Key: pagg_tab_ml_6.a - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_6 -(31 rows) - -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - a | sum | count -----+------+------- - 0 | 0 | 1000 - 1 | 1000 | 1000 - 2 | 2000 | 1000 - 10 | 0 | 1000 - 11 | 1000 | 1000 - 12 | 2000 | 1000 - 20 | 0 | 1000 - 21 | 1000 | 1000 - 22 | 2000 | 1000 -(9 rows) - --- Partial aggregation at all levels as GROUP BY clause does not match with --- PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; - QUERY PLAN ---------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.b, (sum(pagg_tab_ml.a)), (count(*)) - -> Finalize GroupAggregate - Group Key: pagg_tab_ml.b - -> Sort - Sort Key: pagg_tab_ml.b - -> Append - -> Partial HashAggregate - Group Key: pagg_tab_ml.b - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> Partial HashAggregate - Group Key: pagg_tab_ml_1.b - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_1 - -> Partial HashAggregate - Group Key: pagg_tab_ml_2.b - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_2 - -> Partial HashAggregate - Group Key: pagg_tab_ml_3.b - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_3 - -> Partial HashAggregate - Group Key: pagg_tab_ml_4.b - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_4 -(22 rows) - -SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; - b | sum | count ----+-------+------- - 0 | 30000 | 3000 - 1 | 33000 | 3000 - 2 | 36000 | 3000 - 3 | 39000 | 3000 - 4 | 42000 | 3000 -(5 rows) - --- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; - QUERY PLAN ----------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.a, (sum(pagg_tab_ml.b)), (count(*)) - -> Append - -> HashAggregate - Group Key: pagg_tab_ml.a, pagg_tab_ml.b, pagg_tab_ml.c - Filter: (avg(pagg_tab_ml.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> HashAggregate - Group Key: pagg_tab_ml_1.a, pagg_tab_ml_1.b, pagg_tab_ml_1.c - Filter: (avg(pagg_tab_ml_1.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_1 - -> HashAggregate - Group Key: pagg_tab_ml_2.a, pagg_tab_ml_2.b, pagg_tab_ml_2.c - Filter: (avg(pagg_tab_ml_2.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_2 - -> HashAggregate - Group Key: pagg_tab_ml_3.a, pagg_tab_ml_3.b, pagg_tab_ml_3.c - Filter: (avg(pagg_tab_ml_3.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_3 - -> HashAggregate - Group Key: pagg_tab_ml_4.a, pagg_tab_ml_4.b, pagg_tab_ml_4.c - Filter: (avg(pagg_tab_ml_4.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_4 -(23 rows) - -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; - a | sum | count -----+------+------- - 8 | 4000 | 500 - 8 | 4000 | 500 - 9 | 4500 | 500 - 9 | 4500 | 500 - 18 | 4000 | 500 - 18 | 4000 | 500 - 19 | 4500 | 500 - 19 | 4500 | 500 - 28 | 4000 | 500 - 28 | 4000 | 500 - 29 | 4500 | 500 - 29 | 4500 | 500 -(12 rows) - --- Parallelism within partitionwise aggregates -SET min_parallel_table_scan_size TO '8kB'; -SET parallel_setup_cost TO 0; --- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY --- for level 1 only. For subpartitions, GROUP BY clause does not match with --- PARTITION KEY, thus we will have a partial aggregation for them. -EXPLAIN (COSTS OFF) -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.a, (sum(pagg_tab_ml.b)), (count(*)) - -> Append - -> Finalize GroupAggregate - Group Key: pagg_tab_ml.a - Filter: (avg(pagg_tab_ml.b) < '3'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_ml.a - -> Partial HashAggregate - Group Key: pagg_tab_ml.a - -> Parallel Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> Finalize GroupAggregate - Group Key: pagg_tab_ml_2.a - Filter: (avg(pagg_tab_ml_2.b) < '3'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_ml_2.a - -> Parallel Append - -> Partial HashAggregate - Group Key: pagg_tab_ml_2.a - -> Parallel Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_2 - -> Partial HashAggregate - Group Key: pagg_tab_ml_3.a - -> Parallel Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_3 - -> Finalize GroupAggregate - Group Key: pagg_tab_ml_5.a - Filter: (avg(pagg_tab_ml_5.b) < '3'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_ml_5.a - -> Parallel Append - -> Partial HashAggregate - Group Key: pagg_tab_ml_5.a - -> Parallel Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_5 - -> Partial HashAggregate - Group Key: pagg_tab_ml_6.a - -> Parallel Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_6 -(41 rows) - -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; - a | sum | count -----+------+------- - 0 | 0 | 1000 - 1 | 1000 | 1000 - 2 | 2000 | 1000 - 10 | 0 | 1000 - 11 | 1000 | 1000 - 12 | 2000 | 1000 - 20 | 0 | 1000 - 21 | 1000 | 1000 - 22 | 2000 | 1000 -(9 rows) - --- Partial aggregation at all levels as GROUP BY clause does not match with --- PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; - QUERY PLAN ------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_ml.b, (sum(pagg_tab_ml.a)), (count(*)) - -> Finalize GroupAggregate - Group Key: pagg_tab_ml.b - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_ml.b - -> Parallel Append - -> Partial HashAggregate - Group Key: pagg_tab_ml.b - -> Parallel Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> Partial HashAggregate - Group Key: pagg_tab_ml_3.b - -> Parallel Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_3 - -> Partial HashAggregate - Group Key: pagg_tab_ml_1.b - -> Parallel Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_1 - -> Partial HashAggregate - Group Key: pagg_tab_ml_4.b - -> Parallel Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_4 - -> Partial HashAggregate - Group Key: pagg_tab_ml_2.b - -> Parallel Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_2 -(24 rows) - -SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; - b | sum | count ----+-------+------- - 0 | 30000 | 3000 - 1 | 33000 | 3000 - 2 | 36000 | 3000 - 3 | 39000 | 3000 - 4 | 42000 | 3000 -(5 rows) - --- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY -EXPLAIN (COSTS OFF) -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; - QUERY PLAN ----------------------------------------------------------------------------------- - Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_ml.a, (sum(pagg_tab_ml.b)), (count(*)) - -> Parallel Append - -> HashAggregate - Group Key: pagg_tab_ml.a, pagg_tab_ml.b, pagg_tab_ml.c - Filter: (avg(pagg_tab_ml.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p1 pagg_tab_ml - -> HashAggregate - Group Key: pagg_tab_ml_3.a, pagg_tab_ml_3.b, pagg_tab_ml_3.c - Filter: (avg(pagg_tab_ml_3.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p3_s1 pagg_tab_ml_3 - -> HashAggregate - Group Key: pagg_tab_ml_1.a, pagg_tab_ml_1.b, pagg_tab_ml_1.c - Filter: (avg(pagg_tab_ml_1.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p2_s1 pagg_tab_ml_1 - -> HashAggregate - Group Key: pagg_tab_ml_4.a, pagg_tab_ml_4.b, pagg_tab_ml_4.c - Filter: (avg(pagg_tab_ml_4.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p3_s2 pagg_tab_ml_4 - -> HashAggregate - Group Key: pagg_tab_ml_2.a, pagg_tab_ml_2.b, pagg_tab_ml_2.c - Filter: (avg(pagg_tab_ml_2.b) > '7'::numeric) - -> Seq Scan on pagg_tab_ml_p2_s2 pagg_tab_ml_2 -(25 rows) - -SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; - a | sum | count -----+------+------- - 8 | 4000 | 500 - 8 | 4000 | 500 - 9 | 4500 | 500 - 9 | 4500 | 500 - 18 | 4000 | 500 - 18 | 4000 | 500 - 19 | 4500 | 500 - 19 | 4500 | 500 - 28 | 4000 | 500 - 28 | 4000 | 500 - 29 | 4500 | 500 - 29 | 4500 | 500 -(12 rows) - --- Parallelism within partitionwise aggregates (single level) --- Add few parallel setup cost, so that we will see a plan which gathers --- partially created paths even for full aggregation and sticks a single Gather --- followed by finalization step. --- Without this, the cost of doing partial aggregation + Gather + finalization --- for each partition and then Append over it turns out to be same and this --- wins as we add it first. This parallel_setup_cost plays a vital role in --- costing such plans. -SET parallel_setup_cost TO 10; -CREATE TABLE pagg_tab_para(x int, y int) PARTITION BY RANGE(x); -CREATE TABLE pagg_tab_para_p1 PARTITION OF pagg_tab_para FOR VALUES FROM (0) TO (12); -CREATE TABLE pagg_tab_para_p2 PARTITION OF pagg_tab_para FOR VALUES FROM (12) TO (22); -CREATE TABLE pagg_tab_para_p3 PARTITION OF pagg_tab_para FOR VALUES FROM (22) TO (30); -INSERT INTO pagg_tab_para SELECT i % 30, i % 20 FROM generate_series(0, 29999) i; -ANALYZE pagg_tab_para; --- When GROUP BY clause matches; full aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_para.x, (sum(pagg_tab_para.y)), (avg(pagg_tab_para.y)) - -> Finalize GroupAggregate - Group Key: pagg_tab_para.x - Filter: (avg(pagg_tab_para.y) < '7'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_para.x - -> Parallel Append - -> Partial HashAggregate - Group Key: pagg_tab_para.x - -> Parallel Seq Scan on pagg_tab_para_p1 pagg_tab_para - -> Partial HashAggregate - Group Key: pagg_tab_para_1.x - -> Parallel Seq Scan on pagg_tab_para_p2 pagg_tab_para_1 - -> Partial HashAggregate - Group Key: pagg_tab_para_2.x - -> Parallel Seq Scan on pagg_tab_para_p3 pagg_tab_para_2 -(19 rows) - -SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - x | sum | avg | count -----+------+--------------------+------- - 0 | 5000 | 5.0000000000000000 | 1000 - 1 | 6000 | 6.0000000000000000 | 1000 - 10 | 5000 | 5.0000000000000000 | 1000 - 11 | 6000 | 6.0000000000000000 | 1000 - 20 | 5000 | 5.0000000000000000 | 1000 - 21 | 6000 | 6.0000000000000000 | 1000 -(6 rows) - --- When GROUP BY clause does not match; partial aggregation is performed for each partition. -EXPLAIN (COSTS OFF) -SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_para.y, (sum(pagg_tab_para.x)), (avg(pagg_tab_para.x)) - -> Finalize GroupAggregate - Group Key: pagg_tab_para.y - Filter: (avg(pagg_tab_para.x) < '12'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_para.y - -> Parallel Append - -> Partial HashAggregate - Group Key: pagg_tab_para.y - -> Parallel Seq Scan on pagg_tab_para_p1 pagg_tab_para - -> Partial HashAggregate - Group Key: pagg_tab_para_1.y - -> Parallel Seq Scan on pagg_tab_para_p2 pagg_tab_para_1 - -> Partial HashAggregate - Group Key: pagg_tab_para_2.y - -> Parallel Seq Scan on pagg_tab_para_p3 pagg_tab_para_2 -(19 rows) - -SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; - y | sum | avg | count -----+-------+---------------------+------- - 0 | 15000 | 10.0000000000000000 | 1500 - 1 | 16500 | 11.0000000000000000 | 1500 - 10 | 15000 | 10.0000000000000000 | 1500 - 11 | 16500 | 11.0000000000000000 | 1500 -(4 rows) - --- Test when parent can produce parallel paths but not any (or some) of its children --- (Use one more aggregate to tilt the cost estimates for the plan we want) -ALTER TABLE pagg_tab_para_p1 SET (parallel_workers = 0); -ALTER TABLE pagg_tab_para_p3 SET (parallel_workers = 0); -ANALYZE pagg_tab_para; -EXPLAIN (COSTS OFF) -SELECT x, sum(y), avg(y), sum(x+y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - QUERY PLAN -------------------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_para.x, (sum(pagg_tab_para.y)), (avg(pagg_tab_para.y)) - -> Finalize GroupAggregate - Group Key: pagg_tab_para.x - Filter: (avg(pagg_tab_para.y) < '7'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_para.x - -> Partial HashAggregate - Group Key: pagg_tab_para.x - -> Parallel Append - -> Seq Scan on pagg_tab_para_p1 pagg_tab_para_1 - -> Seq Scan on pagg_tab_para_p3 pagg_tab_para_3 - -> Parallel Seq Scan on pagg_tab_para_p2 pagg_tab_para_2 -(15 rows) - -SELECT x, sum(y), avg(y), sum(x+y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - x | sum | avg | sum | count -----+------+--------------------+-------+------- - 0 | 5000 | 5.0000000000000000 | 5000 | 1000 - 1 | 6000 | 6.0000000000000000 | 7000 | 1000 - 10 | 5000 | 5.0000000000000000 | 15000 | 1000 - 11 | 6000 | 6.0000000000000000 | 17000 | 1000 - 20 | 5000 | 5.0000000000000000 | 25000 | 1000 - 21 | 6000 | 6.0000000000000000 | 27000 | 1000 -(6 rows) - -ALTER TABLE pagg_tab_para_p2 SET (parallel_workers = 0); -ANALYZE pagg_tab_para; -EXPLAIN (COSTS OFF) -SELECT x, sum(y), avg(y), sum(x+y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - QUERY PLAN ----------------------------------------------------------------------------------- - Sort - Sort Key: pagg_tab_para.x, (sum(pagg_tab_para.y)), (avg(pagg_tab_para.y)) - -> Finalize GroupAggregate - Group Key: pagg_tab_para.x - Filter: (avg(pagg_tab_para.y) < '7'::numeric) - -> Gather Merge - Workers Planned: 2 - -> Sort - Sort Key: pagg_tab_para.x - -> Partial HashAggregate - Group Key: pagg_tab_para.x - -> Parallel Append - -> Seq Scan on pagg_tab_para_p1 pagg_tab_para_1 - -> Seq Scan on pagg_tab_para_p2 pagg_tab_para_2 - -> Seq Scan on pagg_tab_para_p3 pagg_tab_para_3 -(15 rows) - -SELECT x, sum(y), avg(y), sum(x+y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - x | sum | avg | sum | count -----+------+--------------------+-------+------- - 0 | 5000 | 5.0000000000000000 | 5000 | 1000 - 1 | 6000 | 6.0000000000000000 | 7000 | 1000 - 10 | 5000 | 5.0000000000000000 | 15000 | 1000 - 11 | 6000 | 6.0000000000000000 | 17000 | 1000 - 20 | 5000 | 5.0000000000000000 | 25000 | 1000 - 21 | 6000 | 6.0000000000000000 | 27000 | 1000 -(6 rows) - --- Reset parallelism parameters to get partitionwise aggregation plan. -RESET min_parallel_table_scan_size; -RESET parallel_setup_cost; -EXPLAIN (COSTS OFF) -SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - QUERY PLAN ------------------------------------------------------------------------------ - Sort - Sort Key: pagg_tab_para.x, (sum(pagg_tab_para.y)), (avg(pagg_tab_para.y)) - -> Append - -> HashAggregate - Group Key: pagg_tab_para.x - Filter: (avg(pagg_tab_para.y) < '7'::numeric) - -> Seq Scan on pagg_tab_para_p1 pagg_tab_para - -> HashAggregate - Group Key: pagg_tab_para_1.x - Filter: (avg(pagg_tab_para_1.y) < '7'::numeric) - -> Seq Scan on pagg_tab_para_p2 pagg_tab_para_1 - -> HashAggregate - Group Key: pagg_tab_para_2.x - Filter: (avg(pagg_tab_para_2.y) < '7'::numeric) - -> Seq Scan on pagg_tab_para_p3 pagg_tab_para_2 -(15 rows) - -SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; - x | sum | avg | count -----+------+--------------------+------- - 0 | 5000 | 5.0000000000000000 | 1000 - 1 | 6000 | 6.0000000000000000 | 1000 - 10 | 5000 | 5.0000000000000000 | 1000 - 11 | 6000 | 6.0000000000000000 | 1000 - 20 | 5000 | 5.0000000000000000 | 1000 - 21 | 6000 | 6.0000000000000000 | 1000 -(6 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_info.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_info.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/partition_info.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/partition_info.out 2021-10-30 22:09:26 -0600 @@ -1,351 +1,2 @@ --- --- Tests for functions providing information about partitions --- -SELECT * FROM pg_partition_tree(NULL); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_tree(0); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_ancestors(NULL); - relid -------- -(0 rows) - -SELECT * FROM pg_partition_ancestors(0); - relid -------- -(0 rows) - -SELECT pg_partition_root(NULL); - pg_partition_root -------------------- - -(1 row) - -SELECT pg_partition_root(0); - pg_partition_root -------------------- - -(1 row) - --- Test table partition trees -CREATE TABLE ptif_test (a int, b int) PARTITION BY range (a); -CREATE TABLE ptif_test0 PARTITION OF ptif_test - FOR VALUES FROM (minvalue) TO (0) PARTITION BY list (b); -CREATE TABLE ptif_test01 PARTITION OF ptif_test0 FOR VALUES IN (1); -CREATE TABLE ptif_test1 PARTITION OF ptif_test - FOR VALUES FROM (0) TO (100) PARTITION BY list (b); -CREATE TABLE ptif_test11 PARTITION OF ptif_test1 FOR VALUES IN (1); -CREATE TABLE ptif_test2 PARTITION OF ptif_test - FOR VALUES FROM (100) TO (200); --- This partitioned table should remain with no partitions. -CREATE TABLE ptif_test3 PARTITION OF ptif_test - FOR VALUES FROM (200) TO (maxvalue) PARTITION BY list (b); --- Test pg_partition_root for tables -SELECT pg_partition_root('ptif_test'); - pg_partition_root -------------------- - ptif_test -(1 row) - -SELECT pg_partition_root('ptif_test0'); - pg_partition_root -------------------- - ptif_test -(1 row) - -SELECT pg_partition_root('ptif_test01'); - pg_partition_root -------------------- - ptif_test -(1 row) - -SELECT pg_partition_root('ptif_test3'); - pg_partition_root -------------------- - ptif_test -(1 row) - --- Test index partition tree -CREATE INDEX ptif_test_index ON ONLY ptif_test (a); -CREATE INDEX ptif_test0_index ON ONLY ptif_test0 (a); -ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test0_index; -CREATE INDEX ptif_test01_index ON ptif_test01 (a); -ALTER INDEX ptif_test0_index ATTACH PARTITION ptif_test01_index; -CREATE INDEX ptif_test1_index ON ONLY ptif_test1 (a); -ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test1_index; -CREATE INDEX ptif_test11_index ON ptif_test11 (a); -ALTER INDEX ptif_test1_index ATTACH PARTITION ptif_test11_index; -CREATE INDEX ptif_test2_index ON ptif_test2 (a); -ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test2_index; -CREATE INDEX ptif_test3_index ON ptif_test3 (a); -ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test3_index; --- Test pg_partition_root for indexes -SELECT pg_partition_root('ptif_test_index'); - pg_partition_root -------------------- - ptif_test_index -(1 row) - -SELECT pg_partition_root('ptif_test0_index'); - pg_partition_root -------------------- - ptif_test_index -(1 row) - -SELECT pg_partition_root('ptif_test01_index'); - pg_partition_root -------------------- - ptif_test_index -(1 row) - -SELECT pg_partition_root('ptif_test3_index'); - pg_partition_root -------------------- - ptif_test_index -(1 row) - --- List all tables members of the tree -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test'); - relid | parentrelid | level | isleaf --------------+-------------+-------+-------- - ptif_test | | 0 | f - ptif_test0 | ptif_test | 1 | f - ptif_test1 | ptif_test | 1 | f - ptif_test2 | ptif_test | 1 | t - ptif_test3 | ptif_test | 1 | f - ptif_test01 | ptif_test0 | 2 | t - ptif_test11 | ptif_test1 | 2 | t -(7 rows) - --- List tables from an intermediate level -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test0') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------+-------------+-------+-------- - ptif_test0 | ptif_test | 0 | f - ptif_test01 | ptif_test0 | 1 | t -(2 rows) - --- List from leaf table -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test01') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------+-------------+-------+-------- - ptif_test01 | ptif_test0 | 0 | t -(1 row) - --- List from partitioned table with no partitions -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test3') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf -------------+-------------+-------+-------- - ptif_test3 | ptif_test | 0 | f -(1 row) - --- List all ancestors of root and leaf tables -SELECT * FROM pg_partition_ancestors('ptif_test01'); - relid -------------- - ptif_test01 - ptif_test0 - ptif_test -(3 rows) - -SELECT * FROM pg_partition_ancestors('ptif_test'); - relid ------------ - ptif_test -(1 row) - --- List all members using pg_partition_root with leaf table reference -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree(pg_partition_root('ptif_test01')) p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------+-------------+-------+-------- - ptif_test | | 0 | f - ptif_test0 | ptif_test | 1 | f - ptif_test1 | ptif_test | 1 | f - ptif_test2 | ptif_test | 1 | t - ptif_test3 | ptif_test | 1 | f - ptif_test01 | ptif_test0 | 2 | t - ptif_test11 | ptif_test1 | 2 | t -(7 rows) - --- List all indexes members of the tree -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test_index'); - relid | parentrelid | level | isleaf --------------------+------------------+-------+-------- - ptif_test_index | | 0 | f - ptif_test0_index | ptif_test_index | 1 | f - ptif_test1_index | ptif_test_index | 1 | f - ptif_test2_index | ptif_test_index | 1 | t - ptif_test3_index | ptif_test_index | 1 | f - ptif_test01_index | ptif_test0_index | 2 | t - ptif_test11_index | ptif_test1_index | 2 | t -(7 rows) - --- List indexes from an intermediate level -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test0_index') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------------+------------------+-------+-------- - ptif_test0_index | ptif_test_index | 0 | f - ptif_test01_index | ptif_test0_index | 1 | t -(2 rows) - --- List from leaf index -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test01_index') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------------+------------------+-------+-------- - ptif_test01_index | ptif_test0_index | 0 | t -(1 row) - --- List from partitioned index with no partitions -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_test3_index') p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf -------------------+-----------------+-------+-------- - ptif_test3_index | ptif_test_index | 0 | f -(1 row) - --- List all members using pg_partition_root with leaf index reference -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree(pg_partition_root('ptif_test01_index')) p - JOIN pg_class c ON (p.relid = c.oid); - relid | parentrelid | level | isleaf --------------------+------------------+-------+-------- - ptif_test_index | | 0 | f - ptif_test0_index | ptif_test_index | 1 | f - ptif_test1_index | ptif_test_index | 1 | f - ptif_test2_index | ptif_test_index | 1 | t - ptif_test3_index | ptif_test_index | 1 | f - ptif_test01_index | ptif_test0_index | 2 | t - ptif_test11_index | ptif_test1_index | 2 | t -(7 rows) - --- List all ancestors of root and leaf indexes -SELECT * FROM pg_partition_ancestors('ptif_test01_index'); - relid -------------------- - ptif_test01_index - ptif_test0_index - ptif_test_index -(3 rows) - -SELECT * FROM pg_partition_ancestors('ptif_test_index'); - relid ------------------ - ptif_test_index -(1 row) - -DROP TABLE ptif_test; --- Table that is not part of any partition tree is not listed. -CREATE TABLE ptif_normal_table(a int); -SELECT relid, parentrelid, level, isleaf - FROM pg_partition_tree('ptif_normal_table'); - relid | parentrelid | level | isleaf --------+-------------+-------+-------- -(0 rows) - -SELECT * FROM pg_partition_ancestors('ptif_normal_table'); - relid -------- -(0 rows) - -SELECT pg_partition_root('ptif_normal_table'); - pg_partition_root -------------------- - -(1 row) - -DROP TABLE ptif_normal_table; --- Various partitioning-related functions return empty/NULL if passed relations --- of types that cannot be part of a partition tree; for example, views, --- materialized views, legacy inheritance children or parents, etc. -CREATE VIEW ptif_test_view AS SELECT 1; -CREATE MATERIALIZED VIEW ptif_test_matview AS SELECT 1; -CREATE TABLE ptif_li_parent (); -CREATE TABLE ptif_li_child () INHERITS (ptif_li_parent); -SELECT * FROM pg_partition_tree('ptif_test_view'); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_tree('ptif_test_matview'); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_tree('ptif_li_parent'); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_tree('ptif_li_child'); - relid | parentrelid | isleaf | level --------+-------------+--------+------- -(0 rows) - -SELECT * FROM pg_partition_ancestors('ptif_test_view'); - relid -------- -(0 rows) - -SELECT * FROM pg_partition_ancestors('ptif_test_matview'); - relid -------- -(0 rows) - -SELECT * FROM pg_partition_ancestors('ptif_li_parent'); - relid -------- -(0 rows) - -SELECT * FROM pg_partition_ancestors('ptif_li_child'); - relid -------- -(0 rows) - -SELECT pg_partition_root('ptif_test_view'); - pg_partition_root -------------------- - -(1 row) - -SELECT pg_partition_root('ptif_test_matview'); - pg_partition_root -------------------- - -(1 row) - -SELECT pg_partition_root('ptif_li_parent'); - pg_partition_root -------------------- - -(1 row) - -SELECT pg_partition_root('ptif_li_child'); - pg_partition_root -------------------- - -(1 row) - -DROP VIEW ptif_test_view; -DROP MATERIALIZED VIEW ptif_test_matview; -DROP TABLE ptif_li_parent, ptif_li_child; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tuplesort.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tuplesort.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/tuplesort.out 2021-10-30 21:35:47 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/tuplesort.out 2021-10-30 22:09:26 -0600 @@ -1,686 +1,2 @@ --- only use parallelism when explicitly intending to do so -SET max_parallel_maintenance_workers = 0; -SET max_parallel_workers = 0; --- A table with contents that, when sorted, triggers abbreviated --- key aborts. One easy way to achieve that is to use uuids that all --- have the same prefix, as abbreviated keys for uuids just use the --- first sizeof(Datum) bytes. -CREATE TEMP TABLE abbrev_abort_uuids ( - id serial not null, - abort_increasing uuid, - abort_decreasing uuid, - noabort_increasing uuid, - noabort_decreasing uuid); -INSERT INTO abbrev_abort_uuids (abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing) - SELECT - ('00000000-0000-0000-0000-'||to_char(g.i, '000000000000FM'))::uuid abort_increasing, - ('00000000-0000-0000-0000-'||to_char(20000 - g.i, '000000000000FM'))::uuid abort_decreasing, - (to_char(g.i % 10009, '00000000FM')||'-0000-0000-0000-'||to_char(g.i, '000000000000FM'))::uuid noabort_increasing, - (to_char(((20000 - g.i) % 10009), '00000000FM')||'-0000-0000-0000-'||to_char(20000 - g.i, '000000000000FM'))::uuid noabort_decreasing - FROM generate_series(0, 20000, 1) g(i); --- and a few NULLs -INSERT INTO abbrev_abort_uuids(id) VALUES(0); -INSERT INTO abbrev_abort_uuids DEFAULT VALUES; -INSERT INTO abbrev_abort_uuids DEFAULT VALUES; --- add just a few duplicates -INSERT INTO abbrev_abort_uuids (abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing) - SELECT abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing - FROM abbrev_abort_uuids - WHERE (id < 10 OR id > 19990) AND id % 3 = 0 AND abort_increasing is not null; ----- --- Check sort node uses of tuplesort wrt. abbreviated keys ----- --- plain sort triggering abbreviated abort -SELECT abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_increasing OFFSET 20000 - 4; - abort_increasing | abort_decreasing ---------------------------------------+-------------------------------------- - 00000000-0000-0000-0000-000000019992 | 00000000-0000-0000-0000-000000000008 - 00000000-0000-0000-0000-000000019993 | 00000000-0000-0000-0000-000000000007 - 00000000-0000-0000-0000-000000019994 | 00000000-0000-0000-0000-000000000006 - 00000000-0000-0000-0000-000000019994 | 00000000-0000-0000-0000-000000000006 - 00000000-0000-0000-0000-000000019995 | 00000000-0000-0000-0000-000000000005 - 00000000-0000-0000-0000-000000019996 | 00000000-0000-0000-0000-000000000004 - 00000000-0000-0000-0000-000000019997 | 00000000-0000-0000-0000-000000000003 - 00000000-0000-0000-0000-000000019997 | 00000000-0000-0000-0000-000000000003 - 00000000-0000-0000-0000-000000019998 | 00000000-0000-0000-0000-000000000002 - 00000000-0000-0000-0000-000000019999 | 00000000-0000-0000-0000-000000000001 - 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - | - | - | -(15 rows) - -SELECT abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_decreasing NULLS FIRST OFFSET 20000 - 4; - abort_increasing | abort_decreasing ---------------------------------------+-------------------------------------- - 00000000-0000-0000-0000-000000000011 | 00000000-0000-0000-0000-000000019989 - 00000000-0000-0000-0000-000000000010 | 00000000-0000-0000-0000-000000019990 - 00000000-0000-0000-0000-000000000009 | 00000000-0000-0000-0000-000000019991 - 00000000-0000-0000-0000-000000000008 | 00000000-0000-0000-0000-000000019992 - 00000000-0000-0000-0000-000000000008 | 00000000-0000-0000-0000-000000019992 - 00000000-0000-0000-0000-000000000007 | 00000000-0000-0000-0000-000000019993 - 00000000-0000-0000-0000-000000000006 | 00000000-0000-0000-0000-000000019994 - 00000000-0000-0000-0000-000000000005 | 00000000-0000-0000-0000-000000019995 - 00000000-0000-0000-0000-000000000005 | 00000000-0000-0000-0000-000000019995 - 00000000-0000-0000-0000-000000000004 | 00000000-0000-0000-0000-000000019996 - 00000000-0000-0000-0000-000000000003 | 00000000-0000-0000-0000-000000019997 - 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 - 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 - 00000000-0000-0000-0000-000000000001 | 00000000-0000-0000-0000-000000019999 - 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000020000 -(15 rows) - --- plain sort not triggering abbreviated abort -SELECT noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_increasing OFFSET 20000 - 4; - noabort_increasing | noabort_decreasing ---------------------------------------+-------------------------------------- - 00009997-0000-0000-0000-000000009997 | 00010003-0000-0000-0000-000000010003 - 00009998-0000-0000-0000-000000009998 | 00010002-0000-0000-0000-000000010002 - 00009999-0000-0000-0000-000000009999 | 00010001-0000-0000-0000-000000010001 - 00010000-0000-0000-0000-000000010000 | 00010000-0000-0000-0000-000000010000 - 00010001-0000-0000-0000-000000010001 | 00009999-0000-0000-0000-000000009999 - 00010002-0000-0000-0000-000000010002 | 00009998-0000-0000-0000-000000009998 - 00010003-0000-0000-0000-000000010003 | 00009997-0000-0000-0000-000000009997 - 00010004-0000-0000-0000-000000010004 | 00009996-0000-0000-0000-000000009996 - 00010005-0000-0000-0000-000000010005 | 00009995-0000-0000-0000-000000009995 - 00010006-0000-0000-0000-000000010006 | 00009994-0000-0000-0000-000000009994 - 00010007-0000-0000-0000-000000010007 | 00009993-0000-0000-0000-000000009993 - 00010008-0000-0000-0000-000000010008 | 00009992-0000-0000-0000-000000009992 - | - | - | -(15 rows) - -SELECT noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing NULLS FIRST OFFSET 20000 - 4; - noabort_increasing | noabort_decreasing ---------------------------------------+-------------------------------------- - 00010006-0000-0000-0000-000000010006 | 00009994-0000-0000-0000-000000009994 - 00010005-0000-0000-0000-000000010005 | 00009995-0000-0000-0000-000000009995 - 00010004-0000-0000-0000-000000010004 | 00009996-0000-0000-0000-000000009996 - 00010003-0000-0000-0000-000000010003 | 00009997-0000-0000-0000-000000009997 - 00010002-0000-0000-0000-000000010002 | 00009998-0000-0000-0000-000000009998 - 00010001-0000-0000-0000-000000010001 | 00009999-0000-0000-0000-000000009999 - 00010000-0000-0000-0000-000000010000 | 00010000-0000-0000-0000-000000010000 - 00009999-0000-0000-0000-000000009999 | 00010001-0000-0000-0000-000000010001 - 00009998-0000-0000-0000-000000009998 | 00010002-0000-0000-0000-000000010002 - 00009997-0000-0000-0000-000000009997 | 00010003-0000-0000-0000-000000010003 - 00009996-0000-0000-0000-000000009996 | 00010004-0000-0000-0000-000000010004 - 00009995-0000-0000-0000-000000009995 | 00010005-0000-0000-0000-000000010005 - 00009994-0000-0000-0000-000000009994 | 00010006-0000-0000-0000-000000010006 - 00009993-0000-0000-0000-000000009993 | 00010007-0000-0000-0000-000000010007 - 00009992-0000-0000-0000-000000009992 | 00010008-0000-0000-0000-000000010008 -(15 rows) - --- bounded sort (disables abbreviated keys) -SELECT abort_increasing, noabort_increasing FROM abbrev_abort_uuids ORDER BY abort_increasing LIMIT 5; - abort_increasing | noabort_increasing ---------------------------------------+-------------------------------------- - 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000000000 - 00000000-0000-0000-0000-000000000001 | 00000001-0000-0000-0000-000000000001 - 00000000-0000-0000-0000-000000000002 | 00000002-0000-0000-0000-000000000002 - 00000000-0000-0000-0000-000000000002 | 00000002-0000-0000-0000-000000000002 - 00000000-0000-0000-0000-000000000003 | 00000003-0000-0000-0000-000000000003 -(5 rows) - -SELECT abort_increasing, noabort_increasing FROM abbrev_abort_uuids ORDER BY noabort_increasing NULLS FIRST LIMIT 5; - abort_increasing | noabort_increasing ---------------------------------------+-------------------------------------- - | - | - | - 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000000000 - 00000000-0000-0000-0000-000000010009 | 00000000-0000-0000-0000-000000010009 -(5 rows) - ----- --- Check index creation uses of tuplesort wrt. abbreviated keys ----- --- index creation using abbreviated keys successfully -CREATE INDEX abbrev_abort_uuids__noabort_increasing_idx ON abbrev_abort_uuids (noabort_increasing); -CREATE INDEX abbrev_abort_uuids__noabort_decreasing_idx ON abbrev_abort_uuids (noabort_decreasing); --- verify -EXPLAIN (COSTS OFF) -SELECT id, noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_increasing LIMIT 5; - QUERY PLAN ------------------------------------------------------------------------------------------ - Limit - -> Index Scan using abbrev_abort_uuids__noabort_increasing_idx on abbrev_abort_uuids -(2 rows) - -SELECT id, noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_increasing LIMIT 5; - id | noabort_increasing | noabort_decreasing --------+--------------------------------------+-------------------------------------- - 1 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 - 10010 | 00000000-0000-0000-0000-000000010009 | 00009991-0000-0000-0000-000000009991 - 2 | 00000001-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 - 10011 | 00000001-0000-0000-0000-000000010010 | 00009990-0000-0000-0000-000000009990 - 3 | 00000002-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 -(5 rows) - -EXPLAIN (COSTS OFF) -SELECT id, noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing LIMIT 5; - QUERY PLAN ------------------------------------------------------------------------------------------ - Limit - -> Index Scan using abbrev_abort_uuids__noabort_decreasing_idx on abbrev_abort_uuids -(2 rows) - -SELECT id, noabort_increasing, noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing LIMIT 5; - id | noabort_increasing | noabort_decreasing --------+--------------------------------------+-------------------------------------- - 20001 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20010 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 9992 | 00009991-0000-0000-0000-000000009991 | 00000000-0000-0000-0000-000000010009 - 20000 | 00009990-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 - 9991 | 00009990-0000-0000-0000-000000009990 | 00000001-0000-0000-0000-000000010010 -(5 rows) - --- index creation using abbreviated keys, hitting abort -CREATE INDEX abbrev_abort_uuids__abort_increasing_idx ON abbrev_abort_uuids (abort_increasing); -CREATE INDEX abbrev_abort_uuids__abort_decreasing_idx ON abbrev_abort_uuids (abort_decreasing); --- verify -EXPLAIN (COSTS OFF) -SELECT id, abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_increasing LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------------------------- - Limit - -> Index Scan using abbrev_abort_uuids__abort_increasing_idx on abbrev_abort_uuids -(2 rows) - -SELECT id, abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_increasing LIMIT 5; - id | abort_increasing | abort_decreasing --------+--------------------------------------+-------------------------------------- - 1 | 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000020000 - 2 | 00000000-0000-0000-0000-000000000001 | 00000000-0000-0000-0000-000000019999 - 3 | 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 - 20004 | 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 - 4 | 00000000-0000-0000-0000-000000000003 | 00000000-0000-0000-0000-000000019997 -(5 rows) - -EXPLAIN (COSTS OFF) -SELECT id, abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_decreasing LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------------------------- - Limit - -> Index Scan using abbrev_abort_uuids__abort_decreasing_idx on abbrev_abort_uuids -(2 rows) - -SELECT id, abort_increasing, abort_decreasing FROM abbrev_abort_uuids ORDER BY abort_decreasing LIMIT 5; - id | abort_increasing | abort_decreasing --------+--------------------------------------+-------------------------------------- - 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20000 | 00000000-0000-0000-0000-000000019999 | 00000000-0000-0000-0000-000000000001 - 19999 | 00000000-0000-0000-0000-000000019998 | 00000000-0000-0000-0000-000000000002 - 19998 | 00000000-0000-0000-0000-000000019997 | 00000000-0000-0000-0000-000000000003 -(5 rows) - ----- --- Check CLUSTER uses of tuplesort wrt. abbreviated keys ----- --- when aborting, increasing order -BEGIN; -SET LOCAL enable_indexscan = false; -CLUSTER abbrev_abort_uuids USING abbrev_abort_uuids__abort_increasing_idx; --- head -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 1 | 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 - 2 | 00000000-0000-0000-0000-000000000001 | 00000000-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 - 3 | 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 | 00000002-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 - 20004 | 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 | 00000002-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 - 4 | 00000000-0000-0000-0000-000000000003 | 00000000-0000-0000-0000-000000019997 | 00000003-0000-0000-0000-000000000003 | 00009988-0000-0000-0000-000000019997 -(5 rows) - --- tail -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid DESC LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 0 | | | | - 20002 | | | | - 20003 | | | | - 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 -(5 rows) - -ROLLBACK; --- when aborting, decreasing order -BEGIN; -SET LOCAL enable_indexscan = false; -CLUSTER abbrev_abort_uuids USING abbrev_abort_uuids__abort_decreasing_idx; --- head -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20000 | 00000000-0000-0000-0000-000000019999 | 00000000-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 - 19999 | 00000000-0000-0000-0000-000000019998 | 00000000-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 | 00000002-0000-0000-0000-000000000002 - 20009 | 00000000-0000-0000-0000-000000019997 | 00000000-0000-0000-0000-000000000003 | 00009988-0000-0000-0000-000000019997 | 00000003-0000-0000-0000-000000000003 -(5 rows) - --- tail -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid DESC LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 0 | | | | - 20002 | | | | - 20003 | | | | - 1 | 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 - 2 | 00000000-0000-0000-0000-000000000001 | 00000000-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 -(5 rows) - -ROLLBACK; --- when not aborting, increasing order -BEGIN; -SET LOCAL enable_indexscan = false; -CLUSTER abbrev_abort_uuids USING abbrev_abort_uuids__noabort_increasing_idx; --- head -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 1 | 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 - 10010 | 00000000-0000-0000-0000-000000010009 | 00000000-0000-0000-0000-000000009991 | 00000000-0000-0000-0000-000000010009 | 00009991-0000-0000-0000-000000009991 - 2 | 00000000-0000-0000-0000-000000000001 | 00000000-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 - 10011 | 00000000-0000-0000-0000-000000010010 | 00000000-0000-0000-0000-000000009990 | 00000001-0000-0000-0000-000000010010 | 00009990-0000-0000-0000-000000009990 - 20004 | 00000000-0000-0000-0000-000000000002 | 00000000-0000-0000-0000-000000019998 | 00000002-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 -(5 rows) - --- tail -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid DESC LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 0 | | | | - 20002 | | | | - 20003 | | | | - 10009 | 00000000-0000-0000-0000-000000010008 | 00000000-0000-0000-0000-000000009992 | 00010008-0000-0000-0000-000000010008 | 00009992-0000-0000-0000-000000009992 - 10008 | 00000000-0000-0000-0000-000000010007 | 00000000-0000-0000-0000-000000009993 | 00010007-0000-0000-0000-000000010007 | 00009993-0000-0000-0000-000000009993 -(5 rows) - -ROLLBACK; --- when no aborting, decreasing order -BEGIN; -SET LOCAL enable_indexscan = false; -CLUSTER abbrev_abort_uuids USING abbrev_abort_uuids__noabort_decreasing_idx; --- head -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 - 9992 | 00000000-0000-0000-0000-000000009991 | 00000000-0000-0000-0000-000000010009 | 00009991-0000-0000-0000-000000009991 | 00000000-0000-0000-0000-000000010009 - 20000 | 00000000-0000-0000-0000-000000019999 | 00000000-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001 - 9991 | 00000000-0000-0000-0000-000000009990 | 00000000-0000-0000-0000-000000010010 | 00009990-0000-0000-0000-000000009990 | 00000001-0000-0000-0000-000000010010 -(5 rows) - --- tail -SELECT id, abort_increasing, abort_decreasing, noabort_increasing, noabort_decreasing -FROM abbrev_abort_uuids -ORDER BY ctid DESC LIMIT 5; - id | abort_increasing | abort_decreasing | noabort_increasing | noabort_decreasing --------+--------------------------------------+--------------------------------------+--------------------------------------+-------------------------------------- - 0 | | | | - 20003 | | | | - 20002 | | | | - 9993 | 00000000-0000-0000-0000-000000009992 | 00000000-0000-0000-0000-000000010008 | 00009992-0000-0000-0000-000000009992 | 00010008-0000-0000-0000-000000010008 - 9994 | 00000000-0000-0000-0000-000000009993 | 00000000-0000-0000-0000-000000010007 | 00009993-0000-0000-0000-000000009993 | 00010007-0000-0000-0000-000000010007 -(5 rows) - -ROLLBACK; ----- --- test forward and backward scans for in-memory and disk based tuplesort ----- --- in-memory -BEGIN; -SET LOCAL enable_indexscan = false; --- unfortunately can't show analyze output confirming sort method, --- the memory used output wouldn't be stable -EXPLAIN (COSTS OFF) DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing; - QUERY PLAN --------------------------------------- - Sort - Sort Key: noabort_decreasing - -> Seq Scan on abbrev_abort_uuids -(3 rows) - -DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing; --- first and second -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - --- scroll beyond beginning -FETCH BACKWARD FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - --- scroll beyond end end -FETCH LAST FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -COMMIT; --- disk based -BEGIN; -SET LOCAL enable_indexscan = false; -SET LOCAL work_mem = '100kB'; --- unfortunately can't show analyze output confirming sort method, --- the memory used output wouldn't be stable -EXPLAIN (COSTS OFF) DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing; - QUERY PLAN --------------------------------------- - Sort - Sort Key: noabort_decreasing - -> Seq Scan on abbrev_abort_uuids -(3 rows) - -DECLARE c SCROLL CURSOR FOR SELECT noabort_decreasing FROM abbrev_abort_uuids ORDER BY noabort_decreasing; --- first and second -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - --- scroll beyond beginning -FETCH BACKWARD FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH NEXT FROM c; - noabort_decreasing --------------------------------------- - 00000000-0000-0000-0000-000000000000 -(1 row) - --- scroll beyond end end -FETCH LAST FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -FETCH BACKWARD FROM c; - noabort_decreasing --------------------- - -(1 row) - -FETCH NEXT FROM c; - noabort_decreasing --------------------- -(0 rows) - -COMMIT; ----- --- test tuplesort using both in-memory and disk sort ---- --- memory based -SELECT - -- fixed-width by-value datum - (array_agg(id ORDER BY id DESC NULLS FIRST))[0:5], - -- fixed-width by-ref datum - (array_agg(abort_increasing ORDER BY abort_increasing DESC NULLS LAST))[0:5], - -- variable-width datum - (array_agg(id::text ORDER BY id::text DESC NULLS LAST))[0:5], - -- fixed width by-value datum tuplesort - percentile_disc(0.99) WITHIN GROUP (ORDER BY id), - -- ensure state is shared - percentile_disc(0.01) WITHIN GROUP (ORDER BY id), - -- fixed width by-ref datum tuplesort - percentile_disc(0.8) WITHIN GROUP (ORDER BY abort_increasing), - -- variable width by-ref datum tuplesort - percentile_disc(0.2) WITHIN GROUP (ORDER BY id::text), - -- multi-column tuplesort - rank('00000000-0000-0000-0000-000000000000', '2', '2') WITHIN GROUP (ORDER BY noabort_increasing, id, id::text) -FROM ( - SELECT * FROM abbrev_abort_uuids - UNION ALL - SELECT NULL, NULL, NULL, NULL, NULL) s; - array_agg | array_agg | array_agg | percentile_disc | percentile_disc | percentile_disc | percentile_disc | rank ---------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+-----------------+-----------------+--------------------------------------+-----------------+------ - {NULL,20010,20009,20008,20007} | {00000000-0000-0000-0000-000000020000,00000000-0000-0000-0000-000000020000,00000000-0000-0000-0000-000000019999,00000000-0000-0000-0000-000000019998,00000000-0000-0000-0000-000000019997} | {9999,9998,9997,9996,9995} | 19810 | 200 | 00000000-0000-0000-0000-000000016003 | 136 | 2 -(1 row) - --- disk based (see also above) -BEGIN; -SET LOCAL work_mem = '100kB'; -SELECT - (array_agg(id ORDER BY id DESC NULLS FIRST))[0:5], - (array_agg(abort_increasing ORDER BY abort_increasing DESC NULLS LAST))[0:5], - (array_agg(id::text ORDER BY id::text DESC NULLS LAST))[0:5], - percentile_disc(0.99) WITHIN GROUP (ORDER BY id), - percentile_disc(0.01) WITHIN GROUP (ORDER BY id), - percentile_disc(0.8) WITHIN GROUP (ORDER BY abort_increasing), - percentile_disc(0.2) WITHIN GROUP (ORDER BY id::text), - rank('00000000-0000-0000-0000-000000000000', '2', '2') WITHIN GROUP (ORDER BY noabort_increasing, id, id::text) -FROM ( - SELECT * FROM abbrev_abort_uuids - UNION ALL - SELECT NULL, NULL, NULL, NULL, NULL) s; - array_agg | array_agg | array_agg | percentile_disc | percentile_disc | percentile_disc | percentile_disc | rank ---------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+-----------------+-----------------+--------------------------------------+-----------------+------ - {NULL,20010,20009,20008,20007} | {00000000-0000-0000-0000-000000020000,00000000-0000-0000-0000-000000020000,00000000-0000-0000-0000-000000019999,00000000-0000-0000-0000-000000019998,00000000-0000-0000-0000-000000019997} | {9999,9998,9997,9996,9995} | 19810 | 200 | 00000000-0000-0000-0000-000000016003 | 136 | 2 -(1 row) - -ROLLBACK; ----- --- test tuplesort mark/restore ---- -CREATE TEMP TABLE test_mark_restore(col1 int, col2 int, col12 int); --- need a few duplicates for mark/restore to matter -INSERT INTO test_mark_restore(col1, col2, col12) - SELECT a.i, b.i, a.i * b.i FROM generate_series(1, 500) a(i), generate_series(1, 5) b(i); -BEGIN; -SET LOCAL enable_nestloop = off; -SET LOCAL enable_hashjoin = off; -SET LOCAL enable_material = off; --- set query into variable once, to avoid repetition of the fairly long query -SELECT $$ - SELECT col12, count(distinct a.col1), count(distinct a.col2), count(distinct b.col1), count(distinct b.col2), count(*) - FROM test_mark_restore a - JOIN test_mark_restore b USING(col12) - GROUP BY 1 - HAVING count(*) > 1 - ORDER BY 2 DESC, 1 DESC, 3 DESC, 4 DESC, 5 DESC, 6 DESC - LIMIT 10 -$$ AS qry \gset --- test mark/restore with in-memory sorts -EXPLAIN (COSTS OFF) :qry; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Limit - -> Sort - Sort Key: (count(DISTINCT a.col1)) DESC, a.col12 DESC, (count(DISTINCT a.col2)) DESC, (count(DISTINCT b.col1)) DESC, (count(DISTINCT b.col2)) DESC, (count(*)) DESC - -> GroupAggregate - Group Key: a.col12 - Filter: (count(*) > 1) - -> Merge Join - Merge Cond: (a.col12 = b.col12) - -> Sort - Sort Key: a.col12 DESC - -> Seq Scan on test_mark_restore a - -> Sort - Sort Key: b.col12 DESC - -> Seq Scan on test_mark_restore b -(14 rows) - -:qry; - col12 | count | count | count | count | count --------+-------+-------+-------+-------+------- - 480 | 5 | 5 | 5 | 5 | 25 - 420 | 5 | 5 | 5 | 5 | 25 - 360 | 5 | 5 | 5 | 5 | 25 - 300 | 5 | 5 | 5 | 5 | 25 - 240 | 5 | 5 | 5 | 5 | 25 - 180 | 5 | 5 | 5 | 5 | 25 - 120 | 5 | 5 | 5 | 5 | 25 - 60 | 5 | 5 | 5 | 5 | 25 - 960 | 4 | 4 | 4 | 4 | 16 - 900 | 4 | 4 | 4 | 4 | 16 -(10 rows) - --- test mark/restore with on-disk sorts -SET LOCAL work_mem = '100kB'; -EXPLAIN (COSTS OFF) :qry; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Limit - -> Sort - Sort Key: (count(DISTINCT a.col1)) DESC, a.col12 DESC, (count(DISTINCT a.col2)) DESC, (count(DISTINCT b.col1)) DESC, (count(DISTINCT b.col2)) DESC, (count(*)) DESC - -> GroupAggregate - Group Key: a.col12 - Filter: (count(*) > 1) - -> Merge Join - Merge Cond: (a.col12 = b.col12) - -> Sort - Sort Key: a.col12 DESC - -> Seq Scan on test_mark_restore a - -> Sort - Sort Key: b.col12 DESC - -> Seq Scan on test_mark_restore b -(14 rows) - -:qry; - col12 | count | count | count | count | count --------+-------+-------+-------+-------+------- - 480 | 5 | 5 | 5 | 5 | 25 - 420 | 5 | 5 | 5 | 5 | 25 - 360 | 5 | 5 | 5 | 5 | 25 - 300 | 5 | 5 | 5 | 5 | 25 - 240 | 5 | 5 | 5 | 5 | 25 - 180 | 5 | 5 | 5 | 5 | 25 - 120 | 5 | 5 | 5 | 5 | 25 - 60 | 5 | 5 | 5 | 5 | 25 - 960 | 4 | 4 | 4 | 4 | 16 - 900 | 4 | 4 | 4 | 4 | 16 -(10 rows) - -COMMIT; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/explain.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/explain.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/explain.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/explain.out 2021-10-30 22:09:26 -0600 @@ -1,504 +1,2 @@ --- --- EXPLAIN --- --- There are many test cases elsewhere that use EXPLAIN as a vehicle for --- checking something else (usually planner behavior). This file is --- concerned with testing EXPLAIN in its own right. --- --- To produce stable regression test output, it's usually necessary to --- ignore details such as exact costs or row counts. These filter --- functions replace changeable output details with fixed strings. -create function explain_filter(text) returns setof text -language plpgsql as -$$ -declare - ln text; -begin - for ln in execute $1 - loop - -- Replace any numeric word with just 'N' - ln := regexp_replace(ln, '-?\m\d+\M', 'N', 'g'); - -- In sort output, the above won't match units-suffixed numbers - ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g'); - -- Ignore text-mode buffers output because it varies depending - -- on the system state - CONTINUE WHEN (ln ~ ' +Buffers: .*'); - -- Ignore text-mode "Planning:" line because whether it's output - -- varies depending on the system state - CONTINUE WHEN (ln = 'Planning:'); - return next ln; - end loop; -end; -$$; --- To produce valid JSON output, replace numbers with "0" or "0.0" not "N" -create function explain_filter_to_json(text) returns jsonb -language plpgsql as -$$ -declare - data text := ''; - ln text; -begin - for ln in execute $1 - loop - -- Replace any numeric word with just '0' - ln := regexp_replace(ln, '\m\d+\M', '0', 'g'); - data := data || ln; - end loop; - return data::jsonb; -end; -$$; --- Also, disable JIT, or we'll get different output on machines --- where that's been forced on -set jit = off; --- Simple cases -select explain_filter('explain select * from int8_tbl i8'); - explain_filter ---------------------------------------------------------- - Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) -(1 row) - -select explain_filter('explain (analyze) select * from int8_tbl i8'); - explain_filter ------------------------------------------------------------------------------------------------ - Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N) - Planning Time: N.N ms - Execution Time: N.N ms -(3 rows) - -select explain_filter('explain (analyze, verbose) select * from int8_tbl i8'); - explain_filter ------------------------------------------------------------------------------------------------------- - Seq Scan on public.int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N) - Output: q1, q2 - Planning Time: N.N ms - Execution Time: N.N ms -(4 rows) - -select explain_filter('explain (analyze, buffers, format text) select * from int8_tbl i8'); - explain_filter ------------------------------------------------------------------------------------------------ - Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N) - Planning Time: N.N ms - Execution Time: N.N ms -(3 rows) - -select explain_filter('explain (analyze, buffers, format json) select * from int8_tbl i8'); - explain_filter ------------------------------------- - [ + - { + - "Plan": { + - "Node Type": "Seq Scan", + - "Parallel Aware": false, + - "Async Capable": false, + - "Relation Name": "int8_tbl",+ - "Alias": "i8", + - "Startup Cost": N.N, + - "Total Cost": N.N, + - "Plan Rows": N, + - "Plan Width": N, + - "Actual Startup Time": N.N, + - "Actual Total Time": N.N, + - "Actual Rows": N, + - "Actual Loops": N, + - "Shared Hit Blocks": N, + - "Shared Read Blocks": N, + - "Shared Dirtied Blocks": N, + - "Shared Written Blocks": N, + - "Local Hit Blocks": N, + - "Local Read Blocks": N, + - "Local Dirtied Blocks": N, + - "Local Written Blocks": N, + - "Temp Read Blocks": N, + - "Temp Written Blocks": N + - }, + - "Planning": { + - "Shared Hit Blocks": N, + - "Shared Read Blocks": N, + - "Shared Dirtied Blocks": N, + - "Shared Written Blocks": N, + - "Local Hit Blocks": N, + - "Local Read Blocks": N, + - "Local Dirtied Blocks": N, + - "Local Written Blocks": N, + - "Temp Read Blocks": N, + - "Temp Written Blocks": N + - }, + - "Planning Time": N.N, + - "Triggers": [ + - ], + - "Execution Time": N.N + - } + - ] -(1 row) - -select explain_filter('explain (analyze, buffers, format xml) select * from int8_tbl i8'); - explain_filter --------------------------------------------------------- - + - + - + - Seq Scan + - false + - false + - int8_tbl + - i8 + - N.N + - N.N + - N + - N + - N.N + - N.N + - N + - N + - N + - N + - N+ - N+ - N + - N + - N + - N + - N + - N + - + - + - N + - N + - N+ - N+ - N + - N + - N + - N + - N + - N + - + - N.N + - + - + - N.N + - + - -(1 row) - -select explain_filter('explain (analyze, buffers, format yaml) select * from int8_tbl i8'); - explain_filter -------------------------------- - - Plan: + - Node Type: "Seq Scan" + - Parallel Aware: false + - Async Capable: false + - Relation Name: "int8_tbl"+ - Alias: "i8" + - Startup Cost: N.N + - Total Cost: N.N + - Plan Rows: N + - Plan Width: N + - Actual Startup Time: N.N + - Actual Total Time: N.N + - Actual Rows: N + - Actual Loops: N + - Shared Hit Blocks: N + - Shared Read Blocks: N + - Shared Dirtied Blocks: N + - Shared Written Blocks: N + - Local Hit Blocks: N + - Local Read Blocks: N + - Local Dirtied Blocks: N + - Local Written Blocks: N + - Temp Read Blocks: N + - Temp Written Blocks: N + - Planning: + - Shared Hit Blocks: N + - Shared Read Blocks: N + - Shared Dirtied Blocks: N + - Shared Written Blocks: N + - Local Hit Blocks: N + - Local Read Blocks: N + - Local Dirtied Blocks: N + - Local Written Blocks: N + - Temp Read Blocks: N + - Temp Written Blocks: N + - Planning Time: N.N + - Triggers: + - Execution Time: N.N -(1 row) - -select explain_filter('explain (buffers, format text) select * from int8_tbl i8'); - explain_filter ---------------------------------------------------------- - Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) -(1 row) - -select explain_filter('explain (buffers, format json) select * from int8_tbl i8'); - explain_filter ------------------------------------- - [ + - { + - "Plan": { + - "Node Type": "Seq Scan", + - "Parallel Aware": false, + - "Async Capable": false, + - "Relation Name": "int8_tbl",+ - "Alias": "i8", + - "Startup Cost": N.N, + - "Total Cost": N.N, + - "Plan Rows": N, + - "Plan Width": N, + - "Shared Hit Blocks": N, + - "Shared Read Blocks": N, + - "Shared Dirtied Blocks": N, + - "Shared Written Blocks": N, + - "Local Hit Blocks": N, + - "Local Read Blocks": N, + - "Local Dirtied Blocks": N, + - "Local Written Blocks": N, + - "Temp Read Blocks": N, + - "Temp Written Blocks": N + - }, + - "Planning": { + - "Shared Hit Blocks": N, + - "Shared Read Blocks": N, + - "Shared Dirtied Blocks": N, + - "Shared Written Blocks": N, + - "Local Hit Blocks": N, + - "Local Read Blocks": N, + - "Local Dirtied Blocks": N, + - "Local Written Blocks": N, + - "Temp Read Blocks": N, + - "Temp Written Blocks": N + - } + - } + - ] -(1 row) - --- SETTINGS option --- We have to ignore other settings that might be imposed by the environment, --- so printing the whole Settings field unfortunately won't do. -begin; -set local plan_cache_mode = force_generic_plan; -select true as "OK" - from explain_filter('explain (settings) select * from int8_tbl i8') ln - where ln ~ '^ *Settings: .*plan_cache_mode = ''force_generic_plan'''; - OK ----- - t -(1 row) - -select explain_filter_to_json('explain (settings, format json) select * from int8_tbl i8') #> '{0,Settings,plan_cache_mode}'; - ?column? ----------------------- - "force_generic_plan" -(1 row) - -rollback; --- --- Test production of per-worker data --- --- Unfortunately, because we don't know how many worker processes we'll --- actually get (maybe none at all), we can't examine the "Workers" output --- in any detail. We can check that it parses correctly as JSON, and then --- remove it from the displayed results. -begin; --- encourage use of parallel plans -set parallel_setup_cost=0; -set parallel_tuple_cost=0; -set min_parallel_table_scan_size=0; -set max_parallel_workers_per_gather=4; -select jsonb_pretty( - explain_filter_to_json('explain (analyze, verbose, buffers, format json) - select * from tenk1 order by tenthous') - -- remove "Workers" node of the Seq Scan plan node - #- '{0,Plan,Plans,0,Plans,0,Workers}' - -- remove "Workers" node of the Sort plan node - #- '{0,Plan,Plans,0,Workers}' - -- Also remove its sort-type fields, as those aren't 100% stable - #- '{0,Plan,Plans,0,Sort Method}' - #- '{0,Plan,Plans,0,Sort Space Type}' -); - jsonb_pretty -------------------------------------------------------------- - [ + - { + - "Plan": { + - "Plans": [ + - { + - "Plans": [ + - { + - "Alias": "tenk1", + - "Output": [ + - "unique1", + - "unique2", + - "two", + - "four", + - "ten", + - "twenty", + - "hundred", + - "thousand", + - "twothousand", + - "fivethous", + - "tenthous", + - "odd", + - "even", + - "stringu1", + - "stringu2", + - "string4" + - ], + - "Schema": "public", + - "Node Type": "Seq Scan", + - "Plan Rows": 0, + - "Plan Width": 0, + - "Total Cost": 0.0, + - "Actual Rows": 0, + - "Actual Loops": 0, + - "Startup Cost": 0.0, + - "Async Capable": false, + - "Relation Name": "tenk1", + - "Parallel Aware": true, + - "Local Hit Blocks": 0, + - "Temp Read Blocks": 0, + - "Actual Total Time": 0.0, + - "Local Read Blocks": 0, + - "Shared Hit Blocks": 0, + - "Shared Read Blocks": 0, + - "Actual Startup Time": 0.0, + - "Parent Relationship": "Outer",+ - "Temp Written Blocks": 0, + - "Local Dirtied Blocks": 0, + - "Local Written Blocks": 0, + - "Shared Dirtied Blocks": 0, + - "Shared Written Blocks": 0 + - } + - ], + - "Output": [ + - "unique1", + - "unique2", + - "two", + - "four", + - "ten", + - "twenty", + - "hundred", + - "thousand", + - "twothousand", + - "fivethous", + - "tenthous", + - "odd", + - "even", + - "stringu1", + - "stringu2", + - "string4" + - ], + - "Sort Key": [ + - "tenk1.tenthous" + - ], + - "Node Type": "Sort", + - "Plan Rows": 0, + - "Plan Width": 0, + - "Total Cost": 0.0, + - "Actual Rows": 0, + - "Actual Loops": 0, + - "Startup Cost": 0.0, + - "Async Capable": false, + - "Parallel Aware": false, + - "Sort Space Used": 0, + - "Local Hit Blocks": 0, + - "Temp Read Blocks": 0, + - "Actual Total Time": 0.0, + - "Local Read Blocks": 0, + - "Shared Hit Blocks": 0, + - "Shared Read Blocks": 0, + - "Actual Startup Time": 0.0, + - "Parent Relationship": "Outer", + - "Temp Written Blocks": 0, + - "Local Dirtied Blocks": 0, + - "Local Written Blocks": 0, + - "Shared Dirtied Blocks": 0, + - "Shared Written Blocks": 0 + - } + - ], + - "Output": [ + - "unique1", + - "unique2", + - "two", + - "four", + - "ten", + - "twenty", + - "hundred", + - "thousand", + - "twothousand", + - "fivethous", + - "tenthous", + - "odd", + - "even", + - "stringu1", + - "stringu2", + - "string4" + - ], + - "Node Type": "Gather Merge", + - "Plan Rows": 0, + - "Plan Width": 0, + - "Total Cost": 0.0, + - "Actual Rows": 0, + - "Actual Loops": 0, + - "Startup Cost": 0.0, + - "Async Capable": false, + - "Parallel Aware": false, + - "Workers Planned": 0, + - "Local Hit Blocks": 0, + - "Temp Read Blocks": 0, + - "Workers Launched": 0, + - "Actual Total Time": 0.0, + - "Local Read Blocks": 0, + - "Shared Hit Blocks": 0, + - "Shared Read Blocks": 0, + - "Actual Startup Time": 0.0, + - "Temp Written Blocks": 0, + - "Local Dirtied Blocks": 0, + - "Local Written Blocks": 0, + - "Shared Dirtied Blocks": 0, + - "Shared Written Blocks": 0 + - }, + - "Planning": { + - "Local Hit Blocks": 0, + - "Temp Read Blocks": 0, + - "Local Read Blocks": 0, + - "Shared Hit Blocks": 0, + - "Shared Read Blocks": 0, + - "Temp Written Blocks": 0, + - "Local Dirtied Blocks": 0, + - "Local Written Blocks": 0, + - "Shared Dirtied Blocks": 0, + - "Shared Written Blocks": 0 + - }, + - "Triggers": [ + - ], + - "Planning Time": 0.0, + - "Execution Time": 0.0 + - } + - ] -(1 row) - -rollback; --- Test display of temporary objects -create temp table t1(f1 float8); -create function pg_temp.mysin(float8) returns float8 language plpgsql -as 'begin return sin($1); end'; -select explain_filter('explain (verbose) select * from t1 where pg_temp.mysin(f1) < 0.5'); - explain_filter ------------------------------------------------------------- - Seq Scan on pg_temp.t1 (cost=N.N..N.N rows=N width=N) - Output: f1 - Filter: (pg_temp.mysin(t1.f1) < 'N.N'::double precision) -(3 rows) - --- Test compute_query_id -set compute_query_id = on; -select explain_filter('explain (verbose) select * from int8_tbl i8'); - explain_filter ----------------------------------------------------------------- - Seq Scan on public.int8_tbl i8 (cost=N.N..N.N rows=N width=N) - Output: q1, q2 - Query Identifier: N -(3 rows) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/compression_1.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/compression.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/compression_1.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/compression.out 2021-10-30 22:09:26 -0600 @@ -1,361 +1,2 @@ -\set HIDE_TOAST_COMPRESSION false --- ensure we get stable results regardless of installation's default -SET default_toast_compression = 'pglz'; --- test creating table with compression method -CREATE TABLE cmdata(f1 text COMPRESSION pglz); -CREATE INDEX idx ON cmdata(f1); -INSERT INTO cmdata VALUES(repeat('1234567890', 1000)); -\d+ cmdata - Table "public.cmdata" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+------+-----------+----------+---------+----------+-------------+--------------+------------- - f1 | text | | | | extended | pglz | | -Indexes: - "idx" btree (f1) - -CREATE TABLE cmdata1(f1 TEXT COMPRESSION lz4); -ERROR: compression method lz4 not supported -DETAIL: This functionality requires the server to be built with lz4 support. -HINT: You need to rebuild PostgreSQL using --with-lz4. -INSERT INTO cmdata1 VALUES(repeat('1234567890', 1004)); -ERROR: relation "cmdata1" does not exist -LINE 1: INSERT INTO cmdata1 VALUES(repeat('1234567890', 1004)); - ^ -\d+ cmdata1 --- verify stored compression method in the data -SELECT pg_column_compression(f1) FROM cmdata; - pg_column_compression ------------------------ - pglz -(1 row) - -SELECT pg_column_compression(f1) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT pg_column_compression(f1) FROM cmdata1; - ^ --- decompress data slice -SELECT SUBSTR(f1, 200, 5) FROM cmdata; - substr --------- - 01234 -(1 row) - -SELECT SUBSTR(f1, 2000, 50) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT SUBSTR(f1, 2000, 50) FROM cmdata1; - ^ --- copy with table creation -SELECT * INTO cmmove1 FROM cmdata; -\d+ cmmove1 - Table "public.cmmove1" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+------+-----------+----------+---------+----------+-------------+--------------+------------- - f1 | text | | | | extended | | | - -SELECT pg_column_compression(f1) FROM cmmove1; - pg_column_compression ------------------------ - pglz -(1 row) - --- copy to existing table -CREATE TABLE cmmove3(f1 text COMPRESSION pglz); -INSERT INTO cmmove3 SELECT * FROM cmdata; -INSERT INTO cmmove3 SELECT * FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: INSERT INTO cmmove3 SELECT * FROM cmdata1; - ^ -SELECT pg_column_compression(f1) FROM cmmove3; - pg_column_compression ------------------------ - pglz -(1 row) - --- test LIKE INCLUDING COMPRESSION -CREATE TABLE cmdata2 (LIKE cmdata1 INCLUDING COMPRESSION); -ERROR: relation "cmdata1" does not exist -LINE 1: CREATE TABLE cmdata2 (LIKE cmdata1 INCLUDING COMPRESSION); - ^ -\d+ cmdata2 -DROP TABLE cmdata2; -ERROR: table "cmdata2" does not exist --- try setting compression for incompressible data type -CREATE TABLE cmdata2 (f1 int COMPRESSION pglz); -ERROR: column data type integer does not support compression --- update using datum from different table -CREATE TABLE cmmove2(f1 text COMPRESSION pglz); -INSERT INTO cmmove2 VALUES (repeat('1234567890', 1004)); -SELECT pg_column_compression(f1) FROM cmmove2; - pg_column_compression ------------------------ - pglz -(1 row) - -UPDATE cmmove2 SET f1 = cmdata1.f1 FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: UPDATE cmmove2 SET f1 = cmdata1.f1 FROM cmdata1; - ^ -SELECT pg_column_compression(f1) FROM cmmove2; - pg_column_compression ------------------------ - pglz -(1 row) - --- test externally stored compressed data -CREATE OR REPLACE FUNCTION large_val() RETURNS TEXT LANGUAGE SQL AS -'select array_agg(md5(g::text))::text from generate_series(1, 256) g'; -CREATE TABLE cmdata2 (f1 text COMPRESSION pglz); -INSERT INTO cmdata2 SELECT large_val() || repeat('a', 4000); -SELECT pg_column_compression(f1) FROM cmdata2; - pg_column_compression ------------------------ - pglz -(1 row) - -INSERT INTO cmdata1 SELECT large_val() || repeat('a', 4000); -ERROR: relation "cmdata1" does not exist -LINE 1: INSERT INTO cmdata1 SELECT large_val() || repeat('a', 4000); - ^ -SELECT pg_column_compression(f1) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT pg_column_compression(f1) FROM cmdata1; - ^ -SELECT SUBSTR(f1, 200, 5) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT SUBSTR(f1, 200, 5) FROM cmdata1; - ^ -SELECT SUBSTR(f1, 200, 5) FROM cmdata2; - substr --------- - 8f14e -(1 row) - -DROP TABLE cmdata2; ---test column type update varlena/non-varlena -CREATE TABLE cmdata2 (f1 int); -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+---------+-----------+----------+---------+---------+-------------+--------------+------------- - f1 | integer | | | | plain | | | - -ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE varchar; -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+-------------------+-----------+----------+---------+----------+-------------+--------------+------------- - f1 | character varying | | | | extended | | | - -ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE int USING f1::integer; -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+---------+-----------+----------+---------+---------+-------------+--------------+------------- - f1 | integer | | | | plain | | | - ---changing column storage should not impact the compression method ---but the data should not be compressed -ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE varchar; -ALTER TABLE cmdata2 ALTER COLUMN f1 SET COMPRESSION pglz; -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+-------------------+-----------+----------+---------+----------+-------------+--------------+------------- - f1 | character varying | | | | extended | pglz | | - -ALTER TABLE cmdata2 ALTER COLUMN f1 SET STORAGE plain; -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+-------------------+-----------+----------+---------+---------+-------------+--------------+------------- - f1 | character varying | | | | plain | pglz | | - -INSERT INTO cmdata2 VALUES (repeat('123456789', 800)); -SELECT pg_column_compression(f1) FROM cmdata2; - pg_column_compression ------------------------ - -(1 row) - --- test compression with materialized view -CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: ...TE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; - ^ -\d+ compressmv -SELECT pg_column_compression(f1) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT pg_column_compression(f1) FROM cmdata1; - ^ -SELECT pg_column_compression(x) FROM compressmv; -ERROR: relation "compressmv" does not exist -LINE 1: SELECT pg_column_compression(x) FROM compressmv; - ^ --- test compression with partition -CREATE TABLE cmpart(f1 text COMPRESSION lz4) PARTITION BY HASH(f1); -ERROR: compression method lz4 not supported -DETAIL: This functionality requires the server to be built with lz4 support. -HINT: You need to rebuild PostgreSQL using --with-lz4. -CREATE TABLE cmpart1 PARTITION OF cmpart FOR VALUES WITH (MODULUS 2, REMAINDER 0); -ERROR: relation "cmpart" does not exist -CREATE TABLE cmpart2(f1 text COMPRESSION pglz); -ALTER TABLE cmpart ATTACH PARTITION cmpart2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -ERROR: relation "cmpart" does not exist -INSERT INTO cmpart VALUES (repeat('123456789', 1004)); -ERROR: relation "cmpart" does not exist -LINE 1: INSERT INTO cmpart VALUES (repeat('123456789', 1004)); - ^ -INSERT INTO cmpart VALUES (repeat('123456789', 4004)); -ERROR: relation "cmpart" does not exist -LINE 1: INSERT INTO cmpart VALUES (repeat('123456789', 4004)); - ^ -SELECT pg_column_compression(f1) FROM cmpart1; -ERROR: relation "cmpart1" does not exist -LINE 1: SELECT pg_column_compression(f1) FROM cmpart1; - ^ -SELECT pg_column_compression(f1) FROM cmpart2; - pg_column_compression ------------------------ -(0 rows) - --- test compression with inheritance, error -CREATE TABLE cminh() INHERITS(cmdata, cmdata1); -ERROR: relation "cmdata1" does not exist -CREATE TABLE cminh(f1 TEXT COMPRESSION lz4) INHERITS(cmdata); -NOTICE: merging column "f1" with inherited definition -ERROR: column "f1" has a compression method conflict -DETAIL: pglz versus lz4 --- test default_toast_compression GUC -SET default_toast_compression = ''; -ERROR: invalid value for parameter "default_toast_compression": "" -HINT: Available values: pglz. -SET default_toast_compression = 'I do not exist compression'; -ERROR: invalid value for parameter "default_toast_compression": "I do not exist compression" -HINT: Available values: pglz. -SET default_toast_compression = 'lz4'; -ERROR: invalid value for parameter "default_toast_compression": "lz4" -HINT: Available values: pglz. -SET default_toast_compression = 'pglz'; --- test alter compression method -ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION lz4; -ERROR: compression method lz4 not supported -DETAIL: This functionality requires the server to be built with lz4 support. -HINT: You need to rebuild PostgreSQL using --with-lz4. -INSERT INTO cmdata VALUES (repeat('123456789', 4004)); -\d+ cmdata - Table "public.cmdata" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+------+-----------+----------+---------+----------+-------------+--------------+------------- - f1 | text | | | | extended | pglz | | -Indexes: - "idx" btree (f1) - -SELECT pg_column_compression(f1) FROM cmdata; - pg_column_compression ------------------------ - pglz - pglz -(2 rows) - -ALTER TABLE cmdata2 ALTER COLUMN f1 SET COMPRESSION default; -\d+ cmdata2 - Table "public.cmdata2" - Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+-------------------+-----------+----------+---------+---------+-------------+--------------+------------- - f1 | character varying | | | | plain | | | - --- test alter compression method for materialized views -ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; -ERROR: relation "compressmv" does not exist -\d+ compressmv --- test alter compression method for partitioned tables -ALTER TABLE cmpart1 ALTER COLUMN f1 SET COMPRESSION pglz; -ERROR: relation "cmpart1" does not exist -ALTER TABLE cmpart2 ALTER COLUMN f1 SET COMPRESSION lz4; -ERROR: compression method lz4 not supported -DETAIL: This functionality requires the server to be built with lz4 support. -HINT: You need to rebuild PostgreSQL using --with-lz4. --- new data should be compressed with the current compression method -INSERT INTO cmpart VALUES (repeat('123456789', 1004)); -ERROR: relation "cmpart" does not exist -LINE 1: INSERT INTO cmpart VALUES (repeat('123456789', 1004)); - ^ -INSERT INTO cmpart VALUES (repeat('123456789', 4004)); -ERROR: relation "cmpart" does not exist -LINE 1: INSERT INTO cmpart VALUES (repeat('123456789', 4004)); - ^ -SELECT pg_column_compression(f1) FROM cmpart1; -ERROR: relation "cmpart1" does not exist -LINE 1: SELECT pg_column_compression(f1) FROM cmpart1; - ^ -SELECT pg_column_compression(f1) FROM cmpart2; - pg_column_compression ------------------------ -(0 rows) - --- VACUUM FULL does not recompress -SELECT pg_column_compression(f1) FROM cmdata; - pg_column_compression ------------------------ - pglz - pglz -(2 rows) - -VACUUM FULL cmdata; -SELECT pg_column_compression(f1) FROM cmdata; - pg_column_compression ------------------------ - pglz - pglz -(2 rows) - --- test expression index -DROP TABLE cmdata2; -CREATE TABLE cmdata2 (f1 TEXT COMPRESSION pglz, f2 TEXT COMPRESSION lz4); -ERROR: compression method lz4 not supported -DETAIL: This functionality requires the server to be built with lz4 support. -HINT: You need to rebuild PostgreSQL using --with-lz4. -CREATE UNIQUE INDEX idx1 ON cmdata2 ((f1 || f2)); -ERROR: relation "cmdata2" does not exist -INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::TEXT FROM -generate_series(1, 50) g), VERSION()); -ERROR: relation "cmdata2" does not exist -LINE 1: INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::... - ^ --- check data is ok -SELECT length(f1) FROM cmdata; - length --------- - 10000 - 36036 -(2 rows) - -SELECT length(f1) FROM cmdata1; -ERROR: relation "cmdata1" does not exist -LINE 1: SELECT length(f1) FROM cmdata1; - ^ -SELECT length(f1) FROM cmmove1; - length --------- - 10000 -(1 row) - -SELECT length(f1) FROM cmmove2; - length --------- - 10040 -(1 row) - -SELECT length(f1) FROM cmmove3; - length --------- - 10000 -(1 row) - -CREATE TABLE badcompresstbl (a text COMPRESSION I_Do_Not_Exist_Compression); -- fails -ERROR: invalid compression method "i_do_not_exist_compression" -CREATE TABLE badcompresstbl (a text); -ALTER TABLE badcompresstbl ALTER a SET COMPRESSION I_Do_Not_Exist_Compression; -- fails -ERROR: invalid compression method "i_do_not_exist_compression" -DROP TABLE badcompresstbl; -\set HIDE_TOAST_COMPRESSION true +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/memoize.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/memoize.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/memoize.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/memoize.out 2021-10-30 22:09:26 -0600 @@ -1,159 +1,2 @@ --- Perform tests on the Memoize node. --- The cache hits/misses/evictions from the Memoize node can vary between --- machines. Let's just replace the number with an 'N'. In order to allow us --- to perform validation when the measure was zero, we replace a zero value --- with "Zero". All other numbers are replaced with 'N'. -create function explain_memoize(query text, hide_hitmiss bool) returns setof text -language plpgsql as -$$ -declare - ln text; -begin - for ln in - execute format('explain (analyze, costs off, summary off, timing off) %s', - query) - loop - if hide_hitmiss = true then - ln := regexp_replace(ln, 'Hits: 0', 'Hits: Zero'); - ln := regexp_replace(ln, 'Hits: \d+', 'Hits: N'); - ln := regexp_replace(ln, 'Misses: 0', 'Misses: Zero'); - ln := regexp_replace(ln, 'Misses: \d+', 'Misses: N'); - end if; - ln := regexp_replace(ln, 'Evictions: 0', 'Evictions: Zero'); - ln := regexp_replace(ln, 'Evictions: \d+', 'Evictions: N'); - ln := regexp_replace(ln, 'Memory Usage: \d+', 'Memory Usage: N'); - ln := regexp_replace(ln, 'Heap Fetches: \d+', 'Heap Fetches: N'); - ln := regexp_replace(ln, 'loops=\d+', 'loops=N'); - return next ln; - end loop; -end; -$$; --- Ensure we get a memoize node on the inner side of the nested loop -SET enable_hashjoin TO off; -SET enable_bitmapscan TO off; -SELECT explain_memoize(' -SELECT COUNT(*),AVG(t1.unique1) FROM tenk1 t1 -INNER JOIN tenk1 t2 ON t1.unique1 = t2.twenty -WHERE t2.unique1 < 1000;', false); - explain_memoize -------------------------------------------------------------------------------------------- - Aggregate (actual rows=1 loops=N) - -> Nested Loop (actual rows=1000 loops=N) - -> Seq Scan on tenk1 t2 (actual rows=1000 loops=N) - Filter: (unique1 < 1000) - Rows Removed by Filter: 9000 - -> Memoize (actual rows=1 loops=N) - Cache Key: t2.twenty - Hits: 980 Misses: 20 Evictions: Zero Overflows: 0 Memory Usage: NkB - -> Index Only Scan using tenk1_unique1 on tenk1 t1 (actual rows=1 loops=N) - Index Cond: (unique1 = t2.twenty) - Heap Fetches: N -(11 rows) - --- And check we get the expected results. -SELECT COUNT(*),AVG(t1.unique1) FROM tenk1 t1 -INNER JOIN tenk1 t2 ON t1.unique1 = t2.twenty -WHERE t2.unique1 < 1000; - count | avg --------+-------------------- - 1000 | 9.5000000000000000 -(1 row) - --- Try with LATERAL joins -SELECT explain_memoize(' -SELECT COUNT(*),AVG(t2.unique1) FROM tenk1 t1, -LATERAL (SELECT t2.unique1 FROM tenk1 t2 WHERE t1.twenty = t2.unique1) t2 -WHERE t1.unique1 < 1000;', false); - explain_memoize -------------------------------------------------------------------------------------------- - Aggregate (actual rows=1 loops=N) - -> Nested Loop (actual rows=1000 loops=N) - -> Seq Scan on tenk1 t1 (actual rows=1000 loops=N) - Filter: (unique1 < 1000) - Rows Removed by Filter: 9000 - -> Memoize (actual rows=1 loops=N) - Cache Key: t1.twenty - Hits: 980 Misses: 20 Evictions: Zero Overflows: 0 Memory Usage: NkB - -> Index Only Scan using tenk1_unique1 on tenk1 t2 (actual rows=1 loops=N) - Index Cond: (unique1 = t1.twenty) - Heap Fetches: N -(11 rows) - --- And check we get the expected results. -SELECT COUNT(*),AVG(t2.unique1) FROM tenk1 t1, -LATERAL (SELECT t2.unique1 FROM tenk1 t2 WHERE t1.twenty = t2.unique1) t2 -WHERE t1.unique1 < 1000; - count | avg --------+-------------------- - 1000 | 9.5000000000000000 -(1 row) - --- Reduce work_mem so that we see some cache evictions -SET work_mem TO '64kB'; -SET enable_mergejoin TO off; --- Ensure we get some evictions. We're unable to validate the hits and misses --- here as the number of entries that fit in the cache at once will vary --- between different machines. -SELECT explain_memoize(' -SELECT COUNT(*),AVG(t1.unique1) FROM tenk1 t1 -INNER JOIN tenk1 t2 ON t1.unique1 = t2.thousand -WHERE t2.unique1 < 1200;', true); - explain_memoize -------------------------------------------------------------------------------------------- - Aggregate (actual rows=1 loops=N) - -> Nested Loop (actual rows=1200 loops=N) - -> Seq Scan on tenk1 t2 (actual rows=1200 loops=N) - Filter: (unique1 < 1200) - Rows Removed by Filter: 8800 - -> Memoize (actual rows=1 loops=N) - Cache Key: t2.thousand - Hits: N Misses: N Evictions: N Overflows: 0 Memory Usage: NkB - -> Index Only Scan using tenk1_unique1 on tenk1 t1 (actual rows=1 loops=N) - Index Cond: (unique1 = t2.thousand) - Heap Fetches: N -(11 rows) - -RESET enable_mergejoin; -RESET work_mem; -RESET enable_bitmapscan; -RESET enable_hashjoin; --- Test parallel plans with Memoize -SET min_parallel_table_scan_size TO 0; -SET parallel_setup_cost TO 0; -SET parallel_tuple_cost TO 0; -SET max_parallel_workers_per_gather TO 2; --- Ensure we get a parallel plan. -EXPLAIN (COSTS OFF) -SELECT COUNT(*),AVG(t2.unique1) FROM tenk1 t1, -LATERAL (SELECT t2.unique1 FROM tenk1 t2 WHERE t1.twenty = t2.unique1) t2 -WHERE t1.unique1 < 1000; - QUERY PLAN -------------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Nested Loop - -> Parallel Bitmap Heap Scan on tenk1 t1 - Recheck Cond: (unique1 < 1000) - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 < 1000) - -> Memoize - Cache Key: t1.twenty - -> Index Only Scan using tenk1_unique1 on tenk1 t2 - Index Cond: (unique1 = t1.twenty) -(13 rows) - --- And ensure the parallel plan gives us the correct results. -SELECT COUNT(*),AVG(t2.unique1) FROM tenk1 t1, -LATERAL (SELECT t2.unique1 FROM tenk1 t2 WHERE t1.twenty = t2.unique1) t2 -WHERE t1.unique1 < 1000; - count | avg --------+-------------------- - 1000 | 9.5000000000000000 -(1 row) - -RESET max_parallel_workers_per_gather; -RESET parallel_tuple_cost; -RESET parallel_setup_cost; -RESET min_parallel_table_scan_size; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/event_trigger.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/event_trigger.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/event_trigger.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/event_trigger.out 2021-10-30 22:09:27 -0600 @@ -1,606 +1,2 @@ --- should fail, return type mismatch -create event trigger regress_event_trigger - on ddl_command_start - execute procedure pg_backend_pid(); -ERROR: function pg_backend_pid must return type event_trigger --- OK -create function test_event_trigger() returns event_trigger as $$ -BEGIN - RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag; -END -$$ language plpgsql; --- should fail, can't call it as a plain function -SELECT test_event_trigger(); -ERROR: trigger functions can only be called as triggers -CONTEXT: compilation of PL/pgSQL function "test_event_trigger" near line 1 --- should fail, event triggers cannot have declared arguments -create function test_event_trigger_arg(name text) -returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql; -ERROR: event trigger functions cannot have declared arguments -CONTEXT: compilation of PL/pgSQL function "test_event_trigger_arg" near line 1 --- should fail, SQL functions cannot be event triggers -create function test_event_trigger_sql() returns event_trigger as $$ -SELECT 1 $$ language sql; -ERROR: SQL functions cannot return type event_trigger --- should fail, no elephant_bootstrap entry point -create event trigger regress_event_trigger on elephant_bootstrap - execute procedure test_event_trigger(); -ERROR: unrecognized event name "elephant_bootstrap" --- OK -create event trigger regress_event_trigger on ddl_command_start - execute procedure test_event_trigger(); --- OK -create event trigger regress_event_trigger_end on ddl_command_end - execute function test_event_trigger(); --- should fail, food is not a valid filter variable -create event trigger regress_event_trigger2 on ddl_command_start - when food in ('sandwich') - execute procedure test_event_trigger(); -ERROR: unrecognized filter variable "food" --- should fail, sandwich is not a valid command tag -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('sandwich') - execute procedure test_event_trigger(); -ERROR: filter value "sandwich" not recognized for filter variable "tag" --- should fail, create skunkcabbage is not a valid command tag -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('create table', 'create skunkcabbage') - execute procedure test_event_trigger(); -ERROR: filter value "create skunkcabbage" not recognized for filter variable "tag" --- should fail, can't have event triggers on event triggers -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('DROP EVENT TRIGGER') - execute procedure test_event_trigger(); -ERROR: event triggers are not supported for DROP EVENT TRIGGER --- should fail, can't have event triggers on global objects -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('CREATE ROLE') - execute procedure test_event_trigger(); -ERROR: event triggers are not supported for CREATE ROLE --- should fail, can't have event triggers on global objects -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('CREATE DATABASE') - execute procedure test_event_trigger(); -ERROR: event triggers are not supported for CREATE DATABASE --- should fail, can't have event triggers on global objects -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('CREATE TABLESPACE') - execute procedure test_event_trigger(); -ERROR: event triggers are not supported for CREATE TABLESPACE --- should fail, can't have same filter variable twice -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('create table') and tag in ('CREATE FUNCTION') - execute procedure test_event_trigger(); -ERROR: filter variable "tag" specified more than once --- should fail, can't have arguments -create event trigger regress_event_trigger2 on ddl_command_start - execute procedure test_event_trigger('argument not allowed'); -ERROR: syntax error at or near "'argument not allowed'" -LINE 2: execute procedure test_event_trigger('argument not allowe... - ^ --- OK -create event trigger regress_event_trigger2 on ddl_command_start - when tag in ('create table', 'CREATE FUNCTION') - execute procedure test_event_trigger(); --- OK -comment on event trigger regress_event_trigger is 'test comment'; --- drop as non-superuser should fail -create role regress_evt_user; -set role regress_evt_user; -create event trigger regress_event_trigger_noperms on ddl_command_start - execute procedure test_event_trigger(); -ERROR: permission denied to create event trigger "regress_event_trigger_noperms" -HINT: Must be superuser to create an event trigger. -reset role; --- test enabling and disabling -alter event trigger regress_event_trigger disable; --- fires _trigger2 and _trigger_end should fire, but not _trigger -create table event_trigger_fire1 (a int); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_end CREATE TABLE -alter event trigger regress_event_trigger enable; -set session_replication_role = replica; --- fires nothing -create table event_trigger_fire2 (a int); -alter event trigger regress_event_trigger enable replica; --- fires only _trigger -create table event_trigger_fire3 (a int); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -alter event trigger regress_event_trigger enable always; --- fires only _trigger -create table event_trigger_fire4 (a int); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -reset session_replication_role; --- fires all three -create table event_trigger_fire5 (a int); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_end CREATE TABLE --- non-top-level command -create function f1() returns int -language plpgsql -as $$ -begin - create table event_trigger_fire6 (a int); - return 0; -end $$; -NOTICE: test_event_trigger: ddl_command_start CREATE FUNCTION -NOTICE: test_event_trigger: ddl_command_start CREATE FUNCTION -NOTICE: test_event_trigger: ddl_command_end CREATE FUNCTION -select f1(); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_end CREATE TABLE - f1 ----- - 0 -(1 row) - --- non-top-level command -create procedure p1() -language plpgsql -as $$ -begin - create table event_trigger_fire7 (a int); -end $$; -NOTICE: test_event_trigger: ddl_command_start CREATE PROCEDURE -NOTICE: test_event_trigger: ddl_command_end CREATE PROCEDURE -call p1(); -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_start CREATE TABLE -NOTICE: test_event_trigger: ddl_command_end CREATE TABLE --- clean up -alter event trigger regress_event_trigger disable; -drop table event_trigger_fire2, event_trigger_fire3, event_trigger_fire4, event_trigger_fire5, event_trigger_fire6, event_trigger_fire7; -NOTICE: test_event_trigger: ddl_command_end DROP TABLE -drop routine f1(), p1(); -NOTICE: test_event_trigger: ddl_command_end DROP ROUTINE --- regress_event_trigger_end should fire on these commands -grant all on table event_trigger_fire1 to public; -NOTICE: test_event_trigger: ddl_command_end GRANT -comment on table event_trigger_fire1 is 'here is a comment'; -NOTICE: test_event_trigger: ddl_command_end COMMENT -revoke all on table event_trigger_fire1 from public; -NOTICE: test_event_trigger: ddl_command_end REVOKE -drop table event_trigger_fire1; -NOTICE: test_event_trigger: ddl_command_end DROP TABLE -create foreign data wrapper useless; -NOTICE: test_event_trigger: ddl_command_end CREATE FOREIGN DATA WRAPPER -create server useless_server foreign data wrapper useless; -NOTICE: test_event_trigger: ddl_command_end CREATE SERVER -create user mapping for regress_evt_user server useless_server; -NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING -alter default privileges for role regress_evt_user - revoke delete on tables from regress_evt_user; -NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES --- alter owner to non-superuser should fail -alter event trigger regress_event_trigger owner to regress_evt_user; -ERROR: permission denied to change owner of event trigger "regress_event_trigger" -HINT: The owner of an event trigger must be a superuser. --- alter owner to superuser should work -alter role regress_evt_user superuser; -alter event trigger regress_event_trigger owner to regress_evt_user; --- should fail, name collision -alter event trigger regress_event_trigger rename to regress_event_trigger2; -ERROR: event trigger "regress_event_trigger2" already exists --- OK -alter event trigger regress_event_trigger rename to regress_event_trigger3; --- should fail, doesn't exist any more -drop event trigger regress_event_trigger; -ERROR: event trigger "regress_event_trigger" does not exist --- should fail, regress_evt_user owns some objects -drop role regress_evt_user; -ERROR: role "regress_evt_user" cannot be dropped because some objects depend on it -DETAIL: owner of event trigger regress_event_trigger3 -owner of user mapping for regress_evt_user on server useless_server -owner of default privileges on new relations belonging to role regress_evt_user --- cleanup before next test --- these are all OK; the second one should emit a NOTICE -drop event trigger if exists regress_event_trigger2; -drop event trigger if exists regress_event_trigger2; -NOTICE: event trigger "regress_event_trigger2" does not exist, skipping -drop event trigger regress_event_trigger3; -drop event trigger regress_event_trigger_end; --- test support for dropped objects -CREATE SCHEMA schema_one authorization regress_evt_user; -CREATE SCHEMA schema_two authorization regress_evt_user; -CREATE SCHEMA audit_tbls authorization regress_evt_user; -CREATE TEMP TABLE a_temp_tbl (); -SET SESSION AUTHORIZATION regress_evt_user; -CREATE TABLE schema_one.table_one(a int); -CREATE TABLE schema_one."table two"(a int); -CREATE TABLE schema_one.table_three(a int); -CREATE TABLE audit_tbls.schema_one_table_two(the_value text); -CREATE TABLE schema_two.table_two(a int); -CREATE TABLE schema_two.table_three(a int, b text); -CREATE TABLE audit_tbls.schema_two_table_three(the_value text); -CREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql - CALLED ON NULL INPUT - AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$; -CREATE AGGREGATE schema_two.newton - (BASETYPE = int, SFUNC = schema_two.add, STYPE = int); -RESET SESSION AUTHORIZATION; -CREATE TABLE undroppable_objs ( - object_type text, - object_identity text -); -INSERT INTO undroppable_objs VALUES -('table', 'schema_one.table_three'), -('table', 'audit_tbls.schema_two_table_three'); -CREATE TABLE dropped_objects ( - type text, - schema text, - object text -); --- This tests errors raised within event triggers; the one in audit_tbls --- uses 2nd-level recursive invocation via test_evtrig_dropped_objects(). -CREATE OR REPLACE FUNCTION undroppable() RETURNS event_trigger -LANGUAGE plpgsql AS $$ -DECLARE - obj record; -BEGIN - PERFORM 1 FROM pg_tables WHERE tablename = 'undroppable_objs'; - IF NOT FOUND THEN - RAISE NOTICE 'table undroppable_objs not found, skipping'; - RETURN; - END IF; - FOR obj IN - SELECT * FROM pg_event_trigger_dropped_objects() JOIN - undroppable_objs USING (object_type, object_identity) - LOOP - RAISE EXCEPTION 'object % of type % cannot be dropped', - obj.object_identity, obj.object_type; - END LOOP; -END; -$$; -CREATE EVENT TRIGGER undroppable ON sql_drop - EXECUTE PROCEDURE undroppable(); -CREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger -LANGUAGE plpgsql AS $$ -DECLARE - obj record; -BEGIN - FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() - LOOP - IF obj.object_type = 'table' THEN - EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I', - format('%s_%s', obj.schema_name, obj.object_name)); - END IF; - - INSERT INTO dropped_objects - (type, schema, object) VALUES - (obj.object_type, obj.schema_name, obj.object_identity); - END LOOP; -END -$$; -CREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop - WHEN TAG IN ('drop table', 'drop function', 'drop view', - 'drop owned', 'drop schema', 'alter table') - EXECUTE PROCEDURE test_evtrig_dropped_objects(); -ALTER TABLE schema_one.table_one DROP COLUMN a; -DROP SCHEMA schema_one, schema_two CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table schema_two.table_two -drop cascades to table schema_two.table_three -drop cascades to function schema_two.add(integer,integer) -drop cascades to function schema_two.newton(integer) -drop cascades to table schema_one.table_one -drop cascades to table schema_one."table two" -drop cascades to table schema_one.table_three -NOTICE: table "schema_two_table_two" does not exist, skipping -NOTICE: table "audit_tbls_schema_two_table_three" does not exist, skipping -ERROR: object audit_tbls.schema_two_table_three of type table cannot be dropped -CONTEXT: PL/pgSQL function undroppable() line 14 at RAISE -SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_three" -PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE -DELETE FROM undroppable_objs WHERE object_identity = 'audit_tbls.schema_two_table_three'; -DROP SCHEMA schema_one, schema_two CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table schema_two.table_two -drop cascades to table schema_two.table_three -drop cascades to function schema_two.add(integer,integer) -drop cascades to function schema_two.newton(integer) -drop cascades to table schema_one.table_one -drop cascades to table schema_one."table two" -drop cascades to table schema_one.table_three -NOTICE: table "schema_two_table_two" does not exist, skipping -NOTICE: table "audit_tbls_schema_two_table_three" does not exist, skipping -NOTICE: table "schema_one_table_one" does not exist, skipping -NOTICE: table "schema_one_table two" does not exist, skipping -NOTICE: table "schema_one_table_three" does not exist, skipping -ERROR: object schema_one.table_three of type table cannot be dropped -CONTEXT: PL/pgSQL function undroppable() line 14 at RAISE -DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three'; -DROP SCHEMA schema_one, schema_two CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table schema_two.table_two -drop cascades to table schema_two.table_three -drop cascades to function schema_two.add(integer,integer) -drop cascades to function schema_two.newton(integer) -drop cascades to table schema_one.table_one -drop cascades to table schema_one."table two" -drop cascades to table schema_one.table_three -NOTICE: table "schema_two_table_two" does not exist, skipping -NOTICE: table "audit_tbls_schema_two_table_three" does not exist, skipping -NOTICE: table "schema_one_table_one" does not exist, skipping -NOTICE: table "schema_one_table two" does not exist, skipping -NOTICE: table "schema_one_table_three" does not exist, skipping -SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast'; - type | schema | object ---------------+------------+------------------------------------- - table column | schema_one | schema_one.table_one.a - schema | | schema_two - table | schema_two | schema_two.table_two - type | schema_two | schema_two.table_two - type | schema_two | schema_two.table_two[] - table | audit_tbls | audit_tbls.schema_two_table_three - type | audit_tbls | audit_tbls.schema_two_table_three - type | audit_tbls | audit_tbls.schema_two_table_three[] - table | schema_two | schema_two.table_three - type | schema_two | schema_two.table_three - type | schema_two | schema_two.table_three[] - function | schema_two | schema_two.add(integer,integer) - aggregate | schema_two | schema_two.newton(integer) - schema | | schema_one - table | schema_one | schema_one.table_one - type | schema_one | schema_one.table_one - type | schema_one | schema_one.table_one[] - table | schema_one | schema_one."table two" - type | schema_one | schema_one."table two" - type | schema_one | schema_one."table two"[] - table | schema_one | schema_one.table_three - type | schema_one | schema_one.table_three - type | schema_one | schema_one.table_three[] -(23 rows) - -DROP OWNED BY regress_evt_user; -NOTICE: schema "audit_tbls" does not exist, skipping -SELECT * FROM dropped_objects WHERE type = 'schema'; - type | schema | object ---------+--------+------------ - schema | | schema_two - schema | | schema_one - schema | | audit_tbls -(3 rows) - -DROP ROLE regress_evt_user; -DROP EVENT TRIGGER regress_event_trigger_drop_objects; -DROP EVENT TRIGGER undroppable; --- Event triggers on relations. -CREATE OR REPLACE FUNCTION event_trigger_report_dropped() - RETURNS event_trigger - LANGUAGE plpgsql -AS $$ -DECLARE r record; -BEGIN - FOR r IN SELECT * from pg_event_trigger_dropped_objects() - LOOP - IF NOT r.normal AND NOT r.original THEN - CONTINUE; - END IF; - RAISE NOTICE 'NORMAL: orig=% normal=% istemp=% type=% identity=% name=% args=%', - r.original, r.normal, r.is_temporary, r.object_type, - r.object_identity, r.address_names, r.address_args; - END LOOP; -END; $$; -CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop - EXECUTE PROCEDURE event_trigger_report_dropped(); -CREATE OR REPLACE FUNCTION event_trigger_report_end() - RETURNS event_trigger - LANGUAGE plpgsql -AS $$ -DECLARE r RECORD; -BEGIN - FOR r IN SELECT * FROM pg_event_trigger_ddl_commands() - LOOP - RAISE NOTICE 'END: command_tag=% type=% identity=%', - r.command_tag, r.object_type, r.object_identity; - END LOOP; -END; $$; -CREATE EVENT TRIGGER regress_event_trigger_report_end ON ddl_command_end - EXECUTE PROCEDURE event_trigger_report_end(); -CREATE SCHEMA evttrig - CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two', col_c SERIAL) - CREATE INDEX one_idx ON one (col_b) - CREATE TABLE two (col_c INTEGER CHECK (col_c > 0) REFERENCES one DEFAULT 42) - CREATE TABLE id (col_d int NOT NULL GENERATED ALWAYS AS IDENTITY); -NOTICE: END: command_tag=CREATE SCHEMA type=schema identity=evttrig -NOTICE: END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.one_col_a_seq -NOTICE: END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.one_col_c_seq -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.one -NOTICE: END: command_tag=CREATE INDEX type=index identity=evttrig.one_pkey -NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_a_seq -NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_c_seq -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.two -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.two -NOTICE: END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.id_col_d_seq -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.id -NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.id_col_d_seq -NOTICE: END: command_tag=CREATE INDEX type=index identity=evttrig.one_idx --- Partitioned tables with a partitioned index -CREATE TABLE evttrig.parted ( - id int PRIMARY KEY) - PARTITION BY RANGE (id); -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.parted -NOTICE: END: command_tag=CREATE INDEX type=index identity=evttrig.parted_pkey -CREATE TABLE evttrig.part_1_10 PARTITION OF evttrig.parted (id) - FOR VALUES FROM (1) TO (10); -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.part_1_10 -CREATE TABLE evttrig.part_10_20 PARTITION OF evttrig.parted (id) - FOR VALUES FROM (10) TO (20) PARTITION BY RANGE (id); -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.part_10_20 -CREATE TABLE evttrig.part_10_15 PARTITION OF evttrig.part_10_20 (id) - FOR VALUES FROM (10) TO (15); -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.part_10_15 -CREATE TABLE evttrig.part_15_20 PARTITION OF evttrig.part_10_20 (id) - FOR VALUES FROM (15) TO (20); -NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.part_15_20 -ALTER TABLE evttrig.two DROP COLUMN col_c; -NOTICE: NORMAL: orig=t normal=f istemp=f type=table column identity=evttrig.two.col_c name={evttrig,two,col_c} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table constraint identity=two_col_c_check on evttrig.two name={evttrig,two,two_col_c_check} args={} -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.two -ALTER TABLE evttrig.one ALTER COLUMN col_b DROP DEFAULT; -NOTICE: NORMAL: orig=t normal=f istemp=f type=default value identity=for evttrig.one.col_b name={evttrig,one,col_b} args={} -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.one -ALTER TABLE evttrig.one DROP CONSTRAINT one_pkey; -NOTICE: NORMAL: orig=t normal=f istemp=f type=table constraint identity=one_pkey on evttrig.one name={evttrig,one,one_pkey} args={} -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.one -ALTER TABLE evttrig.one DROP COLUMN col_c; -NOTICE: NORMAL: orig=t normal=f istemp=f type=table column identity=evttrig.one.col_c name={evttrig,one,col_c} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=default value identity=for evttrig.one.col_c name={evttrig,one,col_c} args={} -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.one -ALTER TABLE evttrig.id ALTER COLUMN col_d SET DATA TYPE bigint; -NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.id_col_d_seq -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.id -ALTER TABLE evttrig.id ALTER COLUMN col_d DROP IDENTITY, - ALTER COLUMN col_d SET DATA TYPE int; -NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.id -DROP INDEX evttrig.one_idx; -NOTICE: NORMAL: orig=t normal=f istemp=f type=index identity=evttrig.one_idx name={evttrig,one_idx} args={} -DROP SCHEMA evttrig CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table evttrig.one -drop cascades to table evttrig.two -drop cascades to table evttrig.id -drop cascades to table evttrig.parted -NOTICE: NORMAL: orig=t normal=f istemp=f type=schema identity=evttrig name={evttrig} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.one name={evttrig,one} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=sequence identity=evttrig.one_col_a_seq name={evttrig,one_col_a_seq} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=default value identity=for evttrig.one.col_a name={evttrig,one,col_a} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.two name={evttrig,two} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.id name={evttrig,id} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.parted name={evttrig,parted} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.part_1_10 name={evttrig,part_1_10} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.part_10_20 name={evttrig,part_10_20} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.part_10_15 name={evttrig,part_10_15} args={} -NOTICE: NORMAL: orig=f normal=t istemp=f type=table identity=evttrig.part_15_20 name={evttrig,part_15_20} args={} -DROP TABLE a_temp_tbl; -NOTICE: NORMAL: orig=t normal=f istemp=t type=table identity=pg_temp.a_temp_tbl name={pg_temp,a_temp_tbl} args={} -DROP EVENT TRIGGER regress_event_trigger_report_dropped; -DROP EVENT TRIGGER regress_event_trigger_report_end; --- only allowed from within an event trigger function, should fail -select pg_event_trigger_table_rewrite_oid(); -ERROR: pg_event_trigger_table_rewrite_oid() can only be called in a table_rewrite event trigger function --- test Table Rewrite Event Trigger -CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger -LANGUAGE plpgsql AS $$ -BEGIN - RAISE EXCEPTION 'rewrites not allowed'; -END; -$$; -create event trigger no_rewrite_allowed on table_rewrite - execute procedure test_evtrig_no_rewrite(); -create table rewriteme (id serial primary key, foo float, bar timestamptz); -insert into rewriteme - select x * 1.001 from generate_series(1, 500) as t(x); -alter table rewriteme alter column foo type numeric; -ERROR: rewrites not allowed -CONTEXT: PL/pgSQL function test_evtrig_no_rewrite() line 3 at RAISE -alter table rewriteme add column baz int default 0; --- test with more than one reason to rewrite a single table -CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger -LANGUAGE plpgsql AS $$ -BEGIN - RAISE NOTICE 'Table ''%'' is being rewritten (reason = %)', - pg_event_trigger_table_rewrite_oid()::regclass, - pg_event_trigger_table_rewrite_reason(); -END; -$$; -alter table rewriteme - add column onemore int default 0, - add column another int default -1, - alter column foo type numeric(10,4); -NOTICE: Table 'rewriteme' is being rewritten (reason = 4) --- shouldn't trigger a table_rewrite event -alter table rewriteme alter column foo type numeric(12,4); -begin; -set timezone to 'UTC'; -alter table rewriteme alter column bar type timestamp; -set timezone to '0'; -alter table rewriteme alter column bar type timestamptz; -set timezone to 'Europe/London'; -alter table rewriteme alter column bar type timestamp; -- does rewrite -NOTICE: Table 'rewriteme' is being rewritten (reason = 4) -rollback; --- typed tables are rewritten when their type changes. Don't emit table --- name, because firing order is not stable. -CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger -LANGUAGE plpgsql AS $$ -BEGIN - RAISE NOTICE 'Table is being rewritten (reason = %)', - pg_event_trigger_table_rewrite_reason(); -END; -$$; -create type rewritetype as (a int); -create table rewritemetoo1 of rewritetype; -create table rewritemetoo2 of rewritetype; -alter type rewritetype alter attribute a type text cascade; -NOTICE: Table is being rewritten (reason = 4) -NOTICE: Table is being rewritten (reason = 4) --- but this doesn't work -create table rewritemetoo3 (a rewritetype); -alter type rewritetype alter attribute a type varchar cascade; -ERROR: cannot alter type "rewritetype" because column "rewritemetoo3.a" uses it -drop table rewriteme; -drop event trigger no_rewrite_allowed; -drop function test_evtrig_no_rewrite(); --- test Row Security Event Trigger -RESET SESSION AUTHORIZATION; -CREATE TABLE event_trigger_test (a integer, b text); -CREATE OR REPLACE FUNCTION start_command() -RETURNS event_trigger AS $$ -BEGIN -RAISE NOTICE '% - ddl_command_start', tg_tag; -END; -$$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION end_command() -RETURNS event_trigger AS $$ -BEGIN -RAISE NOTICE '% - ddl_command_end', tg_tag; -END; -$$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION drop_sql_command() -RETURNS event_trigger AS $$ -BEGIN -RAISE NOTICE '% - sql_drop', tg_tag; -END; -$$ LANGUAGE plpgsql; -CREATE EVENT TRIGGER start_rls_command ON ddl_command_start - WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE start_command(); -CREATE EVENT TRIGGER end_rls_command ON ddl_command_end - WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE end_command(); -CREATE EVENT TRIGGER sql_drop_command ON sql_drop - WHEN TAG IN ('DROP POLICY') EXECUTE PROCEDURE drop_sql_command(); -CREATE POLICY p1 ON event_trigger_test USING (FALSE); -NOTICE: CREATE POLICY - ddl_command_start -NOTICE: CREATE POLICY - ddl_command_end -ALTER POLICY p1 ON event_trigger_test USING (TRUE); -NOTICE: ALTER POLICY - ddl_command_start -NOTICE: ALTER POLICY - ddl_command_end -ALTER POLICY p1 ON event_trigger_test RENAME TO p2; -NOTICE: ALTER POLICY - ddl_command_start -NOTICE: ALTER POLICY - ddl_command_end -DROP POLICY p2 ON event_trigger_test; -NOTICE: DROP POLICY - ddl_command_start -NOTICE: DROP POLICY - sql_drop -NOTICE: DROP POLICY - ddl_command_end --- Check the object addresses of all the event triggers. -SELECT - e.evtname, - pg_describe_object('pg_event_trigger'::regclass, e.oid, 0) as descr, - b.type, b.object_names, b.object_args, - pg_identify_object(a.classid, a.objid, a.objsubid) as ident - FROM pg_event_trigger as e, - LATERAL pg_identify_object_as_address('pg_event_trigger'::regclass, e.oid, 0) as b, - LATERAL pg_get_object_address(b.type, b.object_names, b.object_args) as a - ORDER BY e.evtname; - evtname | descr | type | object_names | object_args | ident --------------------+---------------------------------+---------------+---------------------+-------------+-------------------------------------------------------- - end_rls_command | event trigger end_rls_command | event trigger | {end_rls_command} | {} | ("event trigger",,end_rls_command,end_rls_command) - sql_drop_command | event trigger sql_drop_command | event trigger | {sql_drop_command} | {} | ("event trigger",,sql_drop_command,sql_drop_command) - start_rls_command | event trigger start_rls_command | event trigger | {start_rls_command} | {} | ("event trigger",,start_rls_command,start_rls_command) -(3 rows) - -DROP EVENT TRIGGER start_rls_command; -DROP EVENT TRIGGER end_rls_command; -DROP EVENT TRIGGER sql_drop_command; +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/oidjoins.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/oidjoins.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/oidjoins.out 2021-10-30 21:35:49 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/oidjoins.out 2021-10-30 22:09:27 -0600 @@ -1,266 +1,2 @@ --- --- Verify system catalog foreign key relationships --- -DO $doblock$ -declare - fk record; - nkeys integer; - cmd text; - err record; -begin - for fk in select * from pg_get_catalog_foreign_keys() - loop - raise notice 'checking % % => % %', - fk.fktable, fk.fkcols, fk.pktable, fk.pkcols; - nkeys := array_length(fk.fkcols, 1); - cmd := 'SELECT ctid'; - for i in 1 .. nkeys loop - cmd := cmd || ', ' || quote_ident(fk.fkcols[i]); - end loop; - if fk.is_array then - cmd := cmd || ' FROM (SELECT ctid'; - for i in 1 .. nkeys-1 loop - cmd := cmd || ', ' || quote_ident(fk.fkcols[i]); - end loop; - cmd := cmd || ', unnest(' || quote_ident(fk.fkcols[nkeys]); - cmd := cmd || ') as ' || quote_ident(fk.fkcols[nkeys]); - cmd := cmd || ' FROM ' || fk.fktable::text || ') fk WHERE '; - else - cmd := cmd || ' FROM ' || fk.fktable::text || ' fk WHERE '; - end if; - if fk.is_opt then - for i in 1 .. nkeys loop - cmd := cmd || quote_ident(fk.fkcols[i]) || ' != 0 AND '; - end loop; - end if; - cmd := cmd || 'NOT EXISTS(SELECT 1 FROM ' || fk.pktable::text || ' pk WHERE '; - for i in 1 .. nkeys loop - if i > 1 then cmd := cmd || ' AND '; end if; - cmd := cmd || 'pk.' || quote_ident(fk.pkcols[i]); - cmd := cmd || ' = fk.' || quote_ident(fk.fkcols[i]); - end loop; - cmd := cmd || ')'; - -- raise notice 'cmd = %', cmd; - for err in execute cmd loop - raise warning 'FK VIOLATION IN %(%): %', fk.fktable, fk.fkcols, err; - end loop; - end loop; -end -$doblock$; -NOTICE: checking pg_proc {pronamespace} => pg_namespace {oid} -NOTICE: checking pg_proc {proowner} => pg_authid {oid} -NOTICE: checking pg_proc {prolang} => pg_language {oid} -NOTICE: checking pg_proc {provariadic} => pg_type {oid} -NOTICE: checking pg_proc {prosupport} => pg_proc {oid} -NOTICE: checking pg_proc {prorettype} => pg_type {oid} -NOTICE: checking pg_proc {proargtypes} => pg_type {oid} -NOTICE: checking pg_proc {proallargtypes} => pg_type {oid} -NOTICE: checking pg_proc {protrftypes} => pg_type {oid} -NOTICE: checking pg_type {typnamespace} => pg_namespace {oid} -NOTICE: checking pg_type {typowner} => pg_authid {oid} -NOTICE: checking pg_type {typrelid} => pg_class {oid} -NOTICE: checking pg_type {typsubscript} => pg_proc {oid} -NOTICE: checking pg_type {typelem} => pg_type {oid} -NOTICE: checking pg_type {typarray} => pg_type {oid} -NOTICE: checking pg_type {typinput} => pg_proc {oid} -NOTICE: checking pg_type {typoutput} => pg_proc {oid} -NOTICE: checking pg_type {typreceive} => pg_proc {oid} -NOTICE: checking pg_type {typsend} => pg_proc {oid} -NOTICE: checking pg_type {typmodin} => pg_proc {oid} -NOTICE: checking pg_type {typmodout} => pg_proc {oid} -NOTICE: checking pg_type {typanalyze} => pg_proc {oid} -NOTICE: checking pg_type {typbasetype} => pg_type {oid} -NOTICE: checking pg_type {typcollation} => pg_collation {oid} -NOTICE: checking pg_attribute {attrelid} => pg_class {oid} -NOTICE: checking pg_attribute {atttypid} => pg_type {oid} -NOTICE: checking pg_attribute {attcollation} => pg_collation {oid} -NOTICE: checking pg_class {relnamespace} => pg_namespace {oid} -NOTICE: checking pg_class {reltype} => pg_type {oid} -NOTICE: checking pg_class {reloftype} => pg_type {oid} -NOTICE: checking pg_class {relowner} => pg_authid {oid} -NOTICE: checking pg_class {relam} => pg_am {oid} -NOTICE: checking pg_class {reltablespace} => pg_tablespace {oid} -NOTICE: checking pg_class {reltoastrelid} => pg_class {oid} -NOTICE: checking pg_class {relrewrite} => pg_class {oid} -NOTICE: checking pg_attrdef {adrelid} => pg_class {oid} -NOTICE: checking pg_attrdef {adrelid,adnum} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_constraint {connamespace} => pg_namespace {oid} -NOTICE: checking pg_constraint {conrelid} => pg_class {oid} -NOTICE: checking pg_constraint {contypid} => pg_type {oid} -NOTICE: checking pg_constraint {conindid} => pg_class {oid} -NOTICE: checking pg_constraint {conparentid} => pg_constraint {oid} -NOTICE: checking pg_constraint {confrelid} => pg_class {oid} -NOTICE: checking pg_constraint {conpfeqop} => pg_operator {oid} -NOTICE: checking pg_constraint {conppeqop} => pg_operator {oid} -NOTICE: checking pg_constraint {conffeqop} => pg_operator {oid} -NOTICE: checking pg_constraint {conexclop} => pg_operator {oid} -NOTICE: checking pg_constraint {conrelid,conkey} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_constraint {confrelid,confkey} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_inherits {inhrelid} => pg_class {oid} -NOTICE: checking pg_inherits {inhparent} => pg_class {oid} -NOTICE: checking pg_index {indexrelid} => pg_class {oid} -NOTICE: checking pg_index {indrelid} => pg_class {oid} -NOTICE: checking pg_index {indcollation} => pg_collation {oid} -NOTICE: checking pg_index {indclass} => pg_opclass {oid} -NOTICE: checking pg_index {indrelid,indkey} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_operator {oprnamespace} => pg_namespace {oid} -NOTICE: checking pg_operator {oprowner} => pg_authid {oid} -NOTICE: checking pg_operator {oprleft} => pg_type {oid} -NOTICE: checking pg_operator {oprright} => pg_type {oid} -NOTICE: checking pg_operator {oprresult} => pg_type {oid} -NOTICE: checking pg_operator {oprcom} => pg_operator {oid} -NOTICE: checking pg_operator {oprnegate} => pg_operator {oid} -NOTICE: checking pg_operator {oprcode} => pg_proc {oid} -NOTICE: checking pg_operator {oprrest} => pg_proc {oid} -NOTICE: checking pg_operator {oprjoin} => pg_proc {oid} -NOTICE: checking pg_opfamily {opfmethod} => pg_am {oid} -NOTICE: checking pg_opfamily {opfnamespace} => pg_namespace {oid} -NOTICE: checking pg_opfamily {opfowner} => pg_authid {oid} -NOTICE: checking pg_opclass {opcmethod} => pg_am {oid} -NOTICE: checking pg_opclass {opcnamespace} => pg_namespace {oid} -NOTICE: checking pg_opclass {opcowner} => pg_authid {oid} -NOTICE: checking pg_opclass {opcfamily} => pg_opfamily {oid} -NOTICE: checking pg_opclass {opcintype} => pg_type {oid} -NOTICE: checking pg_opclass {opckeytype} => pg_type {oid} -NOTICE: checking pg_am {amhandler} => pg_proc {oid} -NOTICE: checking pg_amop {amopfamily} => pg_opfamily {oid} -NOTICE: checking pg_amop {amoplefttype} => pg_type {oid} -NOTICE: checking pg_amop {amoprighttype} => pg_type {oid} -NOTICE: checking pg_amop {amopopr} => pg_operator {oid} -NOTICE: checking pg_amop {amopmethod} => pg_am {oid} -NOTICE: checking pg_amop {amopsortfamily} => pg_opfamily {oid} -NOTICE: checking pg_amproc {amprocfamily} => pg_opfamily {oid} -NOTICE: checking pg_amproc {amproclefttype} => pg_type {oid} -NOTICE: checking pg_amproc {amprocrighttype} => pg_type {oid} -NOTICE: checking pg_amproc {amproc} => pg_proc {oid} -NOTICE: checking pg_language {lanowner} => pg_authid {oid} -NOTICE: checking pg_language {lanplcallfoid} => pg_proc {oid} -NOTICE: checking pg_language {laninline} => pg_proc {oid} -NOTICE: checking pg_language {lanvalidator} => pg_proc {oid} -NOTICE: checking pg_largeobject_metadata {lomowner} => pg_authid {oid} -NOTICE: checking pg_largeobject {loid} => pg_largeobject_metadata {oid} -NOTICE: checking pg_aggregate {aggfnoid} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggtransfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggfinalfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggcombinefn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggserialfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggdeserialfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggmtransfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggminvtransfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggmfinalfn} => pg_proc {oid} -NOTICE: checking pg_aggregate {aggsortop} => pg_operator {oid} -NOTICE: checking pg_aggregate {aggtranstype} => pg_type {oid} -NOTICE: checking pg_aggregate {aggmtranstype} => pg_type {oid} -NOTICE: checking pg_statistic {starelid} => pg_class {oid} -NOTICE: checking pg_statistic {staop1} => pg_operator {oid} -NOTICE: checking pg_statistic {staop2} => pg_operator {oid} -NOTICE: checking pg_statistic {staop3} => pg_operator {oid} -NOTICE: checking pg_statistic {staop4} => pg_operator {oid} -NOTICE: checking pg_statistic {staop5} => pg_operator {oid} -NOTICE: checking pg_statistic {stacoll1} => pg_collation {oid} -NOTICE: checking pg_statistic {stacoll2} => pg_collation {oid} -NOTICE: checking pg_statistic {stacoll3} => pg_collation {oid} -NOTICE: checking pg_statistic {stacoll4} => pg_collation {oid} -NOTICE: checking pg_statistic {stacoll5} => pg_collation {oid} -NOTICE: checking pg_statistic {starelid,staattnum} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_statistic_ext {stxrelid} => pg_class {oid} -NOTICE: checking pg_statistic_ext {stxnamespace} => pg_namespace {oid} -NOTICE: checking pg_statistic_ext {stxowner} => pg_authid {oid} -NOTICE: checking pg_statistic_ext {stxrelid,stxkeys} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_statistic_ext_data {stxoid} => pg_statistic_ext {oid} -NOTICE: checking pg_rewrite {ev_class} => pg_class {oid} -NOTICE: checking pg_trigger {tgrelid} => pg_class {oid} -NOTICE: checking pg_trigger {tgparentid} => pg_trigger {oid} -NOTICE: checking pg_trigger {tgfoid} => pg_proc {oid} -NOTICE: checking pg_trigger {tgconstrrelid} => pg_class {oid} -NOTICE: checking pg_trigger {tgconstrindid} => pg_class {oid} -NOTICE: checking pg_trigger {tgconstraint} => pg_constraint {oid} -NOTICE: checking pg_trigger {tgrelid,tgattr} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_event_trigger {evtowner} => pg_authid {oid} -NOTICE: checking pg_event_trigger {evtfoid} => pg_proc {oid} -NOTICE: checking pg_description {classoid} => pg_class {oid} -NOTICE: checking pg_cast {castsource} => pg_type {oid} -NOTICE: checking pg_cast {casttarget} => pg_type {oid} -NOTICE: checking pg_cast {castfunc} => pg_proc {oid} -NOTICE: checking pg_enum {enumtypid} => pg_type {oid} -NOTICE: checking pg_namespace {nspowner} => pg_authid {oid} -NOTICE: checking pg_conversion {connamespace} => pg_namespace {oid} -NOTICE: checking pg_conversion {conowner} => pg_authid {oid} -NOTICE: checking pg_conversion {conproc} => pg_proc {oid} -NOTICE: checking pg_depend {classid} => pg_class {oid} -NOTICE: checking pg_depend {refclassid} => pg_class {oid} -NOTICE: checking pg_database {datdba} => pg_authid {oid} -NOTICE: checking pg_database {dattablespace} => pg_tablespace {oid} -NOTICE: checking pg_db_role_setting {setdatabase} => pg_database {oid} -NOTICE: checking pg_db_role_setting {setrole} => pg_authid {oid} -NOTICE: checking pg_tablespace {spcowner} => pg_authid {oid} -NOTICE: checking pg_auth_members {roleid} => pg_authid {oid} -NOTICE: checking pg_auth_members {member} => pg_authid {oid} -NOTICE: checking pg_auth_members {grantor} => pg_authid {oid} -NOTICE: checking pg_shdepend {dbid} => pg_database {oid} -NOTICE: checking pg_shdepend {classid} => pg_class {oid} -NOTICE: checking pg_shdepend {refclassid} => pg_class {oid} -NOTICE: checking pg_shdescription {classoid} => pg_class {oid} -NOTICE: checking pg_ts_config {cfgnamespace} => pg_namespace {oid} -NOTICE: checking pg_ts_config {cfgowner} => pg_authid {oid} -NOTICE: checking pg_ts_config {cfgparser} => pg_ts_parser {oid} -NOTICE: checking pg_ts_config_map {mapcfg} => pg_ts_config {oid} -NOTICE: checking pg_ts_config_map {mapdict} => pg_ts_dict {oid} -NOTICE: checking pg_ts_dict {dictnamespace} => pg_namespace {oid} -NOTICE: checking pg_ts_dict {dictowner} => pg_authid {oid} -NOTICE: checking pg_ts_dict {dicttemplate} => pg_ts_template {oid} -NOTICE: checking pg_ts_parser {prsnamespace} => pg_namespace {oid} -NOTICE: checking pg_ts_parser {prsstart} => pg_proc {oid} -NOTICE: checking pg_ts_parser {prstoken} => pg_proc {oid} -NOTICE: checking pg_ts_parser {prsend} => pg_proc {oid} -NOTICE: checking pg_ts_parser {prsheadline} => pg_proc {oid} -NOTICE: checking pg_ts_parser {prslextype} => pg_proc {oid} -NOTICE: checking pg_ts_template {tmplnamespace} => pg_namespace {oid} -NOTICE: checking pg_ts_template {tmplinit} => pg_proc {oid} -NOTICE: checking pg_ts_template {tmpllexize} => pg_proc {oid} -NOTICE: checking pg_extension {extowner} => pg_authid {oid} -NOTICE: checking pg_extension {extnamespace} => pg_namespace {oid} -NOTICE: checking pg_extension {extconfig} => pg_class {oid} -NOTICE: checking pg_foreign_data_wrapper {fdwowner} => pg_authid {oid} -NOTICE: checking pg_foreign_data_wrapper {fdwhandler} => pg_proc {oid} -NOTICE: checking pg_foreign_data_wrapper {fdwvalidator} => pg_proc {oid} -NOTICE: checking pg_foreign_server {srvowner} => pg_authid {oid} -NOTICE: checking pg_foreign_server {srvfdw} => pg_foreign_data_wrapper {oid} -NOTICE: checking pg_user_mapping {umuser} => pg_authid {oid} -NOTICE: checking pg_user_mapping {umserver} => pg_foreign_server {oid} -NOTICE: checking pg_foreign_table {ftrelid} => pg_class {oid} -NOTICE: checking pg_foreign_table {ftserver} => pg_foreign_server {oid} -NOTICE: checking pg_policy {polrelid} => pg_class {oid} -NOTICE: checking pg_policy {polroles} => pg_authid {oid} -NOTICE: checking pg_default_acl {defaclrole} => pg_authid {oid} -NOTICE: checking pg_default_acl {defaclnamespace} => pg_namespace {oid} -NOTICE: checking pg_init_privs {classoid} => pg_class {oid} -NOTICE: checking pg_seclabel {classoid} => pg_class {oid} -NOTICE: checking pg_shseclabel {classoid} => pg_class {oid} -NOTICE: checking pg_collation {collnamespace} => pg_namespace {oid} -NOTICE: checking pg_collation {collowner} => pg_authid {oid} -NOTICE: checking pg_partitioned_table {partrelid} => pg_class {oid} -NOTICE: checking pg_partitioned_table {partdefid} => pg_class {oid} -NOTICE: checking pg_partitioned_table {partclass} => pg_opclass {oid} -NOTICE: checking pg_partitioned_table {partcollation} => pg_collation {oid} -NOTICE: checking pg_partitioned_table {partrelid,partattrs} => pg_attribute {attrelid,attnum} -NOTICE: checking pg_range {rngtypid} => pg_type {oid} -NOTICE: checking pg_range {rngsubtype} => pg_type {oid} -NOTICE: checking pg_range {rngmultitypid} => pg_type {oid} -NOTICE: checking pg_range {rngcollation} => pg_collation {oid} -NOTICE: checking pg_range {rngsubopc} => pg_opclass {oid} -NOTICE: checking pg_range {rngcanonical} => pg_proc {oid} -NOTICE: checking pg_range {rngsubdiff} => pg_proc {oid} -NOTICE: checking pg_transform {trftype} => pg_type {oid} -NOTICE: checking pg_transform {trflang} => pg_language {oid} -NOTICE: checking pg_transform {trffromsql} => pg_proc {oid} -NOTICE: checking pg_transform {trftosql} => pg_proc {oid} -NOTICE: checking pg_sequence {seqrelid} => pg_class {oid} -NOTICE: checking pg_sequence {seqtypid} => pg_type {oid} -NOTICE: checking pg_publication {pubowner} => pg_authid {oid} -NOTICE: checking pg_publication_rel {prpubid} => pg_publication {oid} -NOTICE: checking pg_publication_rel {prrelid} => pg_class {oid} -NOTICE: checking pg_subscription {subdbid} => pg_database {oid} -NOTICE: checking pg_subscription {subowner} => pg_authid {oid} -NOTICE: checking pg_subscription_rel {srsubid} => pg_subscription {oid} -NOTICE: checking pg_subscription_rel {srrelid} => pg_class {oid} +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/fast_default.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/fast_default.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/fast_default.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/fast_default.out 2021-10-30 22:09:27 -0600 @@ -1,843 +1,2 @@ --- --- ALTER TABLE ADD COLUMN DEFAULT test --- -SET search_path = fast_default; -CREATE SCHEMA fast_default; -CREATE TABLE m(id OID); -INSERT INTO m VALUES (NULL::OID); -CREATE FUNCTION set(tabname name) RETURNS VOID -AS $$ -BEGIN - UPDATE m - SET id = (SELECT c.relfilenode - FROM pg_class AS c, pg_namespace AS s - WHERE c.relname = tabname - AND c.relnamespace = s.oid - AND s.nspname = 'fast_default'); -END; -$$ LANGUAGE 'plpgsql'; -CREATE FUNCTION comp() RETURNS TEXT -AS $$ -BEGIN - RETURN (SELECT CASE - WHEN m.id = c.relfilenode THEN 'Unchanged' - ELSE 'Rewritten' - END - FROM m, pg_class AS c, pg_namespace AS s - WHERE c.relname = 't' - AND c.relnamespace = s.oid - AND s.nspname = 'fast_default'); -END; -$$ LANGUAGE 'plpgsql'; -CREATE FUNCTION log_rewrite() RETURNS event_trigger -LANGUAGE plpgsql as -$func$ - -declare - this_schema text; -begin - select into this_schema relnamespace::regnamespace::text - from pg_class - where oid = pg_event_trigger_table_rewrite_oid(); - if this_schema = 'fast_default' - then - RAISE NOTICE 'rewriting table % for reason %', - pg_event_trigger_table_rewrite_oid()::regclass, - pg_event_trigger_table_rewrite_reason(); - end if; -end; -$func$; -CREATE TABLE has_volatile AS -SELECT * FROM generate_series(1,10) id; -CREATE EVENT TRIGGER has_volatile_rewrite - ON table_rewrite - EXECUTE PROCEDURE log_rewrite(); --- only the last of these should trigger a rewrite -ALTER TABLE has_volatile ADD col1 int; -ALTER TABLE has_volatile ADD col2 int DEFAULT 1; -ALTER TABLE has_volatile ADD col3 timestamptz DEFAULT current_timestamp; -ALTER TABLE has_volatile ADD col4 int DEFAULT (random() * 10000)::int; -NOTICE: rewriting table has_volatile for reason 2 --- Test a large sample of different datatypes -CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT 1); -SELECT set('t'); - set ------ - -(1 row) - -INSERT INTO T VALUES (1), (2); -ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT 'hello', - ALTER COLUMN c_int SET DEFAULT 2; -INSERT INTO T VALUES (3), (4); -ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'world', - ALTER COLUMN c_bpchar SET DEFAULT 'dog'; -INSERT INTO T VALUES (5), (6); -ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02', - ALTER COLUMN c_text SET DEFAULT 'cat'; -INSERT INTO T VALUES (7), (8); -ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01 12:00:00', - ADD COLUMN c_timestamp_null TIMESTAMP, - ALTER COLUMN c_date SET DEFAULT '2010-01-01'; -INSERT INTO T VALUES (9), (10); -ALTER TABLE T ADD COLUMN c_array TEXT[] - DEFAULT '{"This", "is", "the", "real", "world"}', - ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31 11:12:13', - ALTER COLUMN c_timestamp_null SET DEFAULT '2016-09-29 12:00:00'; -INSERT INTO T VALUES (11), (12); -ALTER TABLE T ADD COLUMN c_small SMALLINT DEFAULT -5, - ADD COLUMN c_small_null SMALLINT, - ALTER COLUMN c_array - SET DEFAULT '{"This", "is", "no", "fantasy"}'; -INSERT INTO T VALUES (13), (14); -ALTER TABLE T ADD COLUMN c_big BIGINT DEFAULT 180000000000018, - ALTER COLUMN c_small SET DEFAULT 9, - ALTER COLUMN c_small_null SET DEFAULT 13; -INSERT INTO T VALUES (15), (16); -ALTER TABLE T ADD COLUMN c_num NUMERIC DEFAULT 1.00000000001, - ALTER COLUMN c_big SET DEFAULT -9999999999999999; -INSERT INTO T VALUES (17), (18); -ALTER TABLE T ADD COLUMN c_time TIME DEFAULT '12:00:00', - ALTER COLUMN c_num SET DEFAULT 2.000000000000002; -INSERT INTO T VALUES (19), (20); -ALTER TABLE T ADD COLUMN c_interval INTERVAL DEFAULT '1 day', - ALTER COLUMN c_time SET DEFAULT '23:59:59'; -INSERT INTO T VALUES (21), (22); -ALTER TABLE T ADD COLUMN c_hugetext TEXT DEFAULT repeat('abcdefg',1000), - ALTER COLUMN c_interval SET DEFAULT '3 hours'; -INSERT INTO T VALUES (23), (24); -ALTER TABLE T ALTER COLUMN c_interval DROP DEFAULT, - ALTER COLUMN c_hugetext SET DEFAULT repeat('poiuyt', 1000); -INSERT INTO T VALUES (25), (26); -ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, - ALTER COLUMN c_date DROP DEFAULT, - ALTER COLUMN c_text DROP DEFAULT, - ALTER COLUMN c_timestamp DROP DEFAULT, - ALTER COLUMN c_array DROP DEFAULT, - ALTER COLUMN c_small DROP DEFAULT, - ALTER COLUMN c_big DROP DEFAULT, - ALTER COLUMN c_num DROP DEFAULT, - ALTER COLUMN c_time DROP DEFAULT, - ALTER COLUMN c_hugetext DROP DEFAULT; -INSERT INTO T VALUES (27), (28); -SELECT pk, c_int, c_bpchar, c_text, c_date, c_timestamp, - c_timestamp_null, c_array, c_small, c_small_null, - c_big, c_num, c_time, c_interval, - c_hugetext = repeat('abcdefg',1000) as c_hugetext_origdef, - c_hugetext = repeat('poiuyt', 1000) as c_hugetext_newdef -FROM T ORDER BY pk; - pk | c_int | c_bpchar | c_text | c_date | c_timestamp | c_timestamp_null | c_array | c_small | c_small_null | c_big | c_num | c_time | c_interval | c_hugetext_origdef | c_hugetext_newdef -----+-------+----------+--------+------------+--------------------------+--------------------------+--------------------------+---------+--------------+-------------------+-------------------+----------+------------+--------------------+------------------- - 1 | 1 | hello | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 2 | 1 | hello | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 3 | 2 | hello | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 4 | 2 | hello | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 5 | 2 | dog | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 6 | 2 | dog | world | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 7 | 2 | dog | cat | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 8 | 2 | dog | cat | 06-02-2016 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 9 | 2 | dog | cat | 01-01-2010 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 10 | 2 | dog | cat | 01-01-2010 | Thu Sep 01 12:00:00 2016 | | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 11 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 12 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,the,real,world} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 13 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 14 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | -5 | | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 15 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 16 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | 180000000000018 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 17 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 18 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 1.00000000001 | 12:00:00 | @ 1 day | t | f - 19 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 12:00:00 | @ 1 day | t | f - 20 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 12:00:00 | @ 1 day | t | f - 21 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 1 day | t | f - 22 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 1 day | t | f - 23 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 3 hours | t | f - 24 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 3 hours | t | f - 25 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | | f | t - 26 | 2 | dog | cat | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy} | 9 | 13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | | f | t - 27 | 2 | | | | | Thu Sep 29 12:00:00 2016 | | | 13 | | | | | | - 28 | 2 | | | | | Thu Sep 29 12:00:00 2016 | | | 13 | | | | | | -(28 rows) - -SELECT comp(); - comp ------------ - Unchanged -(1 row) - -DROP TABLE T; --- Test expressions in the defaults -CREATE OR REPLACE FUNCTION foo(a INT) RETURNS TEXT AS $$ -DECLARE res TEXT := ''; - i INT; -BEGIN - i := 0; - WHILE (i < a) LOOP - res := res || chr(ascii('a') + i); - i := i + 1; - END LOOP; - RETURN res; -END; $$ LANGUAGE PLPGSQL STABLE; -CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT LENGTH(foo(6))); -SELECT set('t'); - set ------ - -(1 row) - -INSERT INTO T VALUES (1), (2); -ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT foo(4), - ALTER COLUMN c_int SET DEFAULT LENGTH(foo(8)); -INSERT INTO T VALUES (3), (4); -ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT foo(6), - ALTER COLUMN c_bpchar SET DEFAULT foo(3); -INSERT INTO T VALUES (5), (6); -ALTER TABLE T ADD COLUMN c_date DATE - DEFAULT '2016-06-02'::DATE + LENGTH(foo(10)), - ALTER COLUMN c_text SET DEFAULT foo(12); -INSERT INTO T VALUES (7), (8); -ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP - DEFAULT '2016-09-01'::DATE + LENGTH(foo(10)), - ALTER COLUMN c_date - SET DEFAULT '2010-01-01'::DATE - LENGTH(foo(4)); -INSERT INTO T VALUES (9), (10); -ALTER TABLE T ADD COLUMN c_array TEXT[] - DEFAULT ('{"This", "is", "' || foo(4) || - '","the", "real", "world"}')::TEXT[], - ALTER COLUMN c_timestamp - SET DEFAULT '1970-12-31'::DATE + LENGTH(foo(30)); -INSERT INTO T VALUES (11), (12); -ALTER TABLE T ALTER COLUMN c_int DROP DEFAULT, - ALTER COLUMN c_array - SET DEFAULT ('{"This", "is", "' || foo(1) || - '", "fantasy"}')::text[]; -INSERT INTO T VALUES (13), (14); -ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, - ALTER COLUMN c_date DROP DEFAULT, - ALTER COLUMN c_text DROP DEFAULT, - ALTER COLUMN c_timestamp DROP DEFAULT, - ALTER COLUMN c_array DROP DEFAULT; -INSERT INTO T VALUES (15), (16); -SELECT * FROM T; - pk | c_int | c_bpchar | c_text | c_date | c_timestamp | c_array -----+-------+----------+--------------+------------+--------------------------+------------------------------- - 1 | 6 | abcd | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 2 | 6 | abcd | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 3 | 8 | abcd | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 4 | 8 | abcd | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 5 | 8 | abc | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 6 | 8 | abc | abcdef | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 7 | 8 | abc | abcdefghijkl | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 8 | 8 | abc | abcdefghijkl | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 9 | 8 | abc | abcdefghijkl | 12-28-2009 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 10 | 8 | abc | abcdefghijkl | 12-28-2009 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world} - 11 | 8 | abc | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,abcd,the,real,world} - 12 | 8 | abc | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,abcd,the,real,world} - 13 | | abc | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,a,fantasy} - 14 | | abc | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,a,fantasy} - 15 | | | | | | - 16 | | | | | | -(16 rows) - -SELECT comp(); - comp ------------ - Unchanged -(1 row) - -DROP TABLE T; -DROP FUNCTION foo(INT); --- Fall back to full rewrite for volatile expressions -CREATE TABLE T(pk INT NOT NULL PRIMARY KEY); -INSERT INTO T VALUES (1); -SELECT set('t'); - set ------ - -(1 row) - --- now() is stable, because it returns the transaction timestamp -ALTER TABLE T ADD COLUMN c1 TIMESTAMP DEFAULT now(); -SELECT comp(); - comp ------------ - Unchanged -(1 row) - --- clock_timestamp() is volatile -ALTER TABLE T ADD COLUMN c2 TIMESTAMP DEFAULT clock_timestamp(); -NOTICE: rewriting table t for reason 2 -SELECT comp(); - comp ------------ - Rewritten -(1 row) - -DROP TABLE T; --- Simple querie -CREATE TABLE T (pk INT NOT NULL PRIMARY KEY); -SELECT set('t'); - set ------ - -(1 row) - -INSERT INTO T SELECT * FROM generate_series(1, 10) a; -ALTER TABLE T ADD COLUMN c_bigint BIGINT NOT NULL DEFAULT -1; -INSERT INTO T SELECT b, b - 10 FROM generate_series(11, 20) a(b); -ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'hello'; -INSERT INTO T SELECT b, b - 10, (b + 10)::text FROM generate_series(21, 30) a(b); --- WHERE clause -SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1; - c_bigint | c_text -----------+-------- - -1 | hello -(1 row) - -EXPLAIN (VERBOSE TRUE, COSTS FALSE) -SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1; - QUERY PLAN ----------------------------------------------- - Limit - Output: c_bigint, c_text - -> Seq Scan on fast_default.t - Output: c_bigint, c_text - Filter: (t.c_bigint = '-1'::integer) -(5 rows) - -SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; - c_bigint | c_text -----------+-------- - -1 | hello -(1 row) - -EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; - QUERY PLAN --------------------------------------------- - Limit - Output: c_bigint, c_text - -> Seq Scan on fast_default.t - Output: c_bigint, c_text - Filter: (t.c_text = 'hello'::text) -(5 rows) - --- COALESCE -SELECT COALESCE(c_bigint, pk), COALESCE(c_text, pk::text) -FROM T -ORDER BY pk LIMIT 10; - coalesce | coalesce -----------+---------- - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello - -1 | hello -(10 rows) - --- Aggregate function -SELECT SUM(c_bigint), MAX(c_text COLLATE "C" ), MIN(c_text COLLATE "C") FROM T; - sum | max | min ------+-------+----- - 200 | hello | 31 -(1 row) - --- ORDER BY -SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; - pk | c_bigint | c_text -----+----------+-------- - 1 | -1 | hello - 2 | -1 | hello - 3 | -1 | hello - 4 | -1 | hello - 5 | -1 | hello - 6 | -1 | hello - 7 | -1 | hello - 8 | -1 | hello - 9 | -1 | hello - 10 | -1 | hello -(10 rows) - -EXPLAIN (VERBOSE TRUE, COSTS FALSE) -SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; - QUERY PLAN ----------------------------------------------- - Limit - Output: pk, c_bigint, c_text - -> Sort - Output: pk, c_bigint, c_text - Sort Key: t.c_bigint, t.c_text, t.pk - -> Seq Scan on fast_default.t - Output: pk, c_bigint, c_text -(7 rows) - --- LIMIT -SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10; - pk | c_bigint | c_text -----+----------+-------- - 11 | 1 | hello - 12 | 2 | hello - 13 | 3 | hello - 14 | 4 | hello - 15 | 5 | hello - 16 | 6 | hello - 17 | 7 | hello - 18 | 8 | hello - 19 | 9 | hello - 20 | 10 | hello -(10 rows) - -EXPLAIN (VERBOSE TRUE, COSTS FALSE) -SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10; - QUERY PLAN ----------------------------------------------------- - Limit - Output: pk, c_bigint, c_text - -> Sort - Output: pk, c_bigint, c_text - Sort Key: t.c_bigint, t.c_text, t.pk - -> Seq Scan on fast_default.t - Output: pk, c_bigint, c_text - Filter: (t.c_bigint > '-1'::integer) -(8 rows) - --- DELETE with RETURNING -DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; - pk | c_bigint | c_text -----+----------+-------- - 10 | -1 | hello - 11 | 1 | hello - 12 | 2 | hello - 13 | 3 | hello - 14 | 4 | hello - 15 | 5 | hello - 16 | 6 | hello - 17 | 7 | hello - 18 | 8 | hello - 19 | 9 | hello - 20 | 10 | hello -(11 rows) - -EXPLAIN (VERBOSE TRUE, COSTS FALSE) -DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; - QUERY PLAN ------------------------------------------------------------ - Delete on fast_default.t - Output: pk, c_bigint, c_text - -> Bitmap Heap Scan on fast_default.t - Output: ctid - Recheck Cond: ((t.pk >= 10) AND (t.pk <= 20)) - -> Bitmap Index Scan on t_pkey - Index Cond: ((t.pk >= 10) AND (t.pk <= 20)) -(7 rows) - --- UPDATE -UPDATE T SET c_text = '"' || c_text || '"' WHERE pk < 10; -SELECT * FROM T WHERE c_text LIKE '"%"' ORDER BY PK; - pk | c_bigint | c_text -----+----------+--------- - 1 | -1 | "hello" - 2 | -1 | "hello" - 3 | -1 | "hello" - 4 | -1 | "hello" - 5 | -1 | "hello" - 6 | -1 | "hello" - 7 | -1 | "hello" - 8 | -1 | "hello" - 9 | -1 | "hello" -(9 rows) - -SELECT comp(); - comp ------------ - Unchanged -(1 row) - -DROP TABLE T; --- Combine with other DDL -CREATE TABLE T(pk INT NOT NULL PRIMARY KEY); -SELECT set('t'); - set ------ - -(1 row) - -INSERT INTO T VALUES (1), (2); -ALTER TABLE T ADD COLUMN c_int INT NOT NULL DEFAULT -1; -INSERT INTO T VALUES (3), (4); -ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'Hello'; -INSERT INTO T VALUES (5), (6); -ALTER TABLE T ALTER COLUMN c_text SET DEFAULT 'world', - ALTER COLUMN c_int SET DEFAULT 1; -INSERT INTO T VALUES (7), (8); -SELECT * FROM T ORDER BY pk; - pk | c_int | c_text -----+-------+-------- - 1 | -1 | Hello - 2 | -1 | Hello - 3 | -1 | Hello - 4 | -1 | Hello - 5 | -1 | Hello - 6 | -1 | Hello - 7 | 1 | world - 8 | 1 | world -(8 rows) - --- Add an index -CREATE INDEX i ON T(c_int, c_text); -SELECT c_text FROM T WHERE c_int = -1; - c_text --------- - Hello - Hello - Hello - Hello - Hello - Hello -(6 rows) - -SELECT comp(); - comp ------------ - Unchanged -(1 row) - --- query to exercise expand_tuple function -CREATE TABLE t1 AS -SELECT 1::int AS a , 2::int AS b -FROM generate_series(1,20) q; -ALTER TABLE t1 ADD COLUMN c text; -SELECT a, - stddev(cast((SELECT sum(1) FROM generate_series(1,20) x) AS float4)) - OVER (PARTITION BY a,b,c ORDER BY b) - AS z -FROM t1; - a | z ----+--- - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 - 1 | 0 -(20 rows) - -DROP TABLE T; --- test that we account for missing columns without defaults correctly --- in expand_tuple, and that rows are correctly expanded for triggers -CREATE FUNCTION test_trigger() -RETURNS trigger -LANGUAGE plpgsql -AS $$ - -begin - raise notice 'old tuple: %', to_json(OLD)::text; - if TG_OP = 'DELETE' - then - return OLD; - else - return NEW; - end if; -end; - -$$; --- 2 new columns, both have defaults -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,3); -ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; -ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | 4 | 5 -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":4,"y":5} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | 4 | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, first has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,3); -ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; -ALTER TABLE t ADD COLUMN y int; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | 4 | -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":4,"y":null} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | 4 | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, second has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,3); -ALTER TABLE t ADD COLUMN x int; -ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | | 5 -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":null,"y":5} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, neither has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,3); -ALTER TABLE t ADD COLUMN x int; -ALTER TABLE t ADD COLUMN y int; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | | -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":null,"y":null} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | 3 | | 2 -(1 row) - -DROP TABLE t; --- same as last 4 tests but here the last original column has a NULL value --- 2 new columns, both have defaults -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,NULL); -ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; -ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | 4 | 5 -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":4,"y":5} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | 4 | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, first has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,NULL); -ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; -ALTER TABLE t ADD COLUMN y int; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | 4 | -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":4,"y":null} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | 4 | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, second has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,NULL); -ALTER TABLE t ADD COLUMN x int; -ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | | 5 -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":null,"y":5} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | | 2 -(1 row) - -DROP TABLE t; --- 2 new columns, neither has default -CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); -INSERT INTO t (a,b,c) VALUES (1,2,NULL); -ALTER TABLE t ADD COLUMN x int; -ALTER TABLE t ADD COLUMN y int; -CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | | -(1 row) - -UPDATE t SET y = 2; -NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":null,"y":null} -SELECT * FROM t; - id | a | b | c | x | y -----+---+---+---+---+--- - 1 | 1 | 2 | | | 2 -(1 row) - -DROP TABLE t; --- make sure expanded tuple has correct self pointer --- it will be required by the RI trigger doing the cascading delete -CREATE TABLE leader (a int PRIMARY KEY, b int); -CREATE TABLE follower (a int REFERENCES leader ON DELETE CASCADE, b int); -INSERT INTO leader VALUES (1, 1), (2, 2); -ALTER TABLE leader ADD c int; -ALTER TABLE leader DROP c; -DELETE FROM leader; --- check that ALTER TABLE ... ALTER TYPE does the right thing -CREATE TABLE vtype( a integer); -INSERT INTO vtype VALUES (1); -ALTER TABLE vtype ADD COLUMN b DOUBLE PRECISION DEFAULT 0.2; -ALTER TABLE vtype ADD COLUMN c BOOLEAN DEFAULT true; -SELECT * FROM vtype; - a | b | c ----+-----+--- - 1 | 0.2 | t -(1 row) - -ALTER TABLE vtype - ALTER b TYPE text USING b::text, - ALTER c TYPE text USING c::text; -NOTICE: rewriting table vtype for reason 4 -SELECT * FROM vtype; - a | b | c ----+-----+------ - 1 | 0.2 | true -(1 row) - --- also check the case that doesn't rewrite the table -CREATE TABLE vtype2 (a int); -INSERT INTO vtype2 VALUES (1); -ALTER TABLE vtype2 ADD COLUMN b varchar(10) DEFAULT 'xxx'; -ALTER TABLE vtype2 ALTER COLUMN b SET DEFAULT 'yyy'; -INSERT INTO vtype2 VALUES (2); -ALTER TABLE vtype2 ALTER COLUMN b TYPE varchar(20) USING b::varchar(20); -SELECT * FROM vtype2; - a | b ----+----- - 1 | xxx - 2 | yyy -(2 rows) - --- Ensure that defaults are checked when evaluating whether HOT update --- is possible, this was broken for a while: --- https://postgr.es/m/20190202133521.ylauh3ckqa7colzj%40alap3.anarazel.de -BEGIN; -CREATE TABLE t(); -INSERT INTO t DEFAULT VALUES; -ALTER TABLE t ADD COLUMN a int DEFAULT 1; -CREATE INDEX ON t(a); --- set column with a default 1 to NULL, due to a bug that wasn't --- noticed has heap_getattr buggily returned NULL for default columns -UPDATE t SET a = NULL; --- verify that index and non-index scans show the same result -SET LOCAL enable_seqscan = true; -SELECT * FROM t WHERE a IS NULL; - a ---- - -(1 row) - -SET LOCAL enable_seqscan = false; -SELECT * FROM t WHERE a IS NULL; - a ---- - -(1 row) - -ROLLBACK; --- verify that a default set on a non-plain table doesn't set a missing --- value on the attribute -CREATE FOREIGN DATA WRAPPER dummy; -CREATE SERVER s0 FOREIGN DATA WRAPPER dummy; -CREATE FOREIGN TABLE ft1 (c1 integer NOT NULL) SERVER s0; -ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer DEFAULT 0; -ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10); -SELECT count(*) - FROM pg_attribute - WHERE attrelid = 'ft1'::regclass AND - (attmissingval IS NOT NULL OR atthasmissing); - count -------- - 0 -(1 row) - --- cleanup -DROP FOREIGN TABLE ft1; -DROP SERVER s0; -DROP FOREIGN DATA WRAPPER dummy; -DROP TABLE vtype; -DROP TABLE vtype2; -DROP TABLE follower; -DROP TABLE leader; -DROP FUNCTION test_trigger(); -DROP TABLE t1; -DROP FUNCTION set(name); -DROP FUNCTION comp(); -DROP TABLE m; -DROP TABLE has_volatile; -DROP EVENT TRIGGER has_volatile_rewrite; -DROP FUNCTION log_rewrite; -DROP SCHEMA fast_default; --- Leave a table with an active fast default in place, for pg_upgrade testing -set search_path = public; -create table has_fast_default(f1 int); -insert into has_fast_default values(1); -alter table has_fast_default add column f2 int default 42; -table has_fast_default; - f1 | f2 -----+---- - 1 | 42 -(1 row) - +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -U3 /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/stats.out /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stats.out --- /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/expected/stats.out 2021-10-30 21:35:46 -0600 +++ /home/pgbfarm/buildroot-gharial-HEAD/postgresql/src/test/regress/results/stats.out 2021-10-30 22:09:27 -0600 @@ -1,204 +1,2 @@ --- --- Test Statistics Collector --- --- Must be run after tenk2 has been created (by create_table), --- populated (by create_misc) and indexed (by create_index). --- --- conditio sine qua non -SHOW track_counts; -- must be on - track_counts --------------- - on -(1 row) - --- ensure that both seqscan and indexscan plans are allowed -SET enable_seqscan TO on; -SET enable_indexscan TO on; --- for the moment, we don't want index-only scans here -SET enable_indexonlyscan TO off; --- save counters -CREATE TABLE prevstats AS -SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch, - (b.heap_blks_read + b.heap_blks_hit) AS heap_blks, - (b.idx_blks_read + b.idx_blks_hit) AS idx_blks, - pg_stat_get_snapshot_timestamp() as snap_ts - FROM pg_catalog.pg_stat_user_tables AS t, - pg_catalog.pg_statio_user_tables AS b - WHERE t.relname='tenk2' AND b.relname='tenk2'; --- function to wait for counters to advance -create function wait_for_stats() returns void as $$ -declare - start_time timestamptz := clock_timestamp(); - updated1 bool; - updated2 bool; - updated3 bool; - updated4 bool; -begin - -- we don't want to wait forever; loop will exit after 30 seconds - for i in 1 .. 300 loop - - -- With parallel query, the seqscan and indexscan on tenk2 might be done - -- in parallel worker processes, which will send their stats counters - -- asynchronously to what our own session does. So we must check for - -- those counts to be registered separately from the update counts. - - -- check to see if seqscan has been sensed - SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if indexscan has been sensed - SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if all updates have been sensed - SELECT (n_tup_ins > 0) INTO updated3 - FROM pg_stat_user_tables WHERE relname='trunc_stats_test4'; - - -- We must also check explicitly that pg_stat_get_snapshot_timestamp has - -- advanced, because that comes from the global stats file which might - -- be older than the per-DB stats file we got the other values from. - SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp()) INTO updated4 - FROM prevstats AS pr; - - exit when updated1 and updated2 and updated3 and updated4; - - -- wait a little - perform pg_sleep_for('100 milliseconds'); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - end loop; - - -- report time waited in postmaster log (where it won't change test output) - raise log 'wait_for_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -end -$$ language plpgsql; --- test effects of TRUNCATE on n_live_tup/n_dead_tup counters -CREATE TABLE trunc_stats_test(id serial); -CREATE TABLE trunc_stats_test1(id serial, stuff text); -CREATE TABLE trunc_stats_test2(id serial); -CREATE TABLE trunc_stats_test3(id serial, stuff text); -CREATE TABLE trunc_stats_test4(id serial); --- check that n_live_tup is reset to 0 after truncate -INSERT INTO trunc_stats_test DEFAULT VALUES; -INSERT INTO trunc_stats_test DEFAULT VALUES; -INSERT INTO trunc_stats_test DEFAULT VALUES; -TRUNCATE trunc_stats_test; --- test involving a truncate in a transaction; 4 ins but only 1 live -INSERT INTO trunc_stats_test1 DEFAULT VALUES; -INSERT INTO trunc_stats_test1 DEFAULT VALUES; -INSERT INTO trunc_stats_test1 DEFAULT VALUES; -UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2); -DELETE FROM trunc_stats_test1 WHERE id = 3; -BEGIN; -UPDATE trunc_stats_test1 SET id = id + 100; -TRUNCATE trunc_stats_test1; -INSERT INTO trunc_stats_test1 DEFAULT VALUES; -COMMIT; --- use a savepoint: 1 insert, 1 live -BEGIN; -INSERT INTO trunc_stats_test2 DEFAULT VALUES; -INSERT INTO trunc_stats_test2 DEFAULT VALUES; -SAVEPOINT p1; -INSERT INTO trunc_stats_test2 DEFAULT VALUES; -TRUNCATE trunc_stats_test2; -INSERT INTO trunc_stats_test2 DEFAULT VALUES; -RELEASE SAVEPOINT p1; -COMMIT; --- rollback a savepoint: this should count 4 inserts and have 2 --- live tuples after commit (and 2 dead ones due to aborted subxact) -BEGIN; -INSERT INTO trunc_stats_test3 DEFAULT VALUES; -INSERT INTO trunc_stats_test3 DEFAULT VALUES; -SAVEPOINT p1; -INSERT INTO trunc_stats_test3 DEFAULT VALUES; -INSERT INTO trunc_stats_test3 DEFAULT VALUES; -TRUNCATE trunc_stats_test3; -INSERT INTO trunc_stats_test3 DEFAULT VALUES; -ROLLBACK TO SAVEPOINT p1; -COMMIT; --- rollback a truncate: this should count 2 inserts and produce 2 dead tuples -BEGIN; -INSERT INTO trunc_stats_test4 DEFAULT VALUES; -INSERT INTO trunc_stats_test4 DEFAULT VALUES; -TRUNCATE trunc_stats_test4; -INSERT INTO trunc_stats_test4 DEFAULT VALUES; -ROLLBACK; --- do a seqscan -SELECT count(*) FROM tenk2; - count -------- - 10000 -(1 row) - --- do an indexscan --- make sure it is not a bitmap scan, which might skip fetching heap tuples -SET enable_bitmapscan TO off; -SELECT count(*) FROM tenk2 WHERE unique1 = 1; - count -------- - 1 -(1 row) - -RESET enable_bitmapscan; --- We can't just call wait_for_stats() at this point, because we only --- transmit stats when the session goes idle, and we probably didn't --- transmit the last couple of counts yet thanks to the rate-limiting logic --- in pgstat_report_stat(). But instead of waiting for the rate limiter's --- timeout to elapse, let's just start a new session. The old one will --- then send its stats before dying. -\c - --- wait for stats collector to update -SELECT wait_for_stats(); - wait_for_stats ----------------- - -(1 row) - --- check effects -SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup - FROM pg_stat_user_tables - WHERE relname like 'trunc_stats_test%' order by relname; - relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup --------------------+-----------+-----------+-----------+------------+------------ - trunc_stats_test | 3 | 0 | 0 | 0 | 0 - trunc_stats_test1 | 4 | 2 | 1 | 1 | 0 - trunc_stats_test2 | 1 | 0 | 0 | 1 | 0 - trunc_stats_test3 | 4 | 0 | 0 | 2 | 2 - trunc_stats_test4 | 2 | 0 | 0 | 0 | 2 -(5 rows) - -SELECT st.seq_scan >= pr.seq_scan + 1, - st.seq_tup_read >= pr.seq_tup_read + cl.reltuples, - st.idx_scan >= pr.idx_scan + 1, - st.idx_tup_fetch >= pr.idx_tup_fetch + 1 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - ?column? | ?column? | ?column? | ?column? -----------+----------+----------+---------- - t | t | t | t -(1 row) - -SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages, - st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1 - FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - ?column? | ?column? -----------+---------- - t | t -(1 row) - -SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp() as snapshot_newer -FROM prevstats AS pr; - snapshot_newer ----------------- - t -(1 row) - -DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4; -DROP TABLE prevstats; --- End of Stats Test +psql: error: connection to server on socket "/tmp/pg_regress-26978a/.s.PGSQL.51696" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached.