diff -w -U3 C:/cirrus/src/test/regress/expected/stats_ext.out C:/cirrus/build/testrun/regress/regress/results/stats_ext.out --- C:/cirrus/src/test/regress/expected/stats_ext.out 2024-04-05 16:07:25.921354400 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/stats_ext.out 2024-04-05 16:10:37.412972600 +0000 @@ -3100,193 +3100,7 @@ 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_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | b, c FROM stts_t2 | 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_t* - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+-------------------+-------------------+-----------+--------------+--------- - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | 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_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | b, c FROM stts_t2 | 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_t* - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+-------------------+-------------------+-----------+--------------+--------- - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | 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) - -create statistics (mcv) ON a, b, (a+b), (a-b) FROM stts_t1; -create statistics (mcv) ON a, b, (a+b), (a-b) FROM stts_t1; -create statistics (mcv) ON (a+b), (a-b) FROM stts_t1; -\dX stts_t*expr* - List of extended statistics - Schema | Name | Definition | Ndistinct | Dependencies | MCV ---------+-----------------------------+-------------------------------------+-----------+--------------+--------- - public | stts_t1_a_b_expr_expr_stat | a, b, (a + b), (a - b) FROM stts_t1 | | | defined - public | stts_t1_a_b_expr_expr_stat1 | a, b, (a + b), (a - b) FROM stts_t1 | | | defined - public | stts_t1_expr_expr_stat | (a + b), (a - b) FROM stts_t1 | | | defined -(3 rows) - -drop statistics stts_t1_a_b_expr_expr_stat; -drop statistics stts_t1_a_b_expr_expr_stat1; -drop statistics stts_t1_expr_expr_stat; -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_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | b, c FROM stts_t2 | 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_hoge | col1, col2, col3 FROM stts_t3 | defined | defined | defined - public | stts_t1_a_b_stat | a, b FROM stts_t1 | defined | | - public | stts_t1_a_b_stat1 | a, b FROM stts_t1 | defined | defined | - public | stts_t1_a_b_stat2 | a, b FROM stts_t1 | defined | defined | defined - public | stts_t2_b_c_stat | b, c FROM stts_t2 | 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 --- Check individual columns if we don't have table privilege -SELECT * FROM tststats.priv_test_tbl - WHERE a = 1 and tststats.priv_test_tbl.* > (1, 1) is not null; -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; +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -w -U3 C:/cirrus/src/test/regress/expected/select_parallel.out C:/cirrus/build/testrun/regress/regress/results/select_parallel.out --- C:/cirrus/src/test/regress/expected/select_parallel.out 2024-04-05 16:07:25.915914500 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/select_parallel.out 2024-04-05 16:10:39.370022700 +0000 @@ -1,1271 +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 (ANY ((two = (hashed SubPlan 1).col1) AND (four = (hashed SubPlan 1).col2)))) - 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 (ANY ((((unique1)::double precision + random()))::integer = (hashed SubPlan 1).col1))) - 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 - -> Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Seq Scan on tenk2 - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 - Filter: (unique1 = (InitPlan 1).col1) -(11 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; -create function parallel_safe_volatile(a int) returns int as - $$ begin return a; end; $$ parallel safe volatile language plpgsql; --- Test gather merge atop of a sort of a partial path -explain (costs off) -select * from tenk1 where four = 2 -order by four, hundred, parallel_safe_volatile(thousand); - QUERY PLAN ---------------------------------------------------------------- - Gather Merge - Workers Planned: 4 - -> Sort - Sort Key: hundred, (parallel_safe_volatile(thousand)) - -> Parallel Seq Scan on tenk1 - Filter: (four = 2) -(6 rows) - --- Test gather merge atop of an incremental sort a of partial path -set min_parallel_index_scan_size = 0; -set enable_seqscan = off; -explain (costs off) -select * from tenk1 where four = 2 -order by four, hundred, parallel_safe_volatile(thousand); - QUERY PLAN ---------------------------------------------------------------- - Gather Merge - Workers Planned: 4 - -> Incremental Sort - Sort Key: hundred, (parallel_safe_volatile(thousand)) - Presorted Key: hundred - -> Parallel Index Scan using tenk1_hundred on tenk1 - Filter: (four = 2) -(7 rows) - -reset min_parallel_index_scan_size; -reset enable_seqscan; --- Test GROUP BY with a gather merge path atop of a sort of a partial path -explain (costs off) -select count(*) from tenk1 -group by twenty, parallel_safe_volatile(two); - QUERY PLAN --------------------------------------------------------------------- - Finalize GroupAggregate - Group Key: twenty, (parallel_safe_volatile(two)) - -> Gather Merge - Workers Planned: 4 - -> Sort - Sort Key: twenty, (parallel_safe_volatile(two)) - -> Partial HashAggregate - Group Key: twenty, parallel_safe_volatile(two) - -> Parallel Seq Scan on tenk1 -(9 rows) - -drop function parallel_safe_volatile(int); -SAVEPOINT settings; -SET LOCAL debug_parallel_query = 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 debug_parallel_query = 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 debug_parallel_query = 1; -select count(*) from tenk1; - count -------- - 10000 -(1 row) - -reset debug_parallel_query; -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 debug_parallel_query = 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 debug_parallel_query = 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 - InitPlan 1 - -> Limit - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 tenk1_2 - Filter: (fivethous = 1) - -> Parallel Seq Scan on tenk1 - Filter: (fivethous = (InitPlan 1).col1) - -> Gather - Workers Planned: 4 - InitPlan 2 - -> Limit - -> Gather - Workers Planned: 4 - -> Parallel Seq Scan on tenk1 tenk1_3 - Filter: (fivethous = 1) - -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = (InitPlan 2).col1) -(23 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), ARRAY(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) - --- must disallow pushing sort below gather when pathkey contains an SRF -EXPLAIN (VERBOSE, COSTS OFF) -SELECT unnest(ARRAY[]::integer[]) + 1 AS pathkey - FROM tenk1 t1 JOIN tenk1 t2 ON TRUE - ORDER BY pathkey; - QUERY PLAN ------------------------------------------------------------------------------------------------------ - Sort - Output: (((unnest('{}'::integer[])) + 1)) - Sort Key: (((unnest('{}'::integer[])) + 1)) - -> Result - Output: ((unnest('{}'::integer[])) + 1) - -> ProjectSet - Output: unnest('{}'::integer[]) - -> Nested Loop - -> Gather - Workers Planned: 4 - -> Parallel Index Only Scan using tenk1_hundred on public.tenk1 t1 - -> Materialize - -> Gather - Workers Planned: 4 - -> Parallel Index Only Scan using tenk1_hundred on public.tenk1 t2 -(15 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 "c:/cirrus//.s.PGSQL.40048" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -w -U3 C:/cirrus/src/test/regress/expected/write_parallel.out C:/cirrus/build/testrun/regress/regress/results/write_parallel.out --- C:/cirrus/src/test/regress/expected/write_parallel.out 2024-04-05 16:07:25.957909400 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/write_parallel.out 2024-04-05 16:10:40.749399100 +0000 @@ -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 "c:/cirrus//.s.PGSQL.40048" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -w -U3 C:/cirrus/src/test/regress/expected/vacuum_parallel.out C:/cirrus/build/testrun/regress/regress/results/vacuum_parallel.out --- C:/cirrus/src/test/regress/expected/vacuum_parallel.out 2024-04-05 16:07:25.951072600 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/vacuum_parallel.out 2024-04-05 16:10:41.391971600 +0000 @@ -1,49 +1,2 @@ -SET max_parallel_maintenance_workers TO 4; -SET min_parallel_index_scan_size TO '128kB'; --- Bug #17245: Make sure that we don't totally fail to VACUUM individual indexes that --- happen to be below min_parallel_index_scan_size during parallel VACUUM: -CREATE TABLE parallel_vacuum_table (a int) WITH (autovacuum_enabled = off); -INSERT INTO parallel_vacuum_table SELECT i from generate_series(1, 10000) i; --- Parallel VACUUM will never be used unless there are at least two indexes --- that exceed min_parallel_index_scan_size. Create two such indexes, and --- a third index that is smaller than min_parallel_index_scan_size. -CREATE INDEX regular_sized_index ON parallel_vacuum_table(a); -CREATE INDEX typically_sized_index ON parallel_vacuum_table(a); --- Note: vacuum_in_leader_small_index can apply deduplication, making it ~3x --- smaller than the other indexes -CREATE INDEX vacuum_in_leader_small_index ON parallel_vacuum_table((1)); --- Verify (as best we can) that the cost model for parallel VACUUM --- will make our VACUUM run in parallel, while always leaving it up to the --- parallel leader to handle the vacuum_in_leader_small_index index: -SELECT EXISTS ( -SELECT 1 -FROM pg_class -WHERE oid = 'vacuum_in_leader_small_index'::regclass AND - pg_relation_size(oid) < - pg_size_bytes(current_setting('min_parallel_index_scan_size')) -) as leader_will_handle_small_index; - leader_will_handle_small_index --------------------------------- - t -(1 row) - -SELECT count(*) as trigger_parallel_vacuum_nindexes -FROM pg_class -WHERE oid in ('regular_sized_index'::regclass, 'typically_sized_index'::regclass) AND - pg_relation_size(oid) >= - pg_size_bytes(current_setting('min_parallel_index_scan_size')); - trigger_parallel_vacuum_nindexes ----------------------------------- - 2 -(1 row) - --- Parallel VACUUM with B-Tree page deletions, ambulkdelete calls: -DELETE FROM parallel_vacuum_table; -VACUUM (PARALLEL 4, INDEX_CLEANUP ON) parallel_vacuum_table; --- Since vacuum_in_leader_small_index uses deduplication, we expect an --- assertion failure with bug #17245 (in the absence of bugfix): -INSERT INTO parallel_vacuum_table SELECT i FROM generate_series(1, 10000) i; -RESET max_parallel_maintenance_workers; -RESET min_parallel_index_scan_size; --- Deliberately don't drop table, to get further coverage from tools like --- pg_amcheck in some testing scenarios +psql: error: connection to server on socket "c:/cirrus//.s.PGSQL.40048" failed: FATAL: the database system is not yet accepting connections +DETAIL: Consistent recovery state has not been yet reached. diff -w -U3 C:/cirrus/src/test/regress/expected/select_views.out C:/cirrus/build/testrun/regress/regress/results/select_views.out --- C:/cirrus/src/test/regress/expected/select_views.out 2024-04-05 16:07:25.915914500 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/select_views.out 2024-04-05 16:10:45.421756000 +0000 @@ -1254,299 +1254,7 @@ CREATE FUNCTION f_leak (text) RETURNS bool LANGUAGE 'plpgsql' COST 0.0000001 AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END'; -CREATE TABLE customer ( - cid int primary key, - name text not null, - tel text, - passwd text -); -CREATE TABLE credit_card ( - cid int references customer(cid), - cnum text, - climit int -); -CREATE TABLE credit_usage ( - cid int references customer(cid), - ymd date, - usage int -); -INSERT INTO customer - VALUES (101, 'regress_alice', '+81-12-3456-7890', 'passwd123'), - (102, 'regress_bob', '+01-234-567-8901', 'beafsteak'), - (103, 'regress_eve', '+49-8765-43210', 'hamburger'); -INSERT INTO credit_card - VALUES (101, '1111-2222-3333-4444', 4000), - (102, '5555-6666-7777-8888', 3000), - (103, '9801-2345-6789-0123', 2000); -INSERT INTO credit_usage - VALUES (101, '2011-09-15', 120), - (101, '2011-10-05', 90), - (101, '2011-10-18', 110), - (101, '2011-10-21', 200), - (101, '2011-11-10', 80), - (102, '2011-09-22', 300), - (102, '2011-10-12', 120), - (102, '2011-10-28', 200), - (103, '2011-10-15', 480); -CREATE VIEW my_property_normal AS - SELECT * FROM customer WHERE name = current_user; -CREATE VIEW my_property_secure WITH (security_barrier) AS - SELECT * FROM customer WHERE name = current_user; -CREATE VIEW my_credit_card_normal AS - SELECT * FROM customer l NATURAL JOIN credit_card r - WHERE l.name = current_user; -CREATE VIEW my_credit_card_secure WITH (security_barrier) AS - SELECT * FROM customer l NATURAL JOIN credit_card r - WHERE l.name = current_user; -CREATE VIEW my_credit_card_usage_normal AS - SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; -CREATE VIEW my_credit_card_usage_secure WITH (security_barrier) AS - SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; -GRANT SELECT ON my_property_normal TO public; -GRANT SELECT ON my_property_secure TO public; -GRANT SELECT ON my_credit_card_normal TO public; -GRANT SELECT ON my_credit_card_secure TO public; -GRANT SELECT ON my_credit_card_usage_normal TO public; -GRANT SELECT ON my_credit_card_usage_secure TO public; --- --- Run leaky view scenarios --- -SET SESSION AUTHORIZATION regress_alice; --- --- scenario: if a qualifier with tiny-cost is given, it shall be launched --- prior to the security policy of the view. --- -SELECT * FROM my_property_normal WHERE f_leak(passwd); -NOTICE: f_leak => passwd123 -NOTICE: f_leak => beafsteak -NOTICE: f_leak => hamburger - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_property_normal WHERE f_leak(passwd); - QUERY PLAN ------------------------------------------------------- - Seq Scan on customer - Filter: (f_leak(passwd) AND (name = CURRENT_USER)) -(2 rows) - -SELECT * FROM my_property_secure WHERE f_leak(passwd); -NOTICE: f_leak => passwd123 - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_property_secure WHERE f_leak(passwd); - QUERY PLAN ---------------------------------------------- - Subquery Scan on my_property_secure - Filter: f_leak(my_property_secure.passwd) - -> Seq Scan on customer - Filter: (name = CURRENT_USER) -(4 rows) - --- --- scenario: qualifiers can be pushed down if they contain leaky functions, --- provided they aren't passed data from inside the view. --- -SELECT * FROM my_property_normal v - WHERE f_leak('passwd') AND f_leak(passwd); -NOTICE: f_leak => passwd -NOTICE: f_leak => passwd123 -NOTICE: f_leak => passwd -NOTICE: f_leak => beafsteak -NOTICE: f_leak => passwd -NOTICE: f_leak => hamburger - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_property_normal v - WHERE f_leak('passwd') AND f_leak(passwd); - QUERY PLAN ---------------------------------------------------------------------------------- - Seq Scan on customer - Filter: (f_leak('passwd'::text) AND f_leak(passwd) AND (name = CURRENT_USER)) -(2 rows) - -SELECT * FROM my_property_secure v - WHERE f_leak('passwd') AND f_leak(passwd); -NOTICE: f_leak => passwd -NOTICE: f_leak => passwd123 -NOTICE: f_leak => passwd -NOTICE: f_leak => passwd - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_property_secure v - WHERE f_leak('passwd') AND f_leak(passwd); - QUERY PLAN --------------------------------------------------------------------- - Subquery Scan on v - Filter: f_leak(v.passwd) - -> Seq Scan on customer - Filter: (f_leak('passwd'::text) AND (name = CURRENT_USER)) -(4 rows) - --- --- scenario: if a qualifier references only one-side of a particular join- --- tree, it shall be distributed to the most deep scan plan as --- possible as we can. --- -SELECT * FROM my_credit_card_normal WHERE f_leak(cnum); -NOTICE: f_leak => 1111-2222-3333-4444 -NOTICE: f_leak => 5555-6666-7777-8888 -NOTICE: f_leak => 9801-2345-6789-0123 - cid | name | tel | passwd | cnum | climit ------+---------------+------------------+-----------+---------------------+-------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_normal WHERE f_leak(cnum); - QUERY PLAN ---------------------------------------------- - Hash Join - Hash Cond: (r.cid = l.cid) - -> Seq Scan on credit_card r - Filter: f_leak(cnum) - -> Hash - -> Seq Scan on customer l - Filter: (name = CURRENT_USER) -(7 rows) - -SELECT * FROM my_credit_card_secure WHERE f_leak(cnum); -NOTICE: f_leak => 1111-2222-3333-4444 - cid | name | tel | passwd | cnum | climit ------+---------------+------------------+-----------+---------------------+-------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 -(1 row) - -EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_secure WHERE f_leak(cnum); - QUERY PLAN ---------------------------------------------------- - Subquery Scan on my_credit_card_secure - Filter: f_leak(my_credit_card_secure.cnum) - -> Hash Join - Hash Cond: (r.cid = l.cid) - -> Seq Scan on credit_card r - -> Hash - -> Seq Scan on customer l - Filter: (name = CURRENT_USER) -(8 rows) - --- --- scenario: an external qualifier can be pushed-down by in-front-of the --- views with "security_barrier" attribute, except for operators --- implemented with leakproof functions. --- -SELECT * FROM my_credit_card_usage_normal - WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; -NOTICE: f_leak => 1111-2222-3333-4444 - cid | name | tel | passwd | cnum | climit | ymd | usage ------+---------------+------------------+-----------+---------------------+--------+------------+------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-05-2011 | 90 - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-18-2011 | 110 - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-21-2011 | 200 -(3 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_normal - WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; - QUERY PLAN ------------------------------------------------------------------------------- - Nested Loop - Join Filter: (l.cid = r.cid) - -> Seq Scan on credit_usage r - Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date)) - -> Materialize - -> Subquery Scan on l - Filter: f_leak(l.cnum) - -> Hash Join - Hash Cond: (r_1.cid = l_1.cid) - -> Seq Scan on credit_card r_1 - -> Hash - -> Seq Scan on customer l_1 - Filter: (name = CURRENT_USER) -(13 rows) - -SELECT * FROM my_credit_card_usage_secure - WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; -NOTICE: f_leak => 1111-2222-3333-4444 -NOTICE: f_leak => 1111-2222-3333-4444 -NOTICE: f_leak => 1111-2222-3333-4444 - cid | name | tel | passwd | cnum | climit | ymd | usage ------+---------------+------------------+-----------+---------------------+--------+------------+------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-05-2011 | 90 - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-18-2011 | 110 - 101 | regress_alice | +81-12-3456-7890 | passwd123 | 1111-2222-3333-4444 | 4000 | 10-21-2011 | 200 -(3 rows) - -EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure - WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; - QUERY PLAN ------------------------------------------------------------------------------------- - Subquery Scan on my_credit_card_usage_secure - Filter: f_leak(my_credit_card_usage_secure.cnum) - -> Nested Loop - Join Filter: (l.cid = r.cid) - -> Seq Scan on credit_usage r - Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date)) - -> Materialize - -> Hash Join - Hash Cond: (r_1.cid = l.cid) - -> Seq Scan on credit_card r_1 - -> Hash - -> Seq Scan on customer l - Filter: (name = CURRENT_USER) -(13 rows) - --- --- Test for the case when security_barrier gets changed between rewriter --- and planner stage. --- -PREPARE p1 AS SELECT * FROM my_property_normal WHERE f_leak(passwd); -PREPARE p2 AS SELECT * FROM my_property_secure WHERE f_leak(passwd); -EXECUTE p1; -NOTICE: f_leak => passwd123 -NOTICE: f_leak => beafsteak -NOTICE: f_leak => hamburger - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXECUTE p2; -NOTICE: f_leak => passwd123 - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -RESET SESSION AUTHORIZATION; -ALTER VIEW my_property_normal SET (security_barrier=true); -ALTER VIEW my_property_secure SET (security_barrier=false); -SET SESSION AUTHORIZATION regress_alice; -EXECUTE p1; -- To be perform as a view with security-barrier -NOTICE: f_leak => passwd123 - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - -EXECUTE p2; -- To be perform as a view without security-barrier -NOTICE: f_leak => passwd123 -NOTICE: f_leak => beafsteak -NOTICE: f_leak => hamburger - cid | name | tel | passwd ------+---------------+------------------+----------- - 101 | regress_alice | +81-12-3456-7890 | passwd123 -(1 row) - --- Cleanup. -RESET SESSION AUTHORIZATION; -DROP ROLE regress_alice; +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -w -U3 C:/cirrus/src/test/regress/expected/foreign_key.out C:/cirrus/build/testrun/regress/regress/results/foreign_key.out --- C:/cirrus/src/test/regress/expected/foreign_key.out 2024-04-05 16:07:25.816822100 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/foreign_key.out 2024-04-05 16:10:45.421756000 +0000 @@ -2254,678 +2254,10 @@ ALTER TABLE fk ATTACH PARTITION fk2 FOR VALUES FROM (750) TO (3500); 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 (c int, b int, a int NOT NULL) PARTITION BY RANGE (a); -ALTER TABLE pk5 DROP COLUMN b, DROP COLUMN c; -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); -CREATE TABLE fk3 PARTITION OF fk FOR VALUES FROM (3500) TO (5000); --- these should fail: referenced value not present -INSERT into fk VALUES (1); -ERROR: insert or update on table "fk1" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(1) is not present in table "pk". -INSERT into fk VALUES (1000); -ERROR: insert or update on table "fk2" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(1000) is not present in table "pk". -INSERT into fk VALUES (2000); -ERROR: insert or update on table "fk2" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(2000) is not present in table "pk". -INSERT into fk VALUES (3000); -ERROR: insert or update on table "fk2" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(3000) is not present in table "pk". -INSERT into fk VALUES (4000); -ERROR: insert or update on table "fk3" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(4000) is not present in table "pk". -INSERT into fk VALUES (4500); -ERROR: insert or update on table "fk3" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(4500) is not present in table "pk". --- insert into the referenced table, now they should work -INSERT into pk VALUES (1), (1000), (2000), (3000), (4000), (4500); -INSERT into fk VALUES (1), (1000), (2000), (3000), (4000), (4500); --- should fail: referencing value present -DELETE FROM pk WHERE a = 1; -ERROR: update or delete on table "pk1" violates foreign key constraint "fk_a_fkey1" on table "fk" -DETAIL: Key (a)=(1) is still referenced from table "fk". -DELETE FROM pk WHERE a = 1000; -ERROR: update or delete on table "pk2" violates foreign key constraint "fk_a_fkey2" on table "fk" -DETAIL: Key (a)=(1000) is still referenced from table "fk". -DELETE FROM pk WHERE a = 2000; -ERROR: update or delete on table "pk3" violates foreign key constraint "fk_a_fkey3" on table "fk" -DETAIL: Key (a)=(2000) is still referenced from table "fk". -DELETE FROM pk WHERE a = 3000; -ERROR: update or delete on table "pk4" violates foreign key constraint "fk_a_fkey4" on table "fk" -DETAIL: Key (a)=(3000) is still referenced from table "fk". -DELETE FROM pk WHERE a = 4000; -ERROR: update or delete on table "pk51" violates foreign key constraint "fk_a_fkey6" on table "fk" -DETAIL: Key (a)=(4000) is still referenced from table "fk". -DELETE FROM pk WHERE a = 4500; -ERROR: update or delete on table "pk52" violates foreign key constraint "fk_a_fkey7" on table "fk" -DETAIL: Key (a)=(4500) is still referenced from table "fk". -UPDATE pk SET a = 2 WHERE a = 1; -ERROR: update or delete on table "pk1" violates foreign key constraint "fk_a_fkey1" on table "fk" -DETAIL: Key (a)=(1) is still referenced from table "fk". -UPDATE pk SET a = 1002 WHERE a = 1000; -ERROR: update or delete on table "pk2" violates foreign key constraint "fk_a_fkey2" on table "fk" -DETAIL: Key (a)=(1000) is still referenced from table "fk". -UPDATE pk SET a = 2002 WHERE a = 2000; -ERROR: update or delete on table "pk3" violates foreign key constraint "fk_a_fkey3" on table "fk" -DETAIL: Key (a)=(2000) is still referenced from table "fk". -UPDATE pk SET a = 3002 WHERE a = 3000; -ERROR: update or delete on table "pk4" violates foreign key constraint "fk_a_fkey4" on table "fk" -DETAIL: Key (a)=(3000) is still referenced from table "fk". -UPDATE pk SET a = 4002 WHERE a = 4000; -ERROR: update or delete on table "pk51" violates foreign key constraint "fk_a_fkey6" on table "fk" -DETAIL: Key (a)=(4000) is still referenced from table "fk". -UPDATE pk SET a = 4502 WHERE a = 4500; -ERROR: update or delete on table "pk52" violates foreign key constraint "fk_a_fkey7" on table "fk" -DETAIL: Key (a)=(4500) is still referenced from table "fk". --- now they should work -DELETE FROM fk; -UPDATE pk SET a = 2 WHERE a = 1; -DELETE FROM pk WHERE a = 2; -UPDATE pk SET a = 1002 WHERE a = 1000; -DELETE FROM pk WHERE a = 1002; -UPDATE pk SET a = 2002 WHERE a = 2000; -DELETE FROM pk WHERE a = 2002; -UPDATE pk SET a = 3002 WHERE a = 3000; -DELETE FROM pk WHERE a = 3002; -UPDATE pk SET a = 4002 WHERE a = 4000; -DELETE FROM pk WHERE a = 4002; -UPDATE pk SET a = 4502 WHERE a = 4500; -DELETE FROM pk WHERE a = 4502; -CREATE SCHEMA fkpart4; -SET search_path TO fkpart4; --- dropping/detaching PARTITIONs is prevented if that would break --- a foreign key's existing data -CREATE TABLE droppk (a int PRIMARY KEY) PARTITION BY RANGE (a); -CREATE TABLE droppk1 PARTITION OF droppk FOR VALUES FROM (0) TO (1000); -CREATE TABLE droppk_d PARTITION OF droppk DEFAULT; -CREATE TABLE droppk2 PARTITION OF droppk FOR VALUES FROM (1000) TO (2000) - PARTITION BY RANGE (a); -CREATE TABLE droppk21 PARTITION OF droppk2 FOR VALUES FROM (1000) TO (1400); -CREATE TABLE droppk2_d PARTITION OF droppk2 DEFAULT; -INSERT into droppk VALUES (1), (1000), (1500), (2000); -CREATE TABLE dropfk (a int REFERENCES droppk); -INSERT into dropfk VALUES (1), (1000), (1500), (2000); --- these should all fail -ALTER TABLE droppk DETACH PARTITION droppk_d; -ERROR: removing partition "droppk_d" violates foreign key constraint "dropfk_a_fkey5" -DETAIL: Key (a)=(2000) is still referenced from table "dropfk". -ALTER TABLE droppk2 DETACH PARTITION droppk2_d; -ERROR: removing partition "droppk2_d" violates foreign key constraint "dropfk_a_fkey4" -DETAIL: Key (a)=(1500) is still referenced from table "dropfk". -ALTER TABLE droppk DETACH PARTITION droppk1; -ERROR: removing partition "droppk1" violates foreign key constraint "dropfk_a_fkey1" -DETAIL: Key (a)=(1) is still referenced from table "dropfk". -ALTER TABLE droppk DETACH PARTITION droppk2; -ERROR: removing partition "droppk2" violates foreign key constraint "dropfk_a_fkey2" -DETAIL: Key (a)=(1000) is still referenced from table "dropfk". -ALTER TABLE droppk2 DETACH PARTITION droppk21; -ERROR: removing partition "droppk21" violates foreign key constraint "dropfk_a_fkey3" -DETAIL: Key (a)=(1000) is still referenced from table "dropfk". --- dropping partitions is disallowed -DROP TABLE droppk_d; -ERROR: cannot drop table droppk_d because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk_d -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk2_d; -ERROR: cannot drop table droppk2_d because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk2_d -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk1; -ERROR: cannot drop table droppk1 because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk1 -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk2; -ERROR: cannot drop table droppk2 because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk2 -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk21; -ERROR: cannot drop table droppk21 because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk21 -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DELETE FROM dropfk; --- dropping partitions is disallowed, even when no referencing values -DROP TABLE droppk_d; -ERROR: cannot drop table droppk_d because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk_d -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk2_d; -ERROR: cannot drop table droppk2_d because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk2_d -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP TABLE droppk1; -ERROR: cannot drop table droppk1 because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk1 -HINT: Use DROP ... CASCADE to drop the dependent objects too. --- but DETACH is allowed, and DROP afterwards works -ALTER TABLE droppk2 DETACH PARTITION droppk21; -DROP TABLE droppk2; -ERROR: cannot drop table droppk2 because other objects depend on it -DETAIL: constraint dropfk_a_fkey on table dropfk depends on table droppk2 -HINT: Use DROP ... CASCADE to drop the dependent objects too. --- Verify that initial constraint creation and cloning behave correctly -CREATE SCHEMA fkpart5; -SET search_path TO fkpart5; -CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY LIST (a); -CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1) PARTITION BY LIST (a); -CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1); -CREATE TABLE fk (a int) PARTITION BY LIST (a); -CREATE TABLE fk1 PARTITION OF fk FOR VALUES IN (1) PARTITION BY LIST (a); -CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES IN (1); -ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk; -CREATE TABLE pk2 PARTITION OF pk FOR VALUES IN (2); -CREATE TABLE pk3 (a int NOT NULL) PARTITION BY LIST (a); -CREATE TABLE pk31 PARTITION OF pk3 FOR VALUES IN (31); -CREATE TABLE pk32 (b int, a int NOT NULL); -ALTER TABLE pk32 DROP COLUMN b; -ALTER TABLE pk3 ATTACH PARTITION pk32 FOR VALUES IN (32); -ALTER TABLE pk ATTACH PARTITION pk3 FOR VALUES IN (31, 32); -CREATE TABLE fk2 PARTITION OF fk FOR VALUES IN (2); -CREATE TABLE fk3 (b int, a int); -ALTER TABLE fk3 DROP COLUMN b; -ALTER TABLE fk ATTACH PARTITION fk3 FOR VALUES IN (3); -SELECT pg_describe_object('pg_constraint'::regclass, oid, 0), confrelid::regclass, - CASE WHEN conparentid <> 0 THEN pg_describe_object('pg_constraint'::regclass, conparentid, 0) ELSE 'TOP' END -FROM pg_catalog.pg_constraint -WHERE conrelid IN (SELECT relid FROM pg_partition_tree('fk')) -ORDER BY conrelid::regclass::text, conname; - pg_describe_object | confrelid | case -------------------------------------+-----------+----------------------------------- - constraint fk_a_fkey on table fk | pk | TOP - constraint fk_a_fkey1 on table fk | pk1 | constraint fk_a_fkey on table fk - constraint fk_a_fkey2 on table fk | pk11 | constraint fk_a_fkey1 on table fk - constraint fk_a_fkey3 on table fk | pk2 | constraint fk_a_fkey on table fk - constraint fk_a_fkey4 on table fk | pk3 | constraint fk_a_fkey on table fk - constraint fk_a_fkey5 on table fk | pk31 | constraint fk_a_fkey4 on table fk - constraint fk_a_fkey6 on table fk | pk32 | constraint fk_a_fkey4 on table fk - constraint fk_a_fkey on table fk1 | pk | constraint fk_a_fkey on table fk - constraint fk_a_fkey on table fk11 | pk | constraint fk_a_fkey on table fk1 - constraint fk_a_fkey on table fk2 | pk | constraint fk_a_fkey on table fk - constraint fk_a_fkey on table fk3 | pk | constraint fk_a_fkey on table fk -(11 rows) - -CREATE TABLE fk4 (LIKE fk); -INSERT INTO fk4 VALUES (50); -ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50); -ERROR: insert or update on table "fk4" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(50) is not present in table "pk". --- Verify constraint deferrability -CREATE SCHEMA fkpart9; -SET search_path TO fkpart9; -CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY LIST (a); -CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1, 2) PARTITION BY LIST (a); -CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1); -CREATE TABLE pk3 PARTITION OF pk FOR VALUES IN (3); -CREATE TABLE fk (a int REFERENCES pk DEFERRABLE INITIALLY IMMEDIATE); -INSERT INTO fk VALUES (1); -- should fail -ERROR: insert or update on table "fk" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(1) is not present in table "pk". -BEGIN; -SET CONSTRAINTS fk_a_fkey DEFERRED; -INSERT INTO fk VALUES (1); -COMMIT; -- should fail -ERROR: insert or update on table "fk" violates foreign key constraint "fk_a_fkey" -DETAIL: Key (a)=(1) is not present in table "pk". -BEGIN; -SET CONSTRAINTS fk_a_fkey DEFERRED; -INSERT INTO fk VALUES (1); -INSERT INTO pk VALUES (1); -COMMIT; -- OK -BEGIN; -SET CONSTRAINTS fk_a_fkey DEFERRED; -DELETE FROM pk WHERE a = 1; -DELETE FROM fk WHERE a = 1; -COMMIT; -- OK --- Verify constraint deferrability when changed by ALTER --- Partitioned table at referencing end -CREATE TABLE pt(f1 int, f2 int, f3 int, PRIMARY KEY(f1,f2)); -CREATE TABLE ref(f1 int, f2 int, f3 int) - PARTITION BY list(f1); -CREATE TABLE ref1 PARTITION OF ref FOR VALUES IN (1); -CREATE TABLE ref2 PARTITION OF ref FOR VALUES in (2); -ALTER TABLE ref ADD FOREIGN KEY(f1,f2) REFERENCES pt; -ALTER TABLE ref ALTER CONSTRAINT ref_f1_f2_fkey - DEFERRABLE INITIALLY DEFERRED; -INSERT INTO pt VALUES(1,2,3); -INSERT INTO ref VALUES(1,2,3); -BEGIN; -DELETE FROM pt; -DELETE FROM ref; -ABORT; -DROP TABLE pt, ref; --- Multi-level partitioning at referencing end -CREATE TABLE pt(f1 int, f2 int, f3 int, PRIMARY KEY(f1,f2)); -CREATE TABLE ref(f1 int, f2 int, f3 int) - PARTITION BY list(f1); -CREATE TABLE ref1_2 PARTITION OF ref FOR VALUES IN (1, 2) PARTITION BY list (f2); -CREATE TABLE ref1 PARTITION OF ref1_2 FOR VALUES IN (1); -CREATE TABLE ref2 PARTITION OF ref1_2 FOR VALUES IN (2) PARTITION BY list (f2); -CREATE TABLE ref22 PARTITION OF ref2 FOR VALUES IN (2); -ALTER TABLE ref ADD FOREIGN KEY(f1,f2) REFERENCES pt; -INSERT INTO pt VALUES(1,2,3); -INSERT INTO ref VALUES(1,2,3); -ALTER TABLE ref22 ALTER CONSTRAINT ref_f1_f2_fkey - DEFERRABLE INITIALLY IMMEDIATE; -- fails -ERROR: cannot alter constraint "ref_f1_f2_fkey" on relation "ref22" -DETAIL: Constraint "ref_f1_f2_fkey" is derived from constraint "ref_f1_f2_fkey" of relation "ref". -HINT: You may alter the constraint it derives from instead. -ALTER TABLE ref ALTER CONSTRAINT ref_f1_f2_fkey - DEFERRABLE INITIALLY DEFERRED; -BEGIN; -DELETE FROM pt; -DELETE FROM ref; -ABORT; -DROP TABLE pt, ref; --- Partitioned table at referenced end -CREATE TABLE pt(f1 int, f2 int, f3 int, PRIMARY KEY(f1,f2)) - PARTITION BY LIST(f1); -CREATE TABLE pt1 PARTITION OF pt FOR VALUES IN (1); -CREATE TABLE pt2 PARTITION OF pt FOR VALUES IN (2); -CREATE TABLE ref(f1 int, f2 int, f3 int); -ALTER TABLE ref ADD FOREIGN KEY(f1,f2) REFERENCES pt; -ALTER TABLE ref ALTER CONSTRAINT ref_f1_f2_fkey - DEFERRABLE INITIALLY DEFERRED; -INSERT INTO pt VALUES(1,2,3); -INSERT INTO ref VALUES(1,2,3); -BEGIN; -DELETE FROM pt; -DELETE FROM ref; -ABORT; -DROP TABLE pt, ref; --- Multi-level partitioning at referenced end -CREATE TABLE pt(f1 int, f2 int, f3 int, PRIMARY KEY(f1,f2)) - PARTITION BY LIST(f1); -CREATE TABLE pt1_2 PARTITION OF pt FOR VALUES IN (1, 2) PARTITION BY LIST (f1); -CREATE TABLE pt1 PARTITION OF pt1_2 FOR VALUES IN (1); -CREATE TABLE pt2 PARTITION OF pt1_2 FOR VALUES IN (2); -CREATE TABLE ref(f1 int, f2 int, f3 int); -ALTER TABLE ref ADD FOREIGN KEY(f1,f2) REFERENCES pt; -ALTER TABLE ref ALTER CONSTRAINT ref_f1_f2_fkey1 - DEFERRABLE INITIALLY DEFERRED; -- fails -ERROR: cannot alter constraint "ref_f1_f2_fkey1" on relation "ref" -DETAIL: Constraint "ref_f1_f2_fkey1" is derived from constraint "ref_f1_f2_fkey" of relation "ref". -HINT: You may alter the constraint it derives from instead. -ALTER TABLE ref ALTER CONSTRAINT ref_f1_f2_fkey - DEFERRABLE INITIALLY DEFERRED; -INSERT INTO pt VALUES(1,2,3); -INSERT INTO ref VALUES(1,2,3); -BEGIN; -DELETE FROM pt; -DELETE FROM ref; -ABORT; -DROP TABLE pt, ref; -DROP SCHEMA fkpart9 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table pk -drop cascades to table fk --- Verify ON UPDATE/DELETE behavior -CREATE SCHEMA fkpart6; -SET search_path TO fkpart6; -CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY RANGE (a); -CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); -CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES FROM (1) TO (50); -CREATE TABLE pk12 PARTITION OF pk1 FOR VALUES FROM (50) TO (100); -CREATE TABLE fk (a int) PARTITION BY RANGE (a); -CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); -CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); -CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); -ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE CASCADE ON DELETE CASCADE; -CREATE TABLE fk_d PARTITION OF fk DEFAULT; -INSERT INTO pk VALUES (1); -INSERT INTO fk VALUES (1); -UPDATE pk SET a = 20; -SELECT tableoid::regclass, * FROM fk; - tableoid | a -----------+---- - fk12 | 20 -(1 row) - -DELETE FROM pk WHERE a = 20; -SELECT tableoid::regclass, * FROM fk; - tableoid | a -----------+--- -(0 rows) - -DROP TABLE fk; -TRUNCATE TABLE pk; -INSERT INTO pk VALUES (20), (50); -CREATE TABLE fk (a int) PARTITION BY RANGE (a); -CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); -CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); -CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); -ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET NULL ON DELETE SET NULL; -CREATE TABLE fk_d PARTITION OF fk DEFAULT; -INSERT INTO fk VALUES (20), (50); -UPDATE pk SET a = 21 WHERE a = 20; -DELETE FROM pk WHERE a = 50; -SELECT tableoid::regclass, * FROM fk; - tableoid | a -----------+--- - fk_d | - fk_d | -(2 rows) - -DROP TABLE fk; -TRUNCATE TABLE pk; -INSERT INTO pk VALUES (20), (30), (50); -CREATE TABLE fk (id int, a int DEFAULT 50) PARTITION BY RANGE (a); -CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); -CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); -CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); -ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET DEFAULT ON DELETE SET DEFAULT; -CREATE TABLE fk_d PARTITION OF fk DEFAULT; -INSERT INTO fk VALUES (1, 20), (2, 30); -DELETE FROM pk WHERE a = 20 RETURNING *; - a ----- - 20 -(1 row) - -UPDATE pk SET a = 90 WHERE a = 30 RETURNING *; - a ----- - 90 -(1 row) - -SELECT tableoid::regclass, * FROM fk; - tableoid | id | a -----------+----+---- - fk12 | 1 | 50 - fk12 | 2 | 50 -(2 rows) - -DROP TABLE fk; -TRUNCATE TABLE pk; -INSERT INTO pk VALUES (20), (30); -CREATE TABLE fk (a int DEFAULT 50) PARTITION BY RANGE (a); -CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); -CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); -CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); -ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE RESTRICT ON DELETE RESTRICT; -CREATE TABLE fk_d PARTITION OF fk DEFAULT; -INSERT INTO fk VALUES (20), (30); -DELETE FROM pk WHERE a = 20; -ERROR: update or delete on table "pk11" violates foreign key constraint "fk_a_fkey2" on table "fk" -DETAIL: Key (a)=(20) is still referenced from table "fk". -UPDATE pk SET a = 90 WHERE a = 30; -ERROR: update or delete on table "pk" violates foreign key constraint "fk_a_fkey" on table "fk" -DETAIL: Key (a)=(30) is still referenced from table "fk". -SELECT tableoid::regclass, * FROM fk; - tableoid | a -----------+---- - fk12 | 20 - fk12 | 30 -(2 rows) - -DROP TABLE fk; --- test for reported bug: relispartition not set --- https://postgr.es/m/CA+HiwqHMsRtRYRWYTWavKJ8x14AFsv7bmAV46mYwnfD3vy8goQ@mail.gmail.com -CREATE SCHEMA fkpart7 - CREATE TABLE pkpart (a int) PARTITION BY LIST (a) - CREATE TABLE pkpart1 PARTITION OF pkpart FOR VALUES IN (1); -ALTER TABLE fkpart7.pkpart1 ADD PRIMARY KEY (a); -ALTER TABLE fkpart7.pkpart ADD PRIMARY KEY (a); -CREATE TABLE fkpart7.fk (a int REFERENCES fkpart7.pkpart); -DROP SCHEMA fkpart7 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table fkpart7.pkpart -drop cascades to table fkpart7.fk --- ensure we check partitions are "not used" when dropping constraints -CREATE SCHEMA fkpart8 - CREATE TABLE tbl1(f1 int PRIMARY KEY) - CREATE TABLE tbl2(f1 int REFERENCES tbl1 DEFERRABLE INITIALLY DEFERRED) PARTITION BY RANGE(f1) - CREATE TABLE tbl2_p1 PARTITION OF tbl2 FOR VALUES FROM (minvalue) TO (maxvalue); -INSERT INTO fkpart8.tbl1 VALUES(1); -BEGIN; -INSERT INTO fkpart8.tbl2 VALUES(1); -ALTER TABLE fkpart8.tbl2 DROP CONSTRAINT tbl2_f1_fkey; -ERROR: cannot ALTER TABLE "tbl2_p1" because it has pending trigger events -COMMIT; -DROP SCHEMA fkpart8 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table fkpart8.tbl1 -drop cascades to table fkpart8.tbl2 --- ensure FK referencing a multi-level partitioned table are --- enforce reference to sub-children. -CREATE SCHEMA fkpart9 - CREATE TABLE pk (a INT PRIMARY KEY) PARTITION BY RANGE (a) - CREATE TABLE fk ( - fk_a INT REFERENCES pk(a) ON DELETE CASCADE - ) - CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (30) TO (50) PARTITION BY RANGE (a) - CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES FROM (30) TO (40); -INSERT INTO fkpart9.pk VALUES (35); -INSERT INTO fkpart9.fk VALUES (35); -DELETE FROM fkpart9.pk WHERE a=35; -SELECT * FROM fkpart9.pk; - a ---- -(0 rows) - -SELECT * FROM fkpart9.fk; - fk_a ------- -(0 rows) - -DROP SCHEMA fkpart9 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table fkpart9.pk -drop cascades to table fkpart9.fk --- test that ri_Check_Pk_Match() scans the correct partition for a deferred --- ON DELETE/UPDATE NO ACTION constraint -CREATE SCHEMA fkpart10 - CREATE TABLE tbl1(f1 int PRIMARY KEY) PARTITION BY RANGE(f1) - CREATE TABLE tbl1_p1 PARTITION OF tbl1 FOR VALUES FROM (minvalue) TO (1) - CREATE TABLE tbl1_p2 PARTITION OF tbl1 FOR VALUES FROM (1) TO (maxvalue) - CREATE TABLE tbl2(f1 int REFERENCES tbl1 DEFERRABLE INITIALLY DEFERRED) - CREATE TABLE tbl3(f1 int PRIMARY KEY) PARTITION BY RANGE(f1) - CREATE TABLE tbl3_p1 PARTITION OF tbl3 FOR VALUES FROM (minvalue) TO (1) - CREATE TABLE tbl3_p2 PARTITION OF tbl3 FOR VALUES FROM (1) TO (maxvalue) - CREATE TABLE tbl4(f1 int REFERENCES tbl3 DEFERRABLE INITIALLY DEFERRED); -INSERT INTO fkpart10.tbl1 VALUES (0), (1); -INSERT INTO fkpart10.tbl2 VALUES (0), (1); -INSERT INTO fkpart10.tbl3 VALUES (-2), (-1), (0); -INSERT INTO fkpart10.tbl4 VALUES (-2), (-1); -BEGIN; -DELETE FROM fkpart10.tbl1 WHERE f1 = 0; -UPDATE fkpart10.tbl1 SET f1 = 2 WHERE f1 = 1; -INSERT INTO fkpart10.tbl1 VALUES (0), (1); -COMMIT; --- test that cross-partition updates correctly enforces the foreign key --- restriction (specifically testing INITIAILLY DEFERRED) -BEGIN; -UPDATE fkpart10.tbl1 SET f1 = 3 WHERE f1 = 0; -UPDATE fkpart10.tbl3 SET f1 = f1 * -1; -INSERT INTO fkpart10.tbl1 VALUES (4); -COMMIT; -ERROR: update or delete on table "tbl1" violates foreign key constraint "tbl2_f1_fkey" on table "tbl2" -DETAIL: Key (f1)=(0) is still referenced from table "tbl2". -BEGIN; -UPDATE fkpart10.tbl3 SET f1 = f1 * -1; -UPDATE fkpart10.tbl3 SET f1 = f1 + 3; -UPDATE fkpart10.tbl1 SET f1 = 3 WHERE f1 = 0; -INSERT INTO fkpart10.tbl1 VALUES (0); -COMMIT; -ERROR: update or delete on table "tbl3" violates foreign key constraint "tbl4_f1_fkey" on table "tbl4" -DETAIL: Key (f1)=(-2) is still referenced from table "tbl4". -BEGIN; -UPDATE fkpart10.tbl3 SET f1 = f1 * -1; -UPDATE fkpart10.tbl1 SET f1 = 3 WHERE f1 = 0; -INSERT INTO fkpart10.tbl1 VALUES (0); -INSERT INTO fkpart10.tbl3 VALUES (-2), (-1); -COMMIT; --- test where the updated table now has both an IMMEDIATE and a DEFERRED --- constraint pointing into it -CREATE TABLE fkpart10.tbl5(f1 int REFERENCES fkpart10.tbl3); -INSERT INTO fkpart10.tbl5 VALUES (-2), (-1); -BEGIN; -UPDATE fkpart10.tbl3 SET f1 = f1 * -3; -ERROR: update or delete on table "tbl3" violates foreign key constraint "tbl5_f1_fkey" on table "tbl5" -DETAIL: Key (f1)=(-2) is still referenced from table "tbl5". -COMMIT; --- Now test where the row referenced from the table with an IMMEDIATE --- constraint stays in place, while those referenced from the table with a --- DEFERRED constraint don't. -DELETE FROM fkpart10.tbl5; -INSERT INTO fkpart10.tbl5 VALUES (0); -BEGIN; -UPDATE fkpart10.tbl3 SET f1 = f1 * -3; -COMMIT; -ERROR: update or delete on table "tbl3" violates foreign key constraint "tbl4_f1_fkey" on table "tbl4" -DETAIL: Key (f1)=(-2) is still referenced from table "tbl4". -DROP SCHEMA fkpart10 CASCADE; -NOTICE: drop cascades to 5 other objects -DETAIL: drop cascades to table fkpart10.tbl1 -drop cascades to table fkpart10.tbl2 -drop cascades to table fkpart10.tbl3 -drop cascades to table fkpart10.tbl4 -drop cascades to table fkpart10.tbl5 --- verify foreign keys are enforced during cross-partition updates, --- especially on the PK side -CREATE SCHEMA fkpart11 - CREATE TABLE pk (a INT PRIMARY KEY, b text) PARTITION BY LIST (a) - CREATE TABLE fk ( - a INT, - CONSTRAINT fkey FOREIGN KEY (a) REFERENCES pk(a) ON UPDATE CASCADE ON DELETE CASCADE - ) - CREATE TABLE fk_parted ( - a INT PRIMARY KEY, - CONSTRAINT fkey FOREIGN KEY (a) REFERENCES pk(a) ON UPDATE CASCADE ON DELETE CASCADE - ) PARTITION BY LIST (a) - CREATE TABLE fk_another ( - a INT, - CONSTRAINT fkey FOREIGN KEY (a) REFERENCES fk_parted (a) ON UPDATE CASCADE ON DELETE CASCADE - ) - CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1, 2) PARTITION BY LIST (a) - CREATE TABLE pk2 PARTITION OF pk FOR VALUES IN (3) - CREATE TABLE pk3 PARTITION OF pk FOR VALUES IN (4) - CREATE TABLE fk1 PARTITION OF fk_parted FOR VALUES IN (1, 2) - CREATE TABLE fk2 PARTITION OF fk_parted FOR VALUES IN (3) - CREATE TABLE fk3 PARTITION OF fk_parted FOR VALUES IN (4); -CREATE TABLE fkpart11.pk11 (b text, a int NOT NULL); -ALTER TABLE fkpart11.pk1 ATTACH PARTITION fkpart11.pk11 FOR VALUES IN (1); -CREATE TABLE fkpart11.pk12 (b text, c int, a int NOT NULL); -ALTER TABLE fkpart11.pk12 DROP c; -ALTER TABLE fkpart11.pk1 ATTACH PARTITION fkpart11.pk12 FOR VALUES IN (2); -INSERT INTO fkpart11.pk VALUES (1, 'xxx'), (3, 'yyy'); -INSERT INTO fkpart11.fk VALUES (1), (3); -INSERT INTO fkpart11.fk_parted VALUES (1), (3); -INSERT INTO fkpart11.fk_another VALUES (1), (3); --- moves 2 rows from one leaf partition to another, with both updates being --- cascaded to fk and fk_parted. Updates of fk_parted, of which one is --- cross-partition (3 -> 4), are further cascaded to fk_another. -UPDATE fkpart11.pk SET a = a + 1 RETURNING tableoid::pg_catalog.regclass, *; - tableoid | a | b ----------------+---+----- - fkpart11.pk12 | 2 | xxx - fkpart11.pk3 | 4 | yyy -(2 rows) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk; - tableoid | a --------------+--- - fkpart11.fk | 2 - fkpart11.fk | 4 -(2 rows) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk_parted; - tableoid | a ---------------+--- - fkpart11.fk1 | 2 - fkpart11.fk3 | 4 -(2 rows) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk_another; - tableoid | a ----------------------+--- - fkpart11.fk_another | 2 - fkpart11.fk_another | 4 -(2 rows) - --- let's try with the foreign key pointing at tables in the partition tree --- that are not the same as the query's target table --- 1. foreign key pointing into a non-root ancestor --- --- A cross-partition update on the root table will fail, because we currently --- can't enforce the foreign keys pointing into a non-leaf partition -ALTER TABLE fkpart11.fk DROP CONSTRAINT fkey; -DELETE FROM fkpart11.fk WHERE a = 4; -ALTER TABLE fkpart11.fk ADD CONSTRAINT fkey FOREIGN KEY (a) REFERENCES fkpart11.pk1 (a) ON UPDATE CASCADE ON DELETE CASCADE; -UPDATE fkpart11.pk SET a = a - 1; -ERROR: cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key -DETAIL: A foreign key points to ancestor "pk1" but not the root ancestor "pk". -HINT: Consider defining the foreign key on table "pk". --- it's okay though if the non-leaf partition is updated directly -UPDATE fkpart11.pk1 SET a = a - 1; -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.pk; - tableoid | a | b ----------------+---+----- - fkpart11.pk11 | 1 | xxx - fkpart11.pk3 | 4 | yyy -(2 rows) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk; - tableoid | a --------------+--- - fkpart11.fk | 1 -(1 row) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk_parted; - tableoid | a ---------------+--- - fkpart11.fk1 | 1 - fkpart11.fk3 | 4 -(2 rows) - -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk_another; - tableoid | a ----------------------+--- - fkpart11.fk_another | 4 - fkpart11.fk_another | 1 -(2 rows) - --- 2. foreign key pointing into a single leaf partition --- --- A cross-partition update that deletes from the pointed-to leaf partition --- is allowed to succeed -ALTER TABLE fkpart11.fk DROP CONSTRAINT fkey; -ALTER TABLE fkpart11.fk ADD CONSTRAINT fkey FOREIGN KEY (a) REFERENCES fkpart11.pk11 (a) ON UPDATE CASCADE ON DELETE CASCADE; --- will delete (1) from p11 which is cascaded to fk -UPDATE fkpart11.pk SET a = a + 1 WHERE a = 1; -SELECT tableoid::pg_catalog.regclass, * FROM fkpart11.fk; - tableoid | a -----------+--- -(0 rows) - -DROP TABLE fkpart11.fk; --- check that regular and deferrable AR triggers on the PK tables --- still work as expected -CREATE FUNCTION fkpart11.print_row () RETURNS TRIGGER LANGUAGE plpgsql AS $$ - BEGIN - RAISE NOTICE 'TABLE: %, OP: %, OLD: %, NEW: %', TG_RELNAME, TG_OP, OLD, NEW; - RETURN NULL; - END; -$$; -CREATE TRIGGER trig_upd_pk AFTER UPDATE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -CREATE TRIGGER trig_del_pk AFTER DELETE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -CREATE TRIGGER trig_ins_pk AFTER INSERT ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -CREATE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -CREATE CONSTRAINT TRIGGER trig_del_fk_parted AFTER DELETE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -CREATE CONSTRAINT TRIGGER trig_ins_fk_parted AFTER INSERT ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row(); -UPDATE fkpart11.pk SET a = 3 WHERE a = 4; -NOTICE: TABLE: pk3, OP: DELETE, OLD: (4,yyy), NEW: -NOTICE: TABLE: pk2, OP: INSERT, OLD: , NEW: (3,yyy) -NOTICE: TABLE: fk3, OP: DELETE, OLD: (4), NEW: -NOTICE: TABLE: fk2, OP: INSERT, OLD: , NEW: (3) -UPDATE fkpart11.pk SET a = 1 WHERE a = 2; -NOTICE: TABLE: pk12, OP: DELETE, OLD: (xxx,2), NEW: -NOTICE: TABLE: pk11, OP: INSERT, OLD: , NEW: (xxx,1) -NOTICE: TABLE: fk1, OP: UPDATE, OLD: (2), NEW: (1) -DROP SCHEMA fkpart11 CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table fkpart11.pk -drop cascades to table fkpart11.fk_parted -drop cascades to table fkpart11.fk_another -drop cascades to function fkpart11.print_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 -w -U3 C:/cirrus/src/test/regress/expected/bitmapops.out C:/cirrus/build/testrun/regress/regress/results/bitmapops.out --- C:/cirrus/src/test/regress/expected/bitmapops.out 2024-04-05 16:07:25.770905600 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/bitmapops.out 2024-04-05 16:10:45.421756000 +0000 @@ -12,27 +12,10 @@ INSERT INTO bmscantest SELECT (r%53), (r%59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1,70000) r; -CREATE INDEX i_bmtest_a ON bmscantest(a); -CREATE INDEX i_bmtest_b ON bmscantest(b); --- We want to use bitmapscans. With default settings, the planner currently --- chooses a bitmap scan for the queries below anyway, but let's make sure. -set enable_indexscan=false; -set enable_seqscan=false; --- Lower work_mem to trigger use of lossy bitmaps -set work_mem = 64; --- Test bitmap-and. -SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1; - count -------- - 23 -(1 row) - --- Test bitmap-or. -SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1; - count -------- - 2485 -(1 row) - --- clean up -DROP TABLE bmscantest; +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 -w -U3 C:/cirrus/src/test/regress/expected/tsdicts.out C:/cirrus/build/testrun/regress/regress/results/tsdicts.out --- C:/cirrus/src/test/regress/expected/tsdicts.out 2024-04-05 16:07:25.937311600 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/tsdicts.out 2024-04-05 16:10:45.421756000 +0000 @@ -1,723 +1,3 @@ ---Test text search dictionaries and configurations --- Test ISpell dictionary with ispell affix file -CREATE TEXT SEARCH DICTIONARY ispell ( - Template=ispell, - DictFile=ispell_sample, - AffFile=ispell_sample -); -SELECT ts_lexize('ispell', 'skies'); - ts_lexize ------------ - {sky} -(1 row) - -SELECT ts_lexize('ispell', 'bookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('ispell', 'booking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('ispell', 'foot'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('ispell', 'foots'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('ispell', 'rebookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('ispell', 'rebooking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('ispell', 'rebook'); - ts_lexize ------------ - -(1 row) - -SELECT ts_lexize('ispell', 'unbookings'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('ispell', 'unbooking'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('ispell', 'unbook'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('ispell', 'footklubber'); - ts_lexize ----------------- - {foot,klubber} -(1 row) - -SELECT ts_lexize('ispell', 'footballklubber'); - ts_lexize ------------------------------------------------------- - {footballklubber,foot,ball,klubber,football,klubber} -(1 row) - -SELECT ts_lexize('ispell', 'ballyklubber'); - ts_lexize ----------------- - {ball,klubber} -(1 row) - -SELECT ts_lexize('ispell', 'footballyklubber'); - ts_lexize ---------------------- - {foot,ball,klubber} -(1 row) - --- Test ISpell dictionary with hunspell affix file -CREATE TEXT SEARCH DICTIONARY hunspell ( - Template=ispell, - DictFile=ispell_sample, - AffFile=hunspell_sample -); -SELECT ts_lexize('hunspell', 'skies'); - ts_lexize ------------ - {sky} -(1 row) - -SELECT ts_lexize('hunspell', 'bookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell', 'booking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell', 'foot'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell', 'foots'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell', 'rebookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell', 'rebooking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell', 'rebook'); - ts_lexize ------------ - -(1 row) - -SELECT ts_lexize('hunspell', 'unbookings'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell', 'unbooking'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell', 'unbook'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell', 'footklubber'); - ts_lexize ----------------- - {foot,klubber} -(1 row) - -SELECT ts_lexize('hunspell', 'footballklubber'); - ts_lexize ------------------------------------------------------- - {footballklubber,foot,ball,klubber,football,klubber} -(1 row) - -SELECT ts_lexize('hunspell', 'ballyklubber'); - ts_lexize ----------------- - {ball,klubber} -(1 row) - -SELECT ts_lexize('hunspell', 'footballyklubber'); - ts_lexize ---------------------- - {foot,ball,klubber} -(1 row) - --- Test ISpell dictionary with hunspell affix file with FLAG long parameter -CREATE TEXT SEARCH DICTIONARY hunspell_long ( - Template=ispell, - DictFile=hunspell_sample_long, - AffFile=hunspell_sample_long -); -SELECT ts_lexize('hunspell_long', 'skies'); - ts_lexize ------------ - {sky} -(1 row) - -SELECT ts_lexize('hunspell_long', 'bookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'booking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'foot'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell_long', 'foots'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell_long', 'rebookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'rebooking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'rebook'); - ts_lexize ------------ - -(1 row) - -SELECT ts_lexize('hunspell_long', 'unbookings'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'unbooking'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'unbook'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'booked'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_long', 'footklubber'); - ts_lexize ----------------- - {foot,klubber} -(1 row) - -SELECT ts_lexize('hunspell_long', 'footballklubber'); - ts_lexize ------------------------------------------------------- - {footballklubber,foot,ball,klubber,football,klubber} -(1 row) - -SELECT ts_lexize('hunspell_long', 'ballyklubber'); - ts_lexize ----------------- - {ball,klubber} -(1 row) - -SELECT ts_lexize('hunspell_long', 'ballsklubber'); - ts_lexize ----------------- - {ball,klubber} -(1 row) - -SELECT ts_lexize('hunspell_long', 'footballyklubber'); - ts_lexize ---------------------- - {foot,ball,klubber} -(1 row) - -SELECT ts_lexize('hunspell_long', 'ex-machina'); - ts_lexize ---------------- - {ex-,machina} -(1 row) - --- Test ISpell dictionary with hunspell affix file with FLAG num parameter -CREATE TEXT SEARCH DICTIONARY hunspell_num ( - Template=ispell, - DictFile=hunspell_sample_num, - AffFile=hunspell_sample_num -); -SELECT ts_lexize('hunspell_num', 'skies'); - ts_lexize ------------ - {sky} -(1 row) - -SELECT ts_lexize('hunspell_num', 'sk'); - ts_lexize ------------ - {sky} -(1 row) - -SELECT ts_lexize('hunspell_num', 'bookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'booking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'foot'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell_num', 'foots'); - ts_lexize ------------ - {foot} -(1 row) - -SELECT ts_lexize('hunspell_num', 'rebookings'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'rebooking'); - ts_lexize ----------------- - {booking,book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'rebook'); - ts_lexize ------------ - -(1 row) - -SELECT ts_lexize('hunspell_num', 'unbookings'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'unbooking'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'unbook'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'booked'); - ts_lexize ------------ - {book} -(1 row) - -SELECT ts_lexize('hunspell_num', 'footklubber'); - ts_lexize ----------------- - {foot,klubber} -(1 row) - -SELECT ts_lexize('hunspell_num', 'footballklubber'); - ts_lexize ------------------------------------------------------- - {footballklubber,foot,ball,klubber,football,klubber} -(1 row) - -SELECT ts_lexize('hunspell_num', 'ballyklubber'); - ts_lexize ----------------- - {ball,klubber} -(1 row) - -SELECT ts_lexize('hunspell_num', 'footballyklubber'); - ts_lexize ---------------------- - {foot,ball,klubber} -(1 row) - --- Test suitability of affix and dict files -CREATE TEXT SEARCH DICTIONARY hunspell_err ( - Template=ispell, - DictFile=ispell_sample, - AffFile=hunspell_sample_long -); -ERROR: invalid affix alias "GJUS" -CREATE TEXT SEARCH DICTIONARY hunspell_err ( - Template=ispell, - DictFile=ispell_sample, - AffFile=hunspell_sample_num -); -ERROR: invalid affix flag "SZ\" -CREATE TEXT SEARCH DICTIONARY hunspell_invalid_1 ( - Template=ispell, - DictFile=hunspell_sample_long, - AffFile=ispell_sample -); -CREATE TEXT SEARCH DICTIONARY hunspell_invalid_2 ( - Template=ispell, - DictFile=hunspell_sample_long, - AffFile=hunspell_sample_num -); -CREATE TEXT SEARCH DICTIONARY hunspell_invalid_3 ( - Template=ispell, - DictFile=hunspell_sample_num, - AffFile=ispell_sample -); -CREATE TEXT SEARCH DICTIONARY hunspell_err ( - Template=ispell, - DictFile=hunspell_sample_num, - AffFile=hunspell_sample_long -); -ERROR: invalid affix alias "302,301,202,303" --- Synonym dictionary -CREATE TEXT SEARCH DICTIONARY synonym ( - Template=synonym, - Synonyms=synonym_sample -); -SELECT ts_lexize('synonym', 'PoStGrEs'); - ts_lexize ------------ - {pgsql} -(1 row) - -SELECT ts_lexize('synonym', 'Gogle'); - ts_lexize ------------ - {googl} -(1 row) - -SELECT ts_lexize('synonym', 'indices'); - ts_lexize ------------ - {index} -(1 row) - --- test altering boolean parameters -SELECT dictinitoption FROM pg_ts_dict WHERE dictname = 'synonym'; - dictinitoption ------------------------------ - synonyms = 'synonym_sample' -(1 row) - -ALTER TEXT SEARCH DICTIONARY synonym (CaseSensitive = 1); -SELECT ts_lexize('synonym', 'PoStGrEs'); - ts_lexize ------------ - -(1 row) - -SELECT dictinitoption FROM pg_ts_dict WHERE dictname = 'synonym'; - dictinitoption ------------------------------------------------- - synonyms = 'synonym_sample', casesensitive = 1 -(1 row) - -ALTER TEXT SEARCH DICTIONARY synonym (CaseSensitive = 2); -- fail -ERROR: casesensitive requires a Boolean value -ALTER TEXT SEARCH DICTIONARY synonym (CaseSensitive = off); -SELECT ts_lexize('synonym', 'PoStGrEs'); - ts_lexize ------------ - {pgsql} -(1 row) - -SELECT dictinitoption FROM pg_ts_dict WHERE dictname = 'synonym'; - dictinitoption ----------------------------------------------------- - synonyms = 'synonym_sample', casesensitive = 'off' -(1 row) - --- Create and simple test thesaurus dictionary --- More tests in configuration checks because ts_lexize() --- cannot pass more than one word to thesaurus. -CREATE TEXT SEARCH DICTIONARY thesaurus ( - Template=thesaurus, - DictFile=thesaurus_sample, - Dictionary=english_stem -); -SELECT ts_lexize('thesaurus', 'one'); - ts_lexize ------------ - {1} -(1 row) - --- Test ispell dictionary in configuration -CREATE TEXT SEARCH CONFIGURATION ispell_tst ( - COPY=english -); -ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR - word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart - WITH ispell, english_stem; -SELECT to_tsvector('ispell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); - to_tsvector ----------------------------------------------------------------------------------------------------- - 'ball':7 'book':1,5 'booking':1,5 'foot':7,10 'football':7 'footballklubber':7 'klubber':7 'sky':3 -(1 row) - -SELECT to_tsquery('ispell_tst', 'footballklubber'); - to_tsquery --------------------------------------------------------------------------- - 'footballklubber' | 'foot' & 'ball' & 'klubber' | 'football' & 'klubber' -(1 row) - -SELECT to_tsquery('ispell_tst', 'footballyklubber:b & rebookings:A & sky'); - to_tsquery ------------------------------------------------------------------------- - 'foot':B & 'ball':B & 'klubber':B & ( 'booking':A | 'book':A ) & 'sky' -(1 row) - --- Test ispell dictionary with hunspell affix in configuration -CREATE TEXT SEARCH CONFIGURATION hunspell_tst ( - COPY=ispell_tst -); -ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING - REPLACE ispell WITH hunspell; -SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); - to_tsvector ----------------------------------------------------------------------------------------------------- - 'ball':7 'book':1,5 'booking':1,5 'foot':7,10 'football':7 'footballklubber':7 'klubber':7 'sky':3 -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballklubber'); - to_tsquery --------------------------------------------------------------------------- - 'footballklubber' | 'foot' & 'ball' & 'klubber' | 'football' & 'klubber' -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); - to_tsquery ------------------------------------------------------------------------- - 'foot':B & 'ball':B & 'klubber':B & ( 'booking':A | 'book':A ) & 'sky' -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballyklubber:b <-> sky'); - to_tsquery -------------------------------------------------- - ( 'foot':B & 'ball':B & 'klubber':B ) <-> 'sky' -(1 row) - -SELECT phraseto_tsquery('hunspell_tst', 'footballyklubber sky'); - phraseto_tsquery -------------------------------------------- - ( 'foot' & 'ball' & 'klubber' ) <-> 'sky' -(1 row) - --- Test ispell dictionary with hunspell affix with FLAG long in configuration -ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING - REPLACE hunspell WITH hunspell_long; -SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); - to_tsvector ----------------------------------------------------------------------------------------------------- - 'ball':7 'book':1,5 'booking':1,5 'foot':7,10 'football':7 'footballklubber':7 'klubber':7 'sky':3 -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballklubber'); - to_tsquery --------------------------------------------------------------------------- - 'footballklubber' | 'foot' & 'ball' & 'klubber' | 'football' & 'klubber' -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); - to_tsquery ------------------------------------------------------------------------- - 'foot':B & 'ball':B & 'klubber':B & ( 'booking':A | 'book':A ) & 'sky' -(1 row) - --- Test ispell dictionary with hunspell affix with FLAG num in configuration -ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING - REPLACE hunspell_long WITH hunspell_num; -SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); - to_tsvector ----------------------------------------------------------------------------------------------------- - 'ball':7 'book':1,5 'booking':1,5 'foot':7,10 'football':7 'footballklubber':7 'klubber':7 'sky':3 -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballklubber'); - to_tsquery --------------------------------------------------------------------------- - 'footballklubber' | 'foot' & 'ball' & 'klubber' | 'football' & 'klubber' -(1 row) - -SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); - to_tsquery ------------------------------------------------------------------------- - 'foot':B & 'ball':B & 'klubber':B & ( 'booking':A | 'book':A ) & 'sky' -(1 row) - --- Test synonym dictionary in configuration -CREATE TEXT SEARCH CONFIGURATION synonym_tst ( - COPY=english -); -ALTER TEXT SEARCH CONFIGURATION synonym_tst ALTER MAPPING FOR - asciiword, hword_asciipart, asciihword - WITH synonym, english_stem; -SELECT to_tsvector('synonym_tst', 'Postgresql is often called as postgres or pgsql and pronounced as postgre'); - to_tsvector ---------------------------------------------------- - 'call':4 'often':3 'pgsql':1,6,8,12 'pronounc':10 -(1 row) - -SELECT to_tsvector('synonym_tst', 'Most common mistake is to write Gogle instead of Google'); - to_tsvector ----------------------------------------------------------- - 'common':2 'googl':7,10 'instead':8 'mistak':3 'write':6 -(1 row) - -SELECT to_tsvector('synonym_tst', 'Indexes or indices - Which is right plural form of index?'); - to_tsvector ----------------------------------------------- - 'form':8 'index':1,3,10 'plural':7 'right':6 -(1 row) - -SELECT to_tsquery('synonym_tst', 'Index & indices'); - to_tsquery ---------------------- - 'index' & 'index':* -(1 row) - --- test thesaurus in configuration --- see thesaurus_sample.ths to understand 'odd' resulting tsvector -CREATE TEXT SEARCH CONFIGURATION thesaurus_tst ( - COPY=synonym_tst -); -ALTER TEXT SEARCH CONFIGURATION thesaurus_tst ALTER MAPPING FOR - asciiword, hword_asciipart, asciihword - WITH synonym, thesaurus, english_stem; -SELECT to_tsvector('thesaurus_tst', 'one postgres one two one two three one'); - to_tsvector ----------------------------------- - '1':1,5 '12':3 '123':4 'pgsql':2 -(1 row) - -SELECT to_tsvector('thesaurus_tst', 'Supernovae star is very new star and usually called supernovae (abbreviation SN)'); - to_tsvector --------------------------------------------------------------- - 'abbrevi':10 'call':8 'new':4 'sn':1,9,11 'star':5 'usual':7 -(1 row) - -SELECT to_tsvector('thesaurus_tst', 'Booking tickets is looking like a booking a tickets'); - to_tsvector -------------------------------------------------------- - 'card':3,10 'invit':2,9 'like':6 'look':5 'order':1,8 -(1 row) - --- invalid: non-lowercase quoted identifiers -CREATE TEXT SEARCH DICTIONARY tsdict_case -( - Template = ispell, - "DictFile" = ispell_sample, - "AffFile" = ispell_sample -); -ERROR: unrecognized Ispell parameter: "DictFile" --- Test grammar for configurations -CREATE TEXT SEARCH CONFIGURATION dummy_tst (COPY=english); --- Overriden mapping change with duplicated tokens. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - ALTER MAPPING FOR word, word WITH ispell; --- Not a token supported by the configuration's parser, fails. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - DROP MAPPING FOR not_a_token, not_a_token; -ERROR: token type "not_a_token" does not exist --- Not a token supported by the configuration's parser, fails even --- with IF EXISTS. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - DROP MAPPING IF EXISTS FOR not_a_token, not_a_token; -ERROR: token type "not_a_token" does not exist --- Token supported by the configuration's parser, succeeds. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - DROP MAPPING FOR word, word; --- No mapping for token supported by the configuration's parser, fails. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - DROP MAPPING FOR word; -ERROR: mapping for token type "word" does not exist --- Token supported by the configuration's parser, cannot be found, --- succeeds with IF EXISTS. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - DROP MAPPING IF EXISTS FOR word, word; -NOTICE: mapping for token type "word" does not exist, skipping --- Re-add mapping, with duplicated tokens supported by the parser. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - ADD MAPPING FOR word, word WITH ispell; --- Not a token supported by the configuration's parser, fails. -ALTER TEXT SEARCH CONFIGURATION dummy_tst - ADD MAPPING FOR not_a_token WITH ispell; -ERROR: token type "not_a_token" does not exist -DROP TEXT SEARCH CONFIGURATION dummy_tst; +psql: error: connection to server on socket "c:/cirrus//.s.PGSQL.40048" failed: server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. diff -w -U3 C:/cirrus/src/test/regress/expected/foreign_data.out C:/cirrus/build/testrun/regress/regress/results/foreign_data.out --- C:/cirrus/src/test/regress/expected/foreign_data.out 2024-04-05 16:07:25.814838200 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/foreign_data.out 2024-04-05 16:10:45.421756000 +0000 @@ -2124,85 +2124,10 @@ Number of partitions: 0 \d+ fd_pt2_1 - Foreign table "public.fd_pt2_1" - Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description ---------+---------+-----------+----------+---------+-------------+----------+--------------+------------- - c1 | integer | | not null | | | plain | | - c2 | text | | not null | | | extended | | - c3 | date | | not null | | | plain | | -Check constraints: - "p21chk" CHECK (c2 <> ''::text) -Not-null constraints: - "fd_pt2_1_c1_not_null" NOT NULL "c1" - "fd_pt2_1_c2_not_null" NOT NULL "c2" - "fd_pt2_1_c3_not_null" NOT NULL "c3" -Server: s0 -FDW options: (delimiter ',', quote '"', "be quoted" 'value') - -ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR -ERROR: child table is missing constraint "fd_pt2chk1" -ALTER FOREIGN TABLE fd_pt2_1 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0); -ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -DROP FOREIGN TABLE fd_pt2_1; -DROP TABLE fd_pt2; --- foreign table cannot be part of partition tree made of temporary --- relations. -CREATE TEMP TABLE temp_parted (a int) PARTITION BY LIST (a); -CREATE FOREIGN TABLE foreign_part PARTITION OF temp_parted DEFAULT - SERVER s0; -- ERROR -ERROR: cannot create a permanent relation as partition of temporary relation "temp_parted" -CREATE FOREIGN TABLE foreign_part (a int) SERVER s0; -ALTER TABLE temp_parted ATTACH PARTITION foreign_part DEFAULT; -- ERROR -ERROR: cannot attach a permanent relation as partition of temporary relation "temp_parted" -DROP FOREIGN TABLE foreign_part; -DROP TABLE temp_parted; --- Cleanup -DROP SCHEMA foreign_schema CASCADE; -DROP ROLE regress_test_role; -- ERROR -ERROR: role "regress_test_role" cannot be dropped because some objects depend on it -DETAIL: privileges for foreign-data wrapper foo -privileges for server s4 -owner of user mapping for regress_test_role on server s6 -DROP SERVER t1 CASCADE; -NOTICE: drop cascades to user mapping for public on server t1 -DROP USER MAPPING FOR regress_test_role SERVER s6; -DROP FOREIGN DATA WRAPPER foo CASCADE; -NOTICE: drop cascades to 5 other objects -DETAIL: drop cascades to server s4 -drop cascades to user mapping for regress_foreign_data_user on server s4 -drop cascades to server s6 -drop cascades to server s9 -drop cascades to user mapping for regress_unprivileged_role on server s9 -DROP SERVER s8 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to user mapping for regress_foreign_data_user on server s8 -drop cascades to user mapping for public on server s8 -DROP ROLE regress_test_indirect; -DROP ROLE regress_test_role; -DROP ROLE regress_unprivileged_role; -- ERROR -ERROR: role "regress_unprivileged_role" cannot be dropped because some objects depend on it -DETAIL: privileges for foreign-data wrapper postgresql -REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM regress_unprivileged_role; -DROP ROLE regress_unprivileged_role; -DROP ROLE regress_test_role2; -DROP FOREIGN DATA WRAPPER postgresql CASCADE; -DROP FOREIGN DATA WRAPPER dummy CASCADE; -NOTICE: drop cascades to server s0 -\c -DROP ROLE regress_foreign_data_user; --- At this point we should have no wrappers, no servers, and no mappings. -SELECT fdwname, fdwhandler, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper; - fdwname | fdwhandler | fdwvalidator | fdwoptions ----------+------------+--------------+------------ -(0 rows) - -SELECT srvname, srvoptions FROM pg_foreign_server; - srvname | srvoptions ----------+------------ -(0 rows) - -SELECT * FROM pg_user_mapping; - oid | umuser | umserver | umoptions ------+--------+----------+----------- -(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 -w -U3 C:/cirrus/src/test/regress/expected/alter_table.out C:/cirrus/build/testrun/regress/regress/results/alter_table.out --- C:/cirrus/src/test/regress/expected/alter_table.out 2024-04-05 16:07:25.763504300 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/alter_table.out 2024-04-05 16:10:54.507212800 +0000 @@ -2455,2234 +2455,10 @@ 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 id, - 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 id, - 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 id, - 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 id, - 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, 4)) -(2 rows) - -select * from at_view_2; - id | stuff | j -----+--------+------------------------------------- - 23 | skidoo | {"id":23,"stuff":"skidoo","more":4} -(1 row) - -drop view at_view_2; -drop view at_view_1; -drop table at_base_table; --- related case (bug #17811) -begin; -create temp table t1 as select * from int8_tbl; -create temp view v1 as select 1::int8 as q1; -create temp view v2 as select * from v1; -create or replace temp view v1 with (security_barrier = true) - as select * from t1; -create temp table log (q1 int8, q2 int8); -create rule v1_upd_rule as on update to v1 - do also insert into log values (new.*); -update v2 set q1 = q1 + 1 where q1 = 123; -select * from t1; - q1 | q2 -------------------+------------------- - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 124 | 456 - 124 | 4567890123456789 -(5 rows) - -select * from log; - q1 | q2 ------+------------------ - 124 | 456 - 124 | 4567890123456789 -(2 rows) - -rollback; --- check adding a column not itself 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 -DROP TABLE test_tbl1; -CREATE TABLE test_tbl1 (x int, y text); -CREATE INDEX test_tbl1_idx ON test_tbl1((row(x,y)::test_type1)); -ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails -ERROR: cannot alter type "test_type1" because column "test_tbl1_idx.row" uses it -DROP TABLE test_tbl1; -DROP TYPE test_type1; -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, test_tbl2; -DROP TYPE test_type2; -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); -- has sequence, toast --- check relpersistence of an unlogged table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' -UNION ALL -SELECT r.relname || ' 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 r.relname || ' 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 ------------------------+---------+---------------- - unlogged1 | r | u - unlogged1 toast index | i | u - unlogged1 toast table | t | u - unlogged1_f1_seq | S | u - 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 r.relname || ' 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 r.relname || ' 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 ------------------------+---------+---------------- - unlogged1 | r | p - unlogged1 toast index | i | p - unlogged1 toast table | t | 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); -- has sequence, toast --- check relpersistence of a permanent table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' -UNION ALL -SELECT r.relname || ' 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 r.relname ||' 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 toast index | i | p - logged1 toast table | t | p - logged1_f1_seq | S | p - logged1_pkey | i | 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 r.relname || ' 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 r.relname || ' 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 toast index | i | u - logged1 toast table | t | u - logged1_f1_seq | S | u - logged1_pkey | i | 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; -CREATE TABLE atnotnull1 (); -ALTER TABLE atnotnull1 - ADD COLUMN a INT, - ALTER a SET NOT NULL; -ALTER TABLE atnotnull1 - ADD COLUMN b INT, - ADD NOT NULL b; -ALTER TABLE atnotnull1 - ADD COLUMN c INT, - ADD PRIMARY KEY (c); -\d+ atnotnull1 - Table "public.atnotnull1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - a | integer | | not null | | plain | | - b | integer | | not null | | plain | | - c | integer | | not null | | plain | | -Indexes: - "atnotnull1_pkey" PRIMARY KEY, btree (c) -Not-null constraints: - "atnotnull1_a_not_null" NOT NULL "a" - "atnotnull1_b_not_null" NOT NULL "b" - --- cannot drop column that is part of the partition key -CREATE TABLE partitioned ( - a int, - b int -) PARTITION BY RANGE (a, (a+b+1)); -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" --- specifying storage parameters for partitioned tables is not supported -ALTER TABLE partitioned SET (fillfactor=100); -ERROR: cannot specify storage parameters for a partitioned table -HINT: Specify storage parameters for its leaf partitions instead. --- 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 test_setup.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 -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 case for bug #17409 */ -create table attbl (p1 int constraint pk_attbl primary key); -create table atref (c1 int references attbl(p1)); -cluster attbl using pk_attbl; -alter table attbl alter column p1 set data type bigint; -alter table atref alter column c1 set data type bigint; -drop table attbl, atref; -create table attbl (p1 int constraint pk_attbl primary key); -alter table attbl replica identity using index pk_attbl; -create table atref (c1 int references attbl(p1)); -alter table attbl alter column p1 set data type bigint; -alter table atref alter column c1 set data type bigint; -drop table attbl, atref; -/* End test case for bug #17409 */ --- 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); --- Test altering table having publication -create schema alter1; -create schema alter2; -create table alter1.t1 (a int); -set client_min_messages = 'ERROR'; -create publication pub1 for table alter1.t1, tables in schema alter2; -reset client_min_messages; -alter table alter1.t1 set schema alter2; -\d+ alter2.t1 - Table "alter2.t1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - a | integer | | | | plain | | -Publications: - "pub1" - -drop publication pub1; -drop schema alter1 cascade; -drop schema alter2 cascade; -NOTICE: drop cascades to table alter2.t1 +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 -w -U3 C:/cirrus/src/test/regress/expected/partition_prune.out C:/cirrus/build/testrun/regress/regress/results/partition_prune.out --- C:/cirrus/src/test/regress/expected/partition_prune.out 2024-04-05 16:07:25.881740000 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/partition_prune.out 2024-04-05 16:11:02.359746200 +0000 @@ -2441,2043 +2441,10 @@ 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; --- Temporarily install some debugging to investigate plan instability. -select c.relname,c.relpages,c.reltuples,i.indisvalid,s.autovacuum_count,s.autoanalyze_count -from pg_class c -left join pg_stat_all_tables s on c.oid = s.relid -left join pg_index i on c.oid = i.indexrelid -where c.relname like 'ab\_%' order by c.relname; - relname | relpages | reltuples | indisvalid | autovacuum_count | autoanalyze_count -----------------+----------+-----------+------------+------------------+------------------- - ab_a1 | 0 | -1 | | 0 | 0 - ab_a1_b1 | 0 | -1 | | 0 | 0 - ab_a1_b1_a_idx | 1 | 0 | t | | - ab_a1_b2 | 0 | -1 | | 0 | 0 - ab_a1_b2_a_idx | 1 | 0 | t | | - ab_a1_b3 | 0 | -1 | | 0 | 0 - ab_a1_b3_a_idx | 1 | 0 | t | | - ab_a2 | 0 | -1 | | 0 | 0 - ab_a2_b1 | 0 | -1 | | 0 | 0 - ab_a2_b1_a_idx | 1 | 0 | t | | - ab_a2_b2 | 0 | -1 | | 0 | 0 - ab_a2_b2_a_idx | 1 | 0 | t | | - ab_a2_b3 | 0 | -1 | | 0 | 0 - ab_a2_b3_a_idx | 1 | 0 | t | | - ab_a3 | 0 | -1 | | 0 | 0 - ab_a3_b1 | 0 | -1 | | 0 | 0 - ab_a3_b1_a_idx | 1 | 0 | t | | - ab_a3_b2 | 0 | -1 | | 0 | 0 - ab_a3_b2_a_idx | 1 | 0 | t | | - ab_a3_b3 | 0 | -1 | | 0 | 0 - ab_a3_b3_a_idx | 1 | 0 | t | | -(21 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(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 - -> Aggregate (actual rows=1 loops=1) - -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 - -> 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 = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1) - Index Cond: (a = (InitPlan 1).col1) - -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed) - Recheck Cond: (a = (InitPlan 1).col1) - Filter: (b = (InitPlan 2).col1) - -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed) - Index Cond: (a = (InitPlan 1).col1) -(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 - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = (InitPlan 1).col1) -(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 - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> 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 = (InitPlan 1).col1) - -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = (InitPlan 1).col1) -(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 - -> Result (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b1 ab_1 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) - -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) - -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) - -> Seq Scan on xy_1 (actual rows=0 loops=1) - Filter: ((x = $1) AND (y = (InitPlan 1).col1)) - Rows Removed by Filter: 1 - -> Seq Scan on ab_a1_b1 ab_4 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) - -> Seq Scan on ab_a1_b2 ab_5 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) - -> Seq Scan on ab_a1_b3 ab_6 (never executed) - Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -(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; --- Temporarily install some debugging to investigate plan instability. -select c.relname,c.relpages,c.reltuples,i.indisvalid,s.autovacuum_count,s.autoanalyze_count -from pg_class c -left join pg_stat_all_tables s on c.oid = s.relid -left join pg_index i on c.oid = i.indexrelid -where c.relname like 'ab\_%' order by c.relname; - relname | relpages | reltuples | indisvalid | autovacuum_count | autoanalyze_count -----------------+----------+-----------+------------+------------------+------------------- - ab_a1 | 0 | -1 | | 0 | 0 - ab_a1_b1 | 0 | -1 | | 0 | 0 - ab_a1_b1_a_idx | 1 | 0 | t | | - ab_a1_b2 | 0 | -1 | | 0 | 0 - ab_a1_b2_a_idx | 1 | 0 | t | | - ab_a1_b3 | 0 | -1 | | 0 | 0 - ab_a1_b3_a_idx | 1 | 0 | t | | - ab_a2 | 0 | -1 | | 0 | 0 - ab_a2_b1 | 0 | -1 | | 0 | 0 - ab_a2_b1_a_idx | 1 | 0 | t | | - ab_a2_b2 | 0 | -1 | | 0 | 0 - ab_a2_b2_a_idx | 1 | 0 | t | | - ab_a2_b3 | 0 | -1 | | 0 | 0 - ab_a2_b3_a_idx | 1 | 0 | t | | - ab_a3 | 0 | -1 | | 0 | 0 - ab_a3_b1 | 0 | -1 | | 0 | 0 - ab_a3_b1_a_idx | 1 | 0 | t | | - ab_a3_b2 | 0 | -1 | | 0 | 0 - ab_a3_b2_a_idx | 1 | 0 | t | | - ab_a3_b3 | 0 | -1 | | 0 | 0 - ab_a3_b3_a_idx | 1 | 0 | t | | -(21 rows) - --- 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) - Heap Blocks: exact=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) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) -(36 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 - -> 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 = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = (InitPlan 1).col1) - -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = (InitPlan 1).col1) -(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 - -> Result (actual rows=1 loops=1) - -> Seq Scan on listp_1_1 listp_1 (never executed) - Filter: (a = (InitPlan 1).col1) - -> Seq Scan on listp_2_1 listp_2 (never executed) - Filter: (a = (InitPlan 1).col1) -(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 - -> Result (actual rows=1 loops=1) - -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) - Filter: ((a = $1) AND (abs(b) < (InitPlan 1).col1)) -(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 - -> Result (actual rows=1 loops=1) - -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) - -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) -(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 - -> 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 = (InitPlan 1).col1) - -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1) - Filter: (a = (InitPlan 1).col1) -(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 - -> 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 = (InitPlan 1).col1) - -> Seq Scan on boolp_t boolp_2 (never executed) - Filter: (a = (InitPlan 1).col1) -(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 - -> Result (actual rows=1 loops=1) - InitPlan 1 - -> 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 >= (InitPlan 2).col1) - -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1) - Filter: (a >= (InitPlan 2).col1) - -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1) - Filter: (a >= (InitPlan 2).col1) -(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 - -> Result - -> Seq Scan on p1 p - Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) - -> Seq Scan on q111 q1 - Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) - -> Result - One-Time Filter: (1 = (InitPlan 1).col1) -(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 - -> Result - -> Seq Scan on p1 p - Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) - -> Seq Scan on q111 q1 - Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) - -> Result - One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = (InitPlan 1).col1)) -(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 = (InitPlan 1).col1)) - InitPlan 1 - -> 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 - Workers Launched: N - InitPlan 1 - -> 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 = (InitPlan 1).col1) - -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = (InitPlan 1).col1) -(10 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 ------------------------------------------------------------------------------------ - Gather (actual rows=N loops=N) - Workers Planned: 2 - Workers Launched: N - -> Parallel Append (actual rows=N loops=N) - -> Parallel Append (actual rows=N loops=N) - InitPlan 2 - -> Result (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_1 (never executed) - Filter: (a = (InitPlan 2).col1) - -> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N) - Filter: (a = (InitPlan 2).col1) - -> Parallel Append (actual rows=N loops=N) - InitPlan 1 - -> Result (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_4 (actual rows=N loops=N) - Filter: (a = (InitPlan 1).col1) - -> Parallel Seq Scan on listp_12_2 listp_5 (never executed) - Filter: (a = (InitPlan 1).col1) -(18 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 - -> Result (actual rows=1 loops=1) - InitPlan 2 - -> 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[(InitPlan 1).col1, (InitPlan 2).col1])) - -> 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[(InitPlan 1).col1, (InitPlan 2).col1])) - -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) - Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) - -> 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[(InitPlan 1).col1, (InitPlan 2).col1])) -(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) - -drop table rp_prefix_test1; -drop table rp_prefix_test2; -drop table rp_prefix_test3; --- --- Test that get_steps_using_prefix() handles IS NULL clauses correctly --- -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 8 partitions -select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');' -from generate_Series(0,7) x; - ?column? ------------------------------------------------------------------------------------------------------- - create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); - create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); - create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); - create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); - create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); - create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); - create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); - create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); -(8 rows) - -\gexec -create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); -create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); -create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); -create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); -create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); -create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); --- insert 16 rows, one row for each test to perform. -insert into hp_prefix_test -select - case a when 0 then null else 1 end, - case b when 0 then null else 2 end, - case c when 0 then null else 3 end, - case d when 0 then null else 4 end -from - generate_series(0,1) a, - generate_series(0,1) b, - generate_Series(0,1) c, - generate_Series(0,1) d; --- Ensure partition pruning works correctly for each combination of IS NULL --- and equality quals. This may seem a little excessive, but there have been --- a number of bugs in this area over the years. We make use of row only --- output to reduce the size of the expected results. -\t on -select - 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || - string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) -from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) -group by g.s -order by g.s; - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 - explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 - -\gexec -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null - Seq Scan on hp_prefix_test_p0 hp_prefix_test - Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d IS NULL)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null - Seq Scan on hp_prefix_test_p1 hp_prefix_test - Filter: ((b IS NULL) AND (c IS NULL) AND (d IS NULL) AND (a = 1)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null - Seq Scan on hp_prefix_test_p2 hp_prefix_test - Filter: ((a IS NULL) AND (c IS NULL) AND (d IS NULL) AND (b = 2)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null - Seq Scan on hp_prefix_test_p4 hp_prefix_test - Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null - Seq Scan on hp_prefix_test_p3 hp_prefix_test - Filter: ((a IS NULL) AND (b IS NULL) AND (d IS NULL) AND (c = 3)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null - Seq Scan on hp_prefix_test_p7 hp_prefix_test - Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null - Seq Scan on hp_prefix_test_p4 hp_prefix_test - Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null - Seq Scan on hp_prefix_test_p5 hp_prefix_test - Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 - Seq Scan on hp_prefix_test_p4 hp_prefix_test - Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 - Seq Scan on hp_prefix_test_p6 hp_prefix_test - Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 - Seq Scan on hp_prefix_test_p5 hp_prefix_test - Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 - Seq Scan on hp_prefix_test_p6 hp_prefix_test - Filter: ((c IS NULL) AND (a = 1) AND (b = 2) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 - Seq Scan on hp_prefix_test_p4 hp_prefix_test - Filter: ((a IS NULL) AND (b IS NULL) AND (c = 3) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 - Seq Scan on hp_prefix_test_p5 hp_prefix_test - Filter: ((b IS NULL) AND (a = 1) AND (c = 3) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 - Seq Scan on hp_prefix_test_p6 hp_prefix_test - Filter: ((a IS NULL) AND (b = 2) AND (c = 3) AND (d = 4)) - -explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 - Seq Scan on hp_prefix_test_p4 hp_prefix_test - Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4)) - --- And ensure we get exactly 1 row from each. Again, all 16 possible combinations. -select - 'select tableoid::regclass,* from hp_prefix_test where ' || - string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) -from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) -group by g.s -order by g.s; - select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null - select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null - select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null - select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null - select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null - select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null - select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null - select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null - select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 - select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 - select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 - select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 - select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 - select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 - select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 - select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 - -\gexec -select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null - hp_prefix_test_p0 | | | | - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null - hp_prefix_test_p1 | 1 | | | - -select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null - hp_prefix_test_p2 | | 2 | | - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null - hp_prefix_test_p4 | 1 | 2 | | - -select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null - hp_prefix_test_p3 | | | 3 | - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null - hp_prefix_test_p7 | 1 | | 3 | - -select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null - hp_prefix_test_p4 | | 2 | 3 | - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null - hp_prefix_test_p5 | 1 | 2 | 3 | - -select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 - hp_prefix_test_p4 | | | | 4 - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 - hp_prefix_test_p6 | 1 | | | 4 - -select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 - hp_prefix_test_p5 | | 2 | | 4 - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 - hp_prefix_test_p6 | 1 | 2 | | 4 - -select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 - hp_prefix_test_p4 | | | 3 | 4 - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 - hp_prefix_test_p5 | 1 | | 3 | 4 - -select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 - hp_prefix_test_p6 | | 2 | 3 | 4 - -select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 - hp_prefix_test_p4 | 1 | 2 | 3 | 4 - -\t off -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); +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 -w -U3 C:/cirrus/src/test/regress/expected/partition_aggregate.out C:/cirrus/build/testrun/regress/regress/results/partition_aggregate.out --- C:/cirrus/src/test/regress/expected/partition_aggregate.out 2024-04-05 16:07:25.876854600 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/partition_aggregate.out 2024-04-05 16:11:02.359746200 +0000 @@ -1367,154 +1367,10 @@ (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) - +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 -w -U3 C:/cirrus/src/test/regress/expected/fast_default.out C:/cirrus/build/testrun/regress/regress/results/fast_default.out --- C:/cirrus/src/test/regress/expected/fast_default.out 2024-04-05 16:07:25.811933900 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/fast_default.out 2024-04-05 16:11:06.104373200 +0000 @@ -75,787 +75,10 @@ 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) - --- check that we notice insertion of a volatile default argument -CREATE FUNCTION foolme(timestamptz DEFAULT clock_timestamp()) - RETURNS timestamptz - IMMUTABLE AS 'select $1' LANGUAGE sql; -ALTER TABLE T ADD COLUMN c3 timestamptz DEFAULT foolme(); -NOTICE: rewriting table t for reason 2 -SELECT attname, atthasmissing, attmissingval FROM pg_attribute - WHERE attrelid = 't'::regclass AND attnum > 0 - ORDER BY attnum; - attname | atthasmissing | attmissingval ----------+---------------+--------------- - pk | f | - c1 | f | - c2 | f | - c3 | f | -(4 rows) - -DROP TABLE T; -DROP FUNCTION foolme(timestamptz); --- 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) - +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 -w -U3 C:/cirrus/src/test/regress/expected/tablespace.out C:/cirrus/build/testrun/regress/regress/results/tablespace.out --- C:/cirrus/src/test/regress/expected/tablespace.out 2024-04-05 16:07:25.921354400 +0000 +++ C:/cirrus/build/testrun/regress/regress/results/tablespace.out 2024-04-05 16:11:08.218152600 +0000 @@ -926,43 +926,10 @@ ALTER INDEX testschema.part_a_idx SET TABLESPACE pg_default; -- Fail, not empty DROP TABLESPACE regress_tblspace; -ERROR: tablespace "regress_tblspace" is not empty -CREATE ROLE regress_tablespace_user1 login; -CREATE ROLE regress_tablespace_user2 login; -GRANT USAGE ON SCHEMA testschema TO regress_tablespace_user2; -ALTER TABLESPACE regress_tblspace OWNER TO regress_tablespace_user1; -CREATE TABLE testschema.tablespace_acl (c int); --- new owner lacks permission to create this index from scratch -CREATE INDEX k ON testschema.tablespace_acl (c) TABLESPACE regress_tblspace; -ALTER TABLE testschema.tablespace_acl OWNER TO regress_tablespace_user2; -SET SESSION ROLE regress_tablespace_user2; -CREATE TABLE tablespace_table (i int) TABLESPACE regress_tblspace; -- fail -ERROR: permission denied for tablespace regress_tblspace -ALTER TABLE testschema.tablespace_acl ALTER c TYPE bigint; -REINDEX (TABLESPACE regress_tblspace) TABLE tablespace_table; -- fail -ERROR: permission denied for tablespace regress_tblspace -REINDEX (TABLESPACE regress_tblspace, CONCURRENTLY) TABLE tablespace_table; -- fail -ERROR: permission denied for tablespace regress_tblspace -RESET ROLE; -ALTER TABLESPACE regress_tblspace RENAME TO regress_tblspace_renamed; -ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default; -ALTER INDEX ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default; -ALTER MATERIALIZED VIEW ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default; --- Should show notice that nothing was done -ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default; -NOTICE: no matching relations in tablespace "regress_tblspace_renamed" found -ALTER MATERIALIZED VIEW ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default; -NOTICE: no matching relations in tablespace "regress_tblspace_renamed" found --- Should succeed -DROP TABLESPACE regress_tblspace_renamed; -DROP SCHEMA testschema CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table testschema.foo -drop cascades to table testschema.asselect -drop cascades to table testschema.asexecute -drop cascades to table testschema.part -drop cascades to table testschema.atable -drop cascades to materialized view testschema.amv -drop cascades to table testschema.tablespace_acl -DROP ROLE regress_tablespace_user1; -DROP ROLE regress_tablespace_user2; +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