From 7ec228cd54743d92b67c80c6c362938de06e6305 Mon Sep 17 00:00:00 2001 From: "houzj.fnst" Date: Wed, 1 Sep 2021 15:58:39 +0800 Subject: [PATCH] Regression-test-and-doc-updates --- contrib/test_decoding/expected/ddl.out | 4 + doc/src/sgml/func.sgml | 61 ++ doc/src/sgml/ref/alter_foreign_table.sgml | 13 + doc/src/sgml/ref/alter_function.sgml | 2 +- doc/src/sgml/ref/alter_table.sgml | 12 + doc/src/sgml/ref/create_foreign_table.sgml | 39 + doc/src/sgml/ref/create_table.sgml | 44 ++ doc/src/sgml/ref/create_table_as.sgml | 38 + src/test/regress/expected/alter_table.out | 2 + src/test/regress/expected/compression_1.out | 9 + src/test/regress/expected/copy2.out | 1 + src/test/regress/expected/create_table.out | 14 + .../regress/expected/create_table_like.out | 8 + src/test/regress/expected/domain.out | 2 + src/test/regress/expected/foreign_data.out | 42 ++ src/test/regress/expected/identity.out | 1 + src/test/regress/expected/inherit.out | 13 + src/test/regress/expected/insert.out | 12 + src/test/regress/expected/insert_parallel.out | 713 ++++++++++++++++++ src/test/regress/expected/psql.out | 58 +- src/test/regress/expected/publication.out | 4 + .../regress/expected/replica_identity.out | 1 + src/test/regress/expected/rowsecurity.out | 1 + src/test/regress/expected/rules.out | 3 + src/test/regress/expected/stats_ext.out | 1 + src/test/regress/expected/triggers.out | 1 + src/test/regress/expected/update.out | 1 + src/test/regress/output/tablespace.source | 2 + src/test/regress/parallel_schedule | 1 + src/test/regress/sql/insert_parallel.sql | 381 ++++++++++ 30 files changed, 1456 insertions(+), 28 deletions(-) create mode 100644 src/test/regress/expected/insert_parallel.out create mode 100644 src/test/regress/sql/insert_parallel.sql diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index 4ff0044c78..45aa25bff8 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -446,6 +446,7 @@ WITH (user_catalog_table = true) options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) +Parallel DML: default Options: user_catalog_table=true INSERT INTO replication_metadata(relation, options) @@ -460,6 +461,7 @@ ALTER TABLE replication_metadata RESET (user_catalog_table); options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) +Parallel DML: default INSERT INTO replication_metadata(relation, options) VALUES ('bar', ARRAY['a', 'b']); @@ -473,6 +475,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = true); options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) +Parallel DML: default Options: user_catalog_table=true INSERT INTO replication_metadata(relation, options) @@ -492,6 +495,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = false); rewritemeornot | integer | | | | plain | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) +Parallel DML: default Options: user_catalog_table=false INSERT INTO replication_metadata(relation, options) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 78812b2dbe..49278d9e21 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -24250,6 +24250,67 @@ SELECT collation for ('foo' COLLATE "de_DE"); Undefined objects are identified with NULL values. + + + + + pg_get_table_parallel_dml_safety + + pg_get_table_parallel_dml_safety ( table_name regclass ) + record + ( objid oid, + classid oid, + proparallel char ) + + + Returns a row containing enough information to uniquely identify the + parallel unsafe/restricted table-related objects from which the + table's parallel DML safety is determined. The user can use this + information during development in order to accurately declare a + table's parallel DML safety, or to identify any problematic objects + if parallel DML fails or behaves unexpectedly. Note that when the + use of an object-related parallel unsafe/restricted function is + detected, both the function OID and the object OID are returned. + classid is the OID of the system catalog + containing the object; + objid is the OID of the object itself. + + + + + + + pg_get_table_max_parallel_dml_hazard + + pg_get_table_max_parallel_dml_hazard ( regclass ) + char + + + Returns the worst parallel DML safety hazard that can be found in the + given relation: + + + + s safe + + + + + r restricted + + + + + u unsafe + + + + + + Users can use this function to do a quick check without caring about + specific parallel-related objects. + + diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml index 7ca03f3ac9..ca4b1c261e 100644 --- a/doc/src/sgml/ref/alter_foreign_table.sgml +++ b/doc/src/sgml/ref/alter_foreign_table.sgml @@ -29,6 +29,8 @@ ALTER FOREIGN TABLE [ IF EXISTS ] namenew_name ALTER FOREIGN TABLE [ IF EXISTS ] name SET SCHEMA new_schema +ALTER FOREIGN TABLE [ IF EXISTS ] name + PARALLEL { DEFAULT | UNSAFE | RESTRICTED | SAFE } where action is one of: @@ -299,6 +301,17 @@ ALTER FOREIGN TABLE [ IF EXISTS ] name + + PARALLEL DML + + + Change whether the data in the table can be modified in parallel mode. + See the similar form of ALTER TABLE + for more details. + + + + diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml index 0ee756a94d..a7088bc1cb 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -38,7 +38,7 @@ ALTER FUNCTION name [ ( [ [ execution_cost ROWS result_rows SUPPORT support_function diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 81291577f8..53bbacf9db 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -37,6 +37,8 @@ ALTER TABLE [ IF EXISTS ] name ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT } ALTER TABLE [ IF EXISTS ] name DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ] +ALTER TABLE [ IF EXISTS ] name + PARALLEL { DEFAULT | UNSAFE | RESTRICTED | SAFE } where action is one of: @@ -1030,6 +1032,16 @@ WITH ( MODULUS numeric_literal, REM + + PARALLEL DML + + + Change whether the data in the table can be modified in parallel mode. + See CREATE TABLE for details. + + + + diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml index f9477efe58..32372beed0 100644 --- a/doc/src/sgml/ref/create_foreign_table.sgml +++ b/doc/src/sgml/ref/create_foreign_table.sgml @@ -27,6 +27,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name [, ... ] ] ) [ INHERITS ( parent_table [, ... ] ) ] +[ PARALLEL DML { NOTESET | UNSAFE | RESTRICTED | SAFE } ] SERVER server_name [ OPTIONS ( option 'value' [, ... ] ) ] @@ -36,6 +37,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name | table_constraint } [, ... ] ) ] partition_bound_spec +[ PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } ] SERVER server_name [ OPTIONS ( option 'value' [, ... ] ) ] @@ -290,6 +292,43 @@ CHECK ( expression ) [ NO INHERIT ] + + PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } + + + PARALLEL DML DEFAULT indicates that the safety of + parallel modification will be checked automatically. This is default. + PARALLEL DML UNSAFE indicates that the data in the + table can't be modified in parallel mode, and this forces a serial + execution plan for DML statements operating on the table. + PARALLEL DML RESTRICTED indicates that the data in the + table can be modified in parallel mode, but the modification is + restricted to the parallel group leader. PARALLEL DML + SAFE indicates that the data in the table can be modified in + parallel mode without restriction. Note that + PostgreSQL currently does not support data + modification by parallel workers. + + + + Tables should be labeled parallel dml unsafe/restricted if any parallel + unsafe/restricted function could be executed when modifying the data in + the table (e.g., functions in triggers/index expression/constraints etc.). + + + + To assist in correctly labeling the parallel DML safety level of a table, + PostgreSQL provides some utility functions that may be used during + application development. Refer to + + pg_get_table_parallel_dml_safety() and + + pg_get_table_max_parallel_dml_hazard() for more information. + + + + + server_name diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 473a0a4aeb..5521f5123e 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -33,6 +33,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] +[ PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } ] CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name OF type_name [ ( @@ -45,6 +46,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] +[ PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } ] CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name PARTITION OF parent_table [ ( @@ -57,6 +59,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] +[ PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } ] where column_constraint is: @@ -1336,6 +1339,47 @@ WITH ( MODULUS numeric_literal, REM + + PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } + + + PARALLEL DML UNSAFE indicates that the data in the table + can't be modified in parallel mode, and this forces a serial execution plan + for DML statements operating on the table. This is the default. + PARALLEL DML RESTRICTED indicates that the data in the + table can be modified in parallel mode, but the modification is + restricted to the parallel group leader. + PARALLEL DML SAFE indicates that the data in the table + can be modified in parallel mode without restriction. Note that + PostgreSQL currently does not support data + modification by parallel workers. + + + + Note that for partitioned table, PARALLEL DML DEFAULT + is the same as PARALLEL DML UNSAFE which indicates + that the data in the table can't be modified in parallel mode. + + + + Tables should be labeled parallel dml unsafe/restricted if any parallel + unsafe/restricted function could be executed when modifying the data in + the table + (e.g., functions in triggers/index expressions/constraints etc.). + + + + To assist in correctly labeling the parallel DML safety level of a table, + PostgreSQL provides some utility functions that may be used during + application development. Refer to + + pg_get_table_parallel_dml_safety() and + + pg_get_table_max_parallel_dml_hazard() for more information. + + + + USING INDEX TABLESPACE tablespace_name diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml index 07558ab56c..ba5f80d45c 100644 --- a/doc/src/sgml/ref/create_table_as.sgml +++ b/doc/src/sgml/ref/create_table_as.sgml @@ -27,6 +27,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] + [ PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } ] AS query [ WITH [ NO ] DATA ] @@ -223,6 +224,43 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI + + PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE } + + + PARALLEL DML DEFAULT indicates that the safety of + parallel modification will be checked automatically. This is default. + PARALLEL DML UNSAFE indicates that the data in the + table can't be modified in parallel mode, and this forces a serial + execution plan for DML statements operating on the table. + PARALLEL DML RESTRICTED indicates that the data in the + table can be modified in parallel mode, but the modification is + restricted to the parallel group leader. PARALLEL DML + SAFE indicates that the data in the table can be modified in + parallel mode without restriction. Note that + PostgreSQL currently does not support data + modification by parallel workers. + + + + Tables should be labeled parallel dml unsafe/restricted if any parallel + unsafe/restricted function could be executed when modifying the data in + table (e.g., functions in trigger/index expression/constraints ...). + + + + To assist in correctly labeling the parallel DML safety level of a table, + PostgreSQL provides some utility functions that may be used during + application development. Refer to + + pg_get_table_parallel_dml_safety() and + + pg_get_table_max_parallel_dml_hazard() for more information. + + + + + query diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 4bee0c1173..5fefbe9347 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2206,6 +2206,7 @@ alter table test_storage alter column a set storage external; b | integer | | | 0 | plain | | Indexes: "test_storage_idx" btree (b, a) +Parallel DML: default \d+ test_storage_idx Index "public.test_storage_idx" @@ -4193,6 +4194,7 @@ ALTER TABLE range_parted2 DETACH PARTITION part_rp CONCURRENTLY; a | integer | | | | plain | | Partition key: RANGE (a) Number of partitions: 0 +Parallel DML: default -- constraint should be created \d part_rp diff --git a/src/test/regress/expected/compression_1.out b/src/test/regress/expected/compression_1.out index 1ce2962d55..ad2b1ff001 100644 --- a/src/test/regress/expected/compression_1.out +++ b/src/test/regress/expected/compression_1.out @@ -12,6 +12,7 @@ INSERT INTO cmdata VALUES(repeat('1234567890', 1000)); f1 | text | | | | extended | pglz | | Indexes: "idx" btree (f1) +Parallel DML: default CREATE TABLE cmdata1(f1 TEXT COMPRESSION lz4); ERROR: compression method lz4 not supported @@ -51,6 +52,7 @@ SELECT * INTO cmmove1 FROM cmdata; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+------+-----------+----------+---------+----------+-------------+--------------+------------- f1 | text | | | | extended | | | +Parallel DML: default SELECT pg_column_compression(f1) FROM cmmove1; pg_column_compression @@ -138,6 +140,7 @@ CREATE TABLE cmdata2 (f1 int); Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+---------+-----------+----------+---------+---------+-------------+--------------+------------- f1 | integer | | | | plain | | | +Parallel DML: default ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE varchar; \d+ cmdata2 @@ -145,6 +148,7 @@ ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE varchar; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+-------------------+-----------+----------+---------+----------+-------------+--------------+------------- f1 | character varying | | | | extended | | | +Parallel DML: default ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE int USING f1::integer; \d+ cmdata2 @@ -152,6 +156,7 @@ ALTER TABLE cmdata2 ALTER COLUMN f1 TYPE int USING f1::integer; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+---------+-----------+----------+---------+---------+-------------+--------------+------------- f1 | integer | | | | plain | | | +Parallel DML: default --changing column storage should not impact the compression method --but the data should not be compressed @@ -162,6 +167,7 @@ ALTER TABLE cmdata2 ALTER COLUMN f1 SET COMPRESSION pglz; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+-------------------+-----------+----------+---------+----------+-------------+--------------+------------- f1 | character varying | | | | extended | pglz | | +Parallel DML: default ALTER TABLE cmdata2 ALTER COLUMN f1 SET STORAGE plain; \d+ cmdata2 @@ -169,6 +175,7 @@ ALTER TABLE cmdata2 ALTER COLUMN f1 SET STORAGE plain; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+-------------------+-----------+----------+---------+---------+-------------+--------------+------------- f1 | character varying | | | | plain | pglz | | +Parallel DML: default INSERT INTO cmdata2 VALUES (repeat('123456789', 800)); SELECT pg_column_compression(f1) FROM cmdata2; @@ -249,6 +256,7 @@ INSERT INTO cmdata VALUES (repeat('123456789', 4004)); f1 | text | | | | extended | pglz | | Indexes: "idx" btree (f1) +Parallel DML: default SELECT pg_column_compression(f1) FROM cmdata; pg_column_compression @@ -263,6 +271,7 @@ ALTER TABLE cmdata2 ALTER COLUMN f1 SET COMPRESSION default; Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------+-------------------+-----------+----------+---------+---------+-------------+--------------+------------- f1 | character varying | | | | plain | | | +Parallel DML: default -- test alter compression method for materialized views ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index 5f3685e9ef..cd0d153461 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -519,6 +519,7 @@ alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); f1 | integer | | | | plain | | Check constraints: "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*)) +Parallel DML: default copy check_con_tbl from stdin; NOTICE: input = {"f1":1} diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index a958b84979..fe10ac8bb0 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -505,6 +505,7 @@ Number of partitions: 0 b | text | | | | extended | | Partition key: RANGE (((a + 1)), substr(b, 1, 5)) Number of partitions: 0 +Parallel DML: default INSERT INTO partitioned2 VALUES (1, 'hello'); ERROR: no partition of relation "partitioned2" found for row @@ -518,6 +519,7 @@ CREATE TABLE part2_1 PARTITION OF partitioned2 FOR VALUES FROM (-1, 'aaaaa') TO b | text | | | | extended | | Partition of: partitioned2 FOR VALUES FROM ('-1', 'aaaaa') TO (100, 'ccccc') Partition constraint: (((a + 1) IS NOT NULL) AND (substr(b, 1, 5) IS NOT NULL) AND (((a + 1) > '-1'::integer) OR (((a + 1) = '-1'::integer) AND (substr(b, 1, 5) >= 'aaaaa'::text))) AND (((a + 1) < 100) OR (((a + 1) = 100) AND (substr(b, 1, 5) < 'ccccc'::text)))) +Parallel DML: default DROP TABLE partitioned, partitioned2; -- check reference to partitioned table's rowtype in partition descriptor @@ -559,6 +561,7 @@ select * from partitioned where partitioned = '(1,2)'::partitioned; b | integer | | | | plain | | Partition of: partitioned FOR VALUES IN ('(1,2)') Partition constraint: (((partitioned1.*)::partitioned IS DISTINCT FROM NULL) AND ((partitioned1.*)::partitioned = '(1,2)'::partitioned)) +Parallel DML: default drop table partitioned; -- check that dependencies of partition columns are handled correctly @@ -618,6 +621,7 @@ Partitions: part_null FOR VALUES IN (NULL), part_p1 FOR VALUES IN (1), part_p2 FOR VALUES IN (2), part_p3 FOR VALUES IN (3) +Parallel DML: default -- forbidden expressions for partition bound with list partitioned table CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (somename); @@ -1064,6 +1068,7 @@ drop table test_part_coll_posix; b | integer | | not null | 1 | plain | | Partition of: parted FOR VALUES IN ('b') Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text)) +Parallel DML: default -- Both partition bound and partition key in describe output \d+ part_c @@ -1076,6 +1081,7 @@ Partition of: parted FOR VALUES IN ('c') Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text)) Partition key: RANGE (b) Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10) +Parallel DML: default -- a level-2 partition's constraint will include the parent's expressions \d+ part_c_1_10 @@ -1086,6 +1092,7 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10) b | integer | | not null | 0 | plain | | Partition of: part_c FOR VALUES FROM (1) TO (10) Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10)) +Parallel DML: default -- Show partition count in the parent's describe output -- Tempted to include \d+ output listing partitions with bound info but @@ -1120,6 +1127,7 @@ CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MI c | integer | | | | plain | | Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL)) +Parallel DML: default DROP TABLE unbounded_range_part; CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE); @@ -1132,6 +1140,7 @@ CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALU c | integer | | | | plain | | Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1)) +Parallel DML: default CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE); \d+ range_parted4_2 @@ -1143,6 +1152,7 @@ CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5 c | integer | | | | plain | | Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7)))) +Parallel DML: default CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE); \d+ range_parted4_3 @@ -1154,6 +1164,7 @@ CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, M c | integer | | | | plain | | Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9)) +Parallel DML: default DROP TABLE range_parted4; -- user-defined operator class in partition key @@ -1190,6 +1201,7 @@ SELECT obj_description('parted_col_comment'::regclass); b | text | | | | extended | | Partition key: LIST (a) Number of partitions: 0 +Parallel DML: default DROP TABLE parted_col_comment; -- list partitioning on array type column @@ -1202,6 +1214,7 @@ CREATE TABLE arrlp12 PARTITION OF arrlp FOR VALUES IN ('{1}', '{2}'); a | integer[] | | | | extended | | Partition of: arrlp FOR VALUES IN ('{1}', '{2}') Partition constraint: ((a IS NOT NULL) AND ((a = '{1}'::integer[]) OR (a = '{2}'::integer[]))) +Parallel DML: default DROP TABLE arrlp; -- partition on boolean column @@ -1216,6 +1229,7 @@ create table boolspart_f partition of boolspart for values in (false); Partition key: LIST (a) Partitions: boolspart_f FOR VALUES IN (false), boolspart_t FOR VALUES IN (true) +Parallel DML: default drop table boolspart; -- partitions mixing temporary and permanent relations diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out index 0ed94f1d2f..3757e2f8d0 100644 --- a/src/test/regress/expected/create_table_like.out +++ b/src/test/regress/expected/create_table_like.out @@ -333,6 +333,7 @@ CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING a | text | | not null | | main | | b | text | | | | extended | | c | text | | | | external | | +Parallel DML: default CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS); \d+ ctlt12_comments @@ -342,6 +343,7 @@ CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDIN a | text | | not null | | extended | | A b | text | | | | extended | | B c | text | | | | extended | | C +Parallel DML: default CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1); NOTICE: merging column "a" with inherited definition @@ -356,6 +358,7 @@ NOTICE: merging constraint "ctlt1_a_check" with inherited definition Check constraints: "ctlt1_a_check" CHECK (length(a) > 2) Inherits: ctlt1 +Parallel DML: default SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass; description @@ -378,6 +381,7 @@ Check constraints: "ctlt3_c_check" CHECK (length(c) < 7) Inherits: ctlt1, ctlt3 +Parallel DML: default CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1); NOTICE: merging column "a" with inherited definition @@ -395,6 +399,7 @@ Check constraints: "ctlt3_a_check" CHECK (length(a) < 5) "ctlt3_c_check" CHECK (length(c) < 7) Inherits: ctlt1 +Parallel DML: default SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass; description @@ -418,6 +423,7 @@ Check constraints: Statistics objects: "public.ctlt_all_a_b_stat" ON a, b FROM ctlt_all "public.ctlt_all_expr_stat" ON (a || b) FROM ctlt_all +Parallel DML: default SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 'ctlt_all'::regclass ORDER BY c.relname, objsubid; relname | objsubid | description @@ -458,6 +464,7 @@ Check constraints: Statistics objects: "public.pg_attrdef_a_b_stat" ON a, b FROM public.pg_attrdef "public.pg_attrdef_expr_stat" ON (a || b) FROM public.pg_attrdef +Parallel DML: default DROP TABLE public.pg_attrdef; -- Check that LIKE isn't confused when new table masks the old, either @@ -480,6 +487,7 @@ Check constraints: Statistics objects: "ctl_schema.ctlt1_a_b_stat" ON a, b FROM ctlt1 "ctl_schema.ctlt1_expr_stat" ON (a || b) FROM ctlt1 +Parallel DML: default ROLLBACK; DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE; diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 411d5c003e..cc0bbe85d1 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -276,6 +276,7 @@ Rules: silly AS ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1.r = (dcomptable.d1).r - 1::double precision, d1.i = (dcomptable.d1).i + 1::double precision WHERE (dcomptable.d1).i > 0::double precision +Parallel DML: default drop table dcomptable; drop type comptype cascade; @@ -413,6 +414,7 @@ Rules: silly AS ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1[1].r = dcomptable.d1[1].r - 1::double precision, d1[1].i = dcomptable.d1[1].i + 1::double precision WHERE dcomptable.d1[1].i > 0::double precision +Parallel DML: default drop table dcomptable; drop type comptype cascade; diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index 426080ae39..dcbcdb512a 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -735,6 +735,7 @@ Check constraints: "ft1_c3_check" CHECK (c3 >= '01-01-1994'::date AND c3 <= '01-31-1994'::date) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default \det+ List of foreign tables @@ -857,6 +858,7 @@ Check constraints: "ft1_c3_check" CHECK (c3 >= '01-01-1994'::date AND c3 <= '01-31-1994'::date) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default -- can't change the column type if it's used elsewhere CREATE TABLE use_ft1_column_type (x ft1); @@ -1396,6 +1398,7 @@ CREATE FOREIGN TABLE ft2 () INHERITS (fd_pt1) c2 | text | | | | extended | | c3 | date | | | | plain | | Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1407,6 +1410,7 @@ Child tables: ft2 Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default DROP FOREIGN TABLE ft2; \d+ fd_pt1 @@ -1416,6 +1420,7 @@ DROP FOREIGN TABLE ft2; c1 | integer | | not null | | plain | | c2 | text | | | | extended | | c3 | date | | | | plain | | +Parallel DML: default CREATE FOREIGN TABLE ft2 ( c1 integer NOT NULL, @@ -1431,6 +1436,7 @@ CREATE FOREIGN TABLE ft2 ( c3 | date | | | | | plain | | Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; \d+ fd_pt1 @@ -1441,6 +1447,7 @@ ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; c2 | text | | | | extended | | c3 | date | | | | plain | | Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1452,6 +1459,7 @@ Child tables: ft2 Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default CREATE TABLE ct3() INHERITS(ft2); CREATE FOREIGN TABLE ft3 ( @@ -1475,6 +1483,7 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 Child tables: ct3, ft3 +Parallel DML: default \d+ ct3 Table "public.ct3" @@ -1484,6 +1493,7 @@ Child tables: ct3, c2 | text | | | | extended | | c3 | date | | | | plain | | Inherits: ft2 +Parallel DML: default \d+ ft3 Foreign table "public.ft3" @@ -1494,6 +1504,7 @@ Inherits: ft2 c3 | date | | | | | plain | | Server: s0 Inherits: ft2 +Parallel DML: default -- add attributes recursively ALTER TABLE fd_pt1 ADD COLUMN c4 integer; @@ -1514,6 +1525,7 @@ ALTER TABLE fd_pt1 ADD COLUMN c8 integer; c7 | integer | | not null | | plain | | c8 | integer | | | | plain | | Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1532,6 +1544,7 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 Child tables: ct3, ft3 +Parallel DML: default \d+ ct3 Table "public.ct3" @@ -1546,6 +1559,7 @@ Child tables: ct3, c7 | integer | | not null | | plain | | c8 | integer | | | | plain | | Inherits: ft2 +Parallel DML: default \d+ ft3 Foreign table "public.ft3" @@ -1561,6 +1575,7 @@ Inherits: ft2 c8 | integer | | | | | plain | | Server: s0 Inherits: ft2 +Parallel DML: default -- alter attributes recursively ALTER TABLE fd_pt1 ALTER COLUMN c4 SET DEFAULT 0; @@ -1588,6 +1603,7 @@ ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL; c7 | integer | | | | plain | | c8 | text | | | | external | | Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1606,6 +1622,7 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 Child tables: ct3, ft3 +Parallel DML: default -- drop attributes recursively ALTER TABLE fd_pt1 DROP COLUMN c4; @@ -1621,6 +1638,7 @@ ALTER TABLE fd_pt1 DROP COLUMN c8; c2 | text | | | | extended | | c3 | date | | | | plain | | Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1634,6 +1652,7 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 Child tables: ct3, ft3 +Parallel DML: default -- add constraints recursively ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk1 CHECK (c1 > 0) NO INHERIT; @@ -1661,6 +1680,7 @@ Check constraints: "fd_pt1chk1" CHECK (c1 > 0) NO INHERIT "fd_pt1chk2" CHECK (c2 <> ''::text) Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1676,6 +1696,7 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 Child tables: ct3, ft3 +Parallel DML: default DROP FOREIGN TABLE ft2; -- ERROR ERROR: cannot drop foreign table ft2 because other objects depend on it @@ -1708,6 +1729,7 @@ Check constraints: "fd_pt1chk1" CHECK (c1 > 0) NO INHERIT "fd_pt1chk2" CHECK (c2 <> ''::text) Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1721,6 +1743,7 @@ Check constraints: Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default -- drop constraints recursively ALTER TABLE fd_pt1 DROP CONSTRAINT fd_pt1chk1 CASCADE; @@ -1738,6 +1761,7 @@ ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk3 CHECK (c2 <> '') NOT VALID; Check constraints: "fd_pt1chk3" CHECK (c2 <> ''::text) NOT VALID Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1752,6 +1776,7 @@ Check constraints: Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default -- VALIDATE CONSTRAINT need do nothing on foreign tables ALTER TABLE fd_pt1 VALIDATE CONSTRAINT fd_pt1chk3; @@ -1765,6 +1790,7 @@ ALTER TABLE fd_pt1 VALIDATE CONSTRAINT fd_pt1chk3; Check constraints: "fd_pt1chk3" CHECK (c2 <> ''::text) Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1779,6 +1805,7 @@ Check constraints: Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default -- changes name of an attribute recursively ALTER TABLE fd_pt1 RENAME COLUMN c1 TO f1; @@ -1796,6 +1823,7 @@ ALTER TABLE fd_pt1 RENAME CONSTRAINT fd_pt1chk3 TO f2_check; Check constraints: "f2_check" CHECK (f2 <> ''::text) Child tables: ft2 +Parallel DML: default \d+ ft2 Foreign table "public.ft2" @@ -1810,6 +1838,7 @@ Check constraints: Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') Inherits: fd_pt1 +Parallel DML: default -- TRUNCATE doesn't work on foreign tables, either directly or recursively TRUNCATE ft2; -- ERROR @@ -1859,6 +1888,7 @@ CREATE FOREIGN TABLE fd_pt2_1 PARTITION OF fd_pt2 FOR VALUES IN (1) c3 | date | | | | plain | | Partition key: LIST (c1) Partitions: fd_pt2_1 FOR VALUES IN (1) +Parallel DML: default \d+ fd_pt2_1 Foreign table "public.fd_pt2_1" @@ -1871,6 +1901,7 @@ Partition of: fd_pt2 FOR VALUES IN (1) Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1)) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default -- partition cannot have additional columns DROP FOREIGN TABLE fd_pt2_1; @@ -1890,6 +1921,7 @@ CREATE FOREIGN TABLE fd_pt2_1 ( c4 | character(1) | | | | | extended | | Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ERROR: table "fd_pt2_1" contains column "c4" not found in parent "fd_pt2" @@ -1904,6 +1936,7 @@ DROP FOREIGN TABLE fd_pt2_1; c3 | date | | | | plain | | Partition key: LIST (c1) Number of partitions: 0 +Parallel DML: default CREATE FOREIGN TABLE fd_pt2_1 ( c1 integer NOT NULL, @@ -1919,6 +1952,7 @@ CREATE FOREIGN TABLE fd_pt2_1 ( c3 | date | | | | | plain | | Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default -- no attach partition validation occurs for foreign tables ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); @@ -1931,6 +1965,7 @@ ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); c3 | date | | | | plain | | Partition key: LIST (c1) Partitions: fd_pt2_1 FOR VALUES IN (1) +Parallel DML: default \d+ fd_pt2_1 Foreign table "public.fd_pt2_1" @@ -1943,6 +1978,7 @@ Partition of: fd_pt2 FOR VALUES IN (1) Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1)) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default -- cannot add column to a partition ALTER TABLE fd_pt2_1 ADD c4 char; @@ -1959,6 +1995,7 @@ ALTER TABLE fd_pt2_1 ADD CONSTRAINT p21chk CHECK (c2 <> ''); c3 | date | | | | plain | | Partition key: LIST (c1) Partitions: fd_pt2_1 FOR VALUES IN (1) +Parallel DML: default \d+ fd_pt2_1 Foreign table "public.fd_pt2_1" @@ -1973,6 +2010,7 @@ Check constraints: "p21chk" CHECK (c2 <> ''::text) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default -- cannot drop inherited NOT NULL constraint from a partition ALTER TABLE fd_pt2_1 ALTER c1 DROP NOT NULL; @@ -1989,6 +2027,7 @@ ALTER TABLE fd_pt2 ALTER c2 SET NOT NULL; c3 | date | | | | plain | | Partition key: LIST (c1) Number of partitions: 0 +Parallel DML: default \d+ fd_pt2_1 Foreign table "public.fd_pt2_1" @@ -2001,6 +2040,7 @@ Check constraints: "p21chk" CHECK (c2 <> ''::text) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ERROR: column "c2" in child table must be marked NOT NULL @@ -2019,6 +2059,7 @@ Partition key: LIST (c1) Check constraints: "fd_pt2chk1" CHECK (c1 > 0) Number of partitions: 0 +Parallel DML: default \d+ fd_pt2_1 Foreign table "public.fd_pt2_1" @@ -2031,6 +2072,7 @@ Check constraints: "p21chk" CHECK (c2 <> ''::text) Server: s0 FDW options: (delimiter ',', quote '"', "be quoted" 'value') +Parallel DML: default ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ERROR: child table is missing constraint "fd_pt2chk1" diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out index 99811570b7..a5b5a1b24d 100644 --- a/src/test/regress/expected/identity.out +++ b/src/test/regress/expected/identity.out @@ -506,6 +506,7 @@ TABLE itest8; f3 | integer | | not null | generated by default as identity | plain | | f4 | bigint | | not null | generated always as identity | plain | | f5 | bigint | | | | plain | | +Parallel DML: default \d itest8_f2_seq Sequence "public.itest8_f2_seq" diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 2d49e765de..0a720862eb 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1059,6 +1059,7 @@ ALTER TABLE inhts RENAME d TO dd; dd | integer | | | | plain | | Inherits: inht1, inhs1 +Parallel DML: default DROP TABLE inhts; -- Test for renaming in diamond inheritance @@ -1079,6 +1080,7 @@ ALTER TABLE inht1 RENAME aa TO aaa; z | integer | | | | plain | | Inherits: inht2, inht3 +Parallel DML: default CREATE TABLE inhts (d int) INHERITS (inht2, inhs1); NOTICE: merging multiple inherited definitions of column "b" @@ -1096,6 +1098,7 @@ ERROR: cannot rename inherited column "b" d | integer | | | | plain | | Inherits: inht2, inhs1 +Parallel DML: default WITH RECURSIVE r AS ( SELECT 'inht1'::regclass AS inhrelid @@ -1142,6 +1145,7 @@ CREATE TABLE test_constraints_inh () INHERITS (test_constraints); Indexes: "test_constraints_val1_val2_key" UNIQUE CONSTRAINT, btree (val1, val2) Child tables: test_constraints_inh +Parallel DML: default ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key; \d+ test_constraints @@ -1152,6 +1156,7 @@ ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key val1 | character varying | | | | extended | | val2 | integer | | | | plain | | Child tables: test_constraints_inh +Parallel DML: default \d+ test_constraints_inh Table "public.test_constraints_inh" @@ -1161,6 +1166,7 @@ Child tables: test_constraints_inh val1 | character varying | | | | extended | | val2 | integer | | | | plain | | Inherits: test_constraints +Parallel DML: default DROP TABLE test_constraints_inh; DROP TABLE test_constraints; @@ -1177,6 +1183,7 @@ CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints); Indexes: "test_ex_constraints_c_excl" EXCLUDE USING gist (c WITH &&) Child tables: test_ex_constraints_inh +Parallel DML: default ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl; \d+ test_ex_constraints @@ -1185,6 +1192,7 @@ ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl; --------+--------+-----------+----------+---------+---------+--------------+------------- c | circle | | | | plain | | Child tables: test_ex_constraints_inh +Parallel DML: default \d+ test_ex_constraints_inh Table "public.test_ex_constraints_inh" @@ -1192,6 +1200,7 @@ Child tables: test_ex_constraints_inh --------+--------+-----------+----------+---------+---------+--------------+------------- c | circle | | | | plain | | Inherits: test_ex_constraints +Parallel DML: default DROP TABLE test_ex_constraints_inh; DROP TABLE test_ex_constraints; @@ -1208,6 +1217,7 @@ Indexes: "test_primary_constraints_pkey" PRIMARY KEY, btree (id) Referenced by: TABLE "test_foreign_constraints" CONSTRAINT "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id) +Parallel DML: default \d+ test_foreign_constraints Table "public.test_foreign_constraints" @@ -1217,6 +1227,7 @@ Referenced by: Foreign-key constraints: "test_foreign_constraints_id1_fkey" FOREIGN KEY (id1) REFERENCES test_primary_constraints(id) Child tables: test_foreign_constraints_inh +Parallel DML: default ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey; \d+ test_foreign_constraints @@ -1225,6 +1236,7 @@ ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id --------+---------+-----------+----------+---------+---------+--------------+------------- id1 | integer | | | | plain | | Child tables: test_foreign_constraints_inh +Parallel DML: default \d+ test_foreign_constraints_inh Table "public.test_foreign_constraints_inh" @@ -1232,6 +1244,7 @@ Child tables: test_foreign_constraints_inh --------+---------+-----------+----------+---------+---------+--------------+------------- id1 | integer | | | | plain | | Inherits: test_foreign_constraints +Parallel DML: default DROP TABLE test_foreign_constraints_inh; DROP TABLE test_foreign_constraints; diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index 5063a3dc22..c8440449c1 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -177,6 +177,7 @@ Rules: irule3 AS ON INSERT TO inserttest2 DO INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) SELECT new.f1, new.f2 +Parallel DML: default drop table inserttest2; drop table inserttest; @@ -482,6 +483,7 @@ Partitions: part_aa_bb FOR VALUES IN ('aa', 'bb'), part_null FOR VALUES IN (NULL), part_xx_yy FOR VALUES IN ('xx', 'yy'), PARTITIONED, part_default DEFAULT, PARTITIONED +Parallel DML: default -- cleanup drop table range_parted, list_parted; @@ -497,6 +499,7 @@ create table part_default partition of list_parted default; a | integer | | | | plain | | Partition of: list_parted DEFAULT No partition constraint +Parallel DML: default insert into part_default values (null); insert into part_default values (1); @@ -888,6 +891,7 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL mcrparted6_common_ge_10 FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE), mcrparted7_gt_common_lt_d FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE), mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE) +Parallel DML: default \d+ mcrparted1_lt_b Table "public.mcrparted1_lt_b" @@ -897,6 +901,7 @@ Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVAL b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text)) +Parallel DML: default \d+ mcrparted2_b Table "public.mcrparted2_b" @@ -906,6 +911,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text)) b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text)) +Parallel DML: default \d+ mcrparted3_c_to_common Table "public.mcrparted3_c_to_common" @@ -915,6 +921,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text)) +Parallel DML: default \d+ mcrparted4_common_lt_0 Table "public.mcrparted4_common_lt_0" @@ -924,6 +931,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0)) +Parallel DML: default \d+ mcrparted5_common_0_to_10 Table "public.mcrparted5_common_0_to_10" @@ -933,6 +941,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::te b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10)) +Parallel DML: default \d+ mcrparted6_common_ge_10 Table "public.mcrparted6_common_ge_10" @@ -942,6 +951,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::te b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10)) +Parallel DML: default \d+ mcrparted7_gt_common_lt_d Table "public.mcrparted7_gt_common_lt_d" @@ -951,6 +961,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::te b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text)) +Parallel DML: default \d+ mcrparted8_ge_d Table "public.mcrparted8_ge_d" @@ -960,6 +971,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::te b | integer | | | | plain | | Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE) Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text)) +Parallel DML: default insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10), ('comm', -10), ('common', -10), ('common', 0), ('common', 10), diff --git a/src/test/regress/expected/insert_parallel.out b/src/test/regress/expected/insert_parallel.out new file mode 100644 index 0000000000..304237f619 --- /dev/null +++ b/src/test/regress/expected/insert_parallel.out @@ -0,0 +1,713 @@ +-- +-- PARALLEL +-- +-- +-- START: setup some tables and data needed by the tests. +-- +-- Setup - index expressions test +create function pg_class_relname(Oid) +returns name language sql parallel unsafe +as 'select relname from pg_class where $1 = oid'; +-- For testing purposes, we'll mark this function as parallel-unsafe +create or replace function fullname_parallel_unsafe(f text, l text) returns text as $$ + begin + return f || l; + end; +$$ language plpgsql immutable parallel unsafe; +create or replace function fullname_parallel_restricted(f text, l text) returns text as $$ + begin + return f || l; + end; +$$ language plpgsql immutable parallel restricted; +create table names(index int, first_name text, last_name text); +create table names2(index int, first_name text, last_name text); +create index names2_fullname_idx on names2 (fullname_parallel_unsafe(first_name, last_name)); +create table names4(index int, first_name text, last_name text); +create index names4_fullname_idx on names4 (fullname_parallel_restricted(first_name, last_name)); +insert into names values + (1, 'albert', 'einstein'), + (2, 'niels', 'bohr'), + (3, 'erwin', 'schrodinger'), + (4, 'leonhard', 'euler'), + (5, 'stephen', 'hawking'), + (6, 'isaac', 'newton'), + (7, 'alan', 'turing'), + (8, 'richard', 'feynman'); +-- Setup - column default tests +create or replace function bdefault_unsafe () +returns int language plpgsql parallel unsafe as $$ +begin + RETURN 5; +end $$; +create or replace function cdefault_restricted () +returns int language plpgsql parallel restricted as $$ +begin + RETURN 10; +end $$; +create or replace function ddefault_safe () +returns int language plpgsql parallel safe as $$ +begin + RETURN 20; +end $$; +create table testdef(a int, b int default bdefault_unsafe(), c int default cdefault_restricted(), d int default ddefault_safe()); +create table test_data(a int); +insert into test_data select * from generate_series(1,10); +-- +-- END: setup some tables and data needed by the tests. +-- +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; +create table para_insert_p1 ( + unique1 int4 PRIMARY KEY, + stringu1 name +); +create table para_insert_f1 ( + unique1 int4 REFERENCES para_insert_p1(unique1), + stringu1 name +); +create table para_insert_with_parallel_unsafe( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml unsafe; +create table para_insert_with_parallel_restricted( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml restricted; +create table para_insert_with_parallel_safe( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml safe; +create table para_insert_with_parallel_auto( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml default; +-- Check FK trigger +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('para_insert_f1'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | r + pg_trigger | r + pg_proc | r + pg_trigger | r +(4 rows) + +select pg_get_table_max_parallel_dml_hazard('para_insert_f1'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + r +(1 row) + +-- +-- Test INSERT with underlying query. +-- Set parallel dml safe. +-- (should create plan with parallel SELECT, Gather parent node) +-- +alter table para_insert_p1 parallel dml safe; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; + QUERY PLAN +---------------------------------------- + Insert on para_insert_p1 + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +insert into para_insert_p1 select unique1, stringu1 from tenk1; +-- select some values to verify that the parallel insert worked +select count(*), sum(unique1) from para_insert_p1; + count | sum +-------+---------- + 10000 | 49995000 +(1 row) + +-- verify that the same transaction has been used by all parallel workers +select count(*) from (select distinct cmin,xmin from para_insert_p1) as dt; + count +------- + 1 +(1 row) + +explain (costs off) insert into para_insert_with_parallel_safe select unique1, stringu1 from tenk1; + QUERY PLAN +------------------------------------------ + Insert on para_insert_with_parallel_safe + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +-- +-- Set parallel dml unsafe. +-- (should not create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml unsafe; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; + QUERY PLAN +-------------------------- + Insert on para_insert_p1 + -> Seq Scan on tenk1 +(2 rows) + +explain (costs off) insert into para_insert_with_parallel_unsafe select unique1, stringu1 from tenk1; + QUERY PLAN +-------------------------------------------- + Insert on para_insert_with_parallel_unsafe + -> Seq Scan on tenk1 +(2 rows) + +-- +-- Set parallel dml restricted. +-- (should create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml restricted; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; + QUERY PLAN +---------------------------------------- + Insert on para_insert_p1 + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +explain (costs off) insert into para_insert_with_parallel_restricted select unique1, stringu1 from tenk1; + QUERY PLAN +------------------------------------------------ + Insert on para_insert_with_parallel_restricted + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +-- +-- Reset parallel dml. +-- (should create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml default; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; + QUERY PLAN +---------------------------------------- + Insert on para_insert_p1 + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +explain (costs off) insert into para_insert_with_parallel_auto select unique1, stringu1 from tenk1; + QUERY PLAN +------------------------------------------ + Insert on para_insert_with_parallel_auto + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +-- +-- Test INSERT with ordered underlying query. +-- (should create plan with parallel SELECT, GatherMerge parent node) +-- +truncate para_insert_p1 cascade; +NOTICE: truncate cascades to table "para_insert_f1" +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 order by unique1; + QUERY PLAN +---------------------------------------------- + Insert on para_insert_p1 + -> Gather Merge + Workers Planned: 4 + -> Sort + Sort Key: tenk1.unique1 + -> Parallel Seq Scan on tenk1 +(6 rows) + +insert into para_insert_p1 select unique1, stringu1 from tenk1 order by unique1; +-- select some values to verify that the parallel insert worked +select count(*), sum(unique1) from para_insert_p1; + count | sum +-------+---------- + 10000 | 49995000 +(1 row) + +-- verify that the same transaction has been used by all parallel workers +select count(*) from (select distinct cmin,xmin from para_insert_p1) as dt; + count +------- + 1 +(1 row) + +-- +-- Test INSERT with RETURNING clause. +-- (should create plan with parallel SELECT, Gather parent node) +-- +create table test_data1(like test_data); +explain (costs off) insert into test_data1 select * from test_data where a = 10 returning a as data; + QUERY PLAN +-------------------------------------------- + Insert on test_data1 + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on test_data + Filter: (a = 10) +(5 rows) + +insert into test_data1 select * from test_data where a = 10 returning a as data; + data +------ + 10 +(1 row) + +-- +-- Test INSERT into a table with a foreign key. +-- (Insert into a table with a foreign key is parallel-restricted, +-- as doing this in a parallel worker would create a new commandId +-- and within a worker this is not currently supported) +-- +explain (costs off) insert into para_insert_f1 select unique1, stringu1 from tenk1; + QUERY PLAN +---------------------------------------- + Insert on para_insert_f1 + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +insert into para_insert_f1 select unique1, stringu1 from tenk1; +-- select some values to verify that the insert worked +select count(*), sum(unique1) from para_insert_f1; + count | sum +-------+---------- + 10000 | 49995000 +(1 row) + +-- +-- Test INSERT with ON CONFLICT ... DO UPDATE ... +-- (should not create a parallel plan) +-- +create table test_conflict_table(id serial primary key, somedata int); +explain (costs off) insert into test_conflict_table(id, somedata) select a, a from test_data; + QUERY PLAN +-------------------------------------------- + Insert on test_conflict_table + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on test_data +(4 rows) + +insert into test_conflict_table(id, somedata) select a, a from test_data; +explain (costs off) insert into test_conflict_table(id, somedata) select a, a from test_data ON CONFLICT(id) DO UPDATE SET somedata = EXCLUDED.somedata + 1; + QUERY PLAN +------------------------------------------------------ + Insert on test_conflict_table + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: test_conflict_table_pkey + -> Seq Scan on test_data +(4 rows) + +-- +-- Test INSERT with parallel-unsafe index expression +-- (should not create a parallel plan) +-- +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names2'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | u + pg_index | u +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('names2'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + u +(1 row) + +explain (costs off) insert into names2 select * from names; + QUERY PLAN +------------------------- + Insert on names2 + -> Seq Scan on names +(2 rows) + +-- +-- Test INSERT with parallel-restricted index expression +-- (should create a parallel plan) +-- +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names4'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | r + pg_index | r +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('names4'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + r +(1 row) + +explain (costs off) insert into names4 select * from names; + QUERY PLAN +---------------------------------------- + Insert on names4 + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on names +(4 rows) + +-- +-- Test INSERT with underlying query - and RETURNING (no projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names5 (like names); +explain (costs off) insert into names5 select * from names returning *; + QUERY PLAN +---------------------------------------- + Insert on names5 + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on names +(4 rows) + +-- +-- Test INSERT with underlying ordered query - and RETURNING (no projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names6 (like names); +explain (costs off) insert into names6 select * from names order by last_name returning *; + QUERY PLAN +---------------------------------------------- + Insert on names6 + -> Gather Merge + Workers Planned: 3 + -> Sort + Sort Key: names.last_name + -> Parallel Seq Scan on names +(6 rows) + +insert into names6 select * from names order by last_name returning *; + index | first_name | last_name +-------+------------+------------- + 2 | niels | bohr + 1 | albert | einstein + 4 | leonhard | euler + 8 | richard | feynman + 5 | stephen | hawking + 6 | isaac | newton + 3 | erwin | schrodinger + 7 | alan | turing +(8 rows) + +-- +-- Test INSERT with underlying ordered query - and RETURNING (with projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names7 (like names); +explain (costs off) insert into names7 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; + QUERY PLAN +---------------------------------------------- + Insert on names7 + -> Gather Merge + Workers Planned: 3 + -> Sort + Sort Key: names.last_name + -> Parallel Seq Scan on names +(6 rows) + +insert into names7 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; + last_name_then_first_name +--------------------------- + bohr, niels + einstein, albert + euler, leonhard + feynman, richard + hawking, stephen + newton, isaac + schrodinger, erwin + turing, alan +(8 rows) + +-- +-- Test INSERT into temporary table with underlying query. +-- (Insert into a temp table is parallel-restricted; +-- should create a parallel plan; parallel SELECT) +-- +create temporary table temp_names (like names); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('temp_names'); + pg_class_relname | proparallel +------------------+------------- + pg_class | r +(1 row) + +select pg_get_table_max_parallel_dml_hazard('temp_names'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + r +(1 row) + +explain (costs off) insert into temp_names select * from names; + QUERY PLAN +---------------------------------------- + Insert on temp_names + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on names +(4 rows) + +insert into temp_names select * from names; +-- +-- Test INSERT with column defaults +-- +-- +-- +-- Parallel INSERT with unsafe column default, should not use a parallel plan +-- +explain (costs off) insert into testdef(a,c,d) select a,a*4,a*8 from test_data; + QUERY PLAN +----------------------------- + Insert on testdef + -> Seq Scan on test_data +(2 rows) + +-- +-- Parallel INSERT with restricted column default, should use parallel SELECT +-- +explain (costs off) insert into testdef(a,b,d) select a,a*2,a*8 from test_data; + QUERY PLAN +-------------------------------------------- + Insert on testdef + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on test_data +(4 rows) + +insert into testdef(a,b,d) select a,a*2,a*8 from test_data; +select * from testdef order by a; + a | b | c | d +----+----+----+---- + 1 | 2 | 10 | 8 + 2 | 4 | 10 | 16 + 3 | 6 | 10 | 24 + 4 | 8 | 10 | 32 + 5 | 10 | 10 | 40 + 6 | 12 | 10 | 48 + 7 | 14 | 10 | 56 + 8 | 16 | 10 | 64 + 9 | 18 | 10 | 72 + 10 | 20 | 10 | 80 +(10 rows) + +truncate testdef; +-- +-- Parallel INSERT with restricted and unsafe column defaults, should not use a parallel plan +-- +explain (costs off) insert into testdef(a,d) select a,a*8 from test_data; + QUERY PLAN +----------------------------- + Insert on testdef + -> Seq Scan on test_data +(2 rows) + +-- +-- Test INSERT into partition with underlying query. +-- +create table parttable1 (a int, b name) partition by range (a); +create table parttable1_1 partition of parttable1 for values from (0) to (5000); +create table parttable1_2 partition of parttable1 for values from (5000) to (10000); +alter table parttable1 parallel dml safe; +explain (costs off) insert into parttable1 select unique1,stringu1 from tenk1; + QUERY PLAN +---------------------------------------- + Insert on parttable1 + -> Gather + Workers Planned: 4 + -> Parallel Seq Scan on tenk1 +(4 rows) + +insert into parttable1 select unique1,stringu1 from tenk1; +select count(*) from parttable1_1; + count +------- + 5000 +(1 row) + +select count(*) from parttable1_2; + count +------- + 5000 +(1 row) + +-- +-- Test table with parallel-unsafe check constraint +-- +create or replace function check_b_unsafe(b name) returns boolean as $$ + begin + return (b <> 'XXXXXX'); + end; +$$ language plpgsql parallel unsafe; +create table table_check_b(a int4, b name check (check_b_unsafe(b)), c name); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('table_check_b'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | u + pg_constraint | u +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('table_check_b'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + u +(1 row) + +explain (costs off) insert into table_check_b(a,b,c) select unique1, unique2, stringu1 from tenk1; + QUERY PLAN +------------------------- + Insert on table_check_b + -> Seq Scan on tenk1 +(2 rows) + +-- +-- Test table with parallel-safe before stmt-level triggers +-- (should create a parallel SELECT plan; triggers should fire) +-- +create table names_with_safe_trigger (like names); +create or replace function insert_before_trigger_safe() returns trigger as $$ + begin + raise notice 'hello from insert_before_trigger_safe'; + return new; + end; +$$ language plpgsql parallel safe; +create trigger insert_before_trigger_safe before insert on names_with_safe_trigger + for each statement execute procedure insert_before_trigger_safe(); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names_with_safe_trigger'); + pg_class_relname | proparallel +------------------+------------- +(0 rows) + +select pg_get_table_max_parallel_dml_hazard('names_with_safe_trigger'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + s +(1 row) + +explain (costs off) insert into names_with_safe_trigger select * from names; + QUERY PLAN +---------------------------------------- + Insert on names_with_safe_trigger + -> Gather + Workers Planned: 3 + -> Parallel Seq Scan on names +(4 rows) + +insert into names_with_safe_trigger select * from names; +NOTICE: hello from insert_before_trigger_safe +-- +-- Test table with parallel-unsafe before stmt-level triggers +-- (should not create a parallel plan; triggers should fire) +-- +create table names_with_unsafe_trigger (like names); +create or replace function insert_before_trigger_unsafe() returns trigger as $$ + begin + raise notice 'hello from insert_before_trigger_unsafe'; + return new; + end; +$$ language plpgsql parallel unsafe; +create trigger insert_before_trigger_unsafe before insert on names_with_unsafe_trigger + for each statement execute procedure insert_before_trigger_unsafe(); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names_with_unsafe_trigger'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | u + pg_trigger | u +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('names_with_unsafe_trigger'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + u +(1 row) + +explain (costs off) insert into names_with_unsafe_trigger select * from names; + QUERY PLAN +------------------------------------- + Insert on names_with_unsafe_trigger + -> Seq Scan on names +(2 rows) + +insert into names_with_unsafe_trigger select * from names; +NOTICE: hello from insert_before_trigger_unsafe +-- +-- Test partition with parallel-unsafe trigger +-- (should not create a parallel plan) +-- +create table part_unsafe_trigger (a int4, b name) partition by range (a); +create table part_unsafe_trigger_1 partition of part_unsafe_trigger for values from (0) to (5000); +create table part_unsafe_trigger_2 partition of part_unsafe_trigger for values from (5000) to (10000); +create trigger part_insert_before_trigger_unsafe before insert on part_unsafe_trigger_1 + for each statement execute procedure insert_before_trigger_unsafe(); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('part_unsafe_trigger'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | u + pg_trigger | u +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('part_unsafe_trigger'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + u +(1 row) + +explain (costs off) insert into part_unsafe_trigger select unique1, stringu1 from tenk1; + QUERY PLAN +------------------------------- + Insert on part_unsafe_trigger + -> Seq Scan on tenk1 +(2 rows) + +-- +-- Test DOMAIN column with a CHECK constraint +-- +create function sql_is_distinct_from_u(anyelement, anyelement) +returns boolean language sql parallel unsafe +as 'select $1 is distinct from $2 limit 1'; +create domain inotnull_u int + check (sql_is_distinct_from_u(value, null)); +create table dom_table_u (x inotnull_u, y int); +-- Test DOMAIN column with parallel-unsafe CHECK constraint +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('dom_table_u'); + pg_class_relname | proparallel +------------------+------------- + pg_proc | u + pg_constraint | u +(2 rows) + +select pg_get_table_max_parallel_dml_hazard('dom_table_u'); + pg_get_table_max_parallel_dml_hazard +-------------------------------------- + u +(1 row) + +explain (costs off) insert into dom_table_u select unique1, unique2 from tenk1; + QUERY PLAN +------------------------- + Insert on dom_table_u + -> Seq Scan on tenk1 +(2 rows) + +rollback; +-- +-- Clean up anything not created in the transaction +-- +drop table names; +drop index names2_fullname_idx; +drop table names2; +drop index names4_fullname_idx; +drop table names4; +drop table testdef; +drop table test_data; +drop function bdefault_unsafe; +drop function cdefault_restricted; +drop function ddefault_safe; +drop function fullname_parallel_unsafe; +drop function fullname_parallel_restricted; diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 1b2f6bc418..760abca4e8 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -2818,6 +2818,7 @@ CREATE MATERIALIZED VIEW mat_view_heap_psql USING heap_psql AS SELECT f1 from tb --------+----------------+-----------+----------+---------+----------+--------------+------------- f1 | integer | | | | plain | | f2 | character(100) | | | | extended | | +Parallel DML: default \d+ tbl_heap Table "tableam_display.tbl_heap" @@ -2825,6 +2826,7 @@ CREATE MATERIALIZED VIEW mat_view_heap_psql USING heap_psql AS SELECT f1 from tb --------+----------------+-----------+----------+---------+----------+--------------+------------- f1 | integer | | | | plain | | f2 | character(100) | | | | extended | | +Parallel DML: default \set HIDE_TABLEAM off \d+ tbl_heap_psql @@ -2834,6 +2836,7 @@ CREATE MATERIALIZED VIEW mat_view_heap_psql USING heap_psql AS SELECT f1 from tb f1 | integer | | | | plain | | f2 | character(100) | | | | extended | | Access method: heap_psql +Parallel DML: default \d+ tbl_heap Table "tableam_display.tbl_heap" @@ -2842,50 +2845,51 @@ Access method: heap_psql f1 | integer | | | | plain | | f2 | character(100) | | | | extended | | Access method: heap +Parallel DML: default -- AM is displayed for tables, indexes and materialized views. \d+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | 0 bytes | - tableam_display | tbl_heap | table | regress_display_role | permanent | heap | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | 0 bytes | - tableam_display | view_heap_psql | view | regress_display_role | permanent | | 0 bytes | + List of relations + Schema | Name | Type | Owner | Persistence | Access method | Parallel DML | Size | Description +-----------------+--------------------+-------------------+----------------------+-------------+---------------+--------------+---------+------------- + tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | default | 0 bytes | + tableam_display | tbl_heap | table | regress_display_role | permanent | heap | default | 0 bytes | + tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | default | 0 bytes | + tableam_display | view_heap_psql | view | regress_display_role | permanent | | default | 0 bytes | (4 rows) \dt+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+---------------+-------+----------------------+-------------+---------------+---------+------------- - tableam_display | tbl_heap | table | regress_display_role | permanent | heap | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | 0 bytes | + List of relations + Schema | Name | Type | Owner | Persistence | Access method | Parallel DML | Size | Description +-----------------+---------------+-------+----------------------+-------------+---------------+--------------+---------+------------- + tableam_display | tbl_heap | table | regress_display_role | permanent | heap | default | 0 bytes | + tableam_display | tbl_heap_psql | table | regress_display_role | permanent | heap_psql | default | 0 bytes | (2 rows) \dm+ - List of relations - Schema | Name | Type | Owner | Persistence | Access method | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | 0 bytes | + List of relations + Schema | Name | Type | Owner | Persistence | Access method | Parallel DML | Size | Description +-----------------+--------------------+-------------------+----------------------+-------------+---------------+--------------+---------+------------- + tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | default | 0 bytes | (1 row) -- But not for views and sequences. \dv+ - List of relations - Schema | Name | Type | Owner | Persistence | Size | Description ------------------+----------------+------+----------------------+-------------+---------+------------- - tableam_display | view_heap_psql | view | regress_display_role | permanent | 0 bytes | + List of relations + Schema | Name | Type | Owner | Persistence | Parallel DML | Size | Description +-----------------+----------------+------+----------------------+-------------+--------------+---------+------------- + tableam_display | view_heap_psql | view | regress_display_role | permanent | default | 0 bytes | (1 row) \set HIDE_TABLEAM on \d+ - List of relations - Schema | Name | Type | Owner | Persistence | Size | Description ------------------+--------------------+-------------------+----------------------+-------------+---------+------------- - tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | 0 bytes | - tableam_display | tbl_heap | table | regress_display_role | permanent | 0 bytes | - tableam_display | tbl_heap_psql | table | regress_display_role | permanent | 0 bytes | - tableam_display | view_heap_psql | view | regress_display_role | permanent | 0 bytes | + List of relations + Schema | Name | Type | Owner | Persistence | Parallel DML | Size | Description +-----------------+--------------------+-------------------+----------------------+-------------+--------------+---------+------------- + tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | default | 0 bytes | + tableam_display | tbl_heap | table | regress_display_role | permanent | default | 0 bytes | + tableam_display | tbl_heap_psql | table | regress_display_role | permanent | default | 0 bytes | + tableam_display | view_heap_psql | view | regress_display_role | permanent | default | 0 bytes | (4 rows) RESET ROLE; diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 4a5ef0bc24..ffb498dc88 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -85,6 +85,7 @@ Indexes: "testpub_tbl2_pkey" PRIMARY KEY, btree (id) Publications: "testpub_foralltables" +Parallel DML: default \dRp+ testpub_foralltables Publication testpub_foralltables @@ -198,6 +199,7 @@ Publications: "testpib_ins_trunct" "testpub_default" "testpub_fortbl" +Parallel DML: default \d+ testpub_tbl1 Table "public.testpub_tbl1" @@ -211,6 +213,7 @@ Publications: "testpib_ins_trunct" "testpub_default" "testpub_fortbl" +Parallel DML: default \dRp+ testpub_default Publication testpub_default @@ -236,6 +239,7 @@ Indexes: Publications: "testpib_ins_trunct" "testpub_fortbl" +Parallel DML: default -- permissions SET ROLE regress_publication_user2; diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 79002197a7..482fe4d8c4 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -171,6 +171,7 @@ Indexes: "test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE "test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb) Replica Identity: FULL +Parallel DML: default ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 89397e41f0..26ab706515 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -958,6 +958,7 @@ Policies: Partitions: part_document_fiction FOR VALUES FROM (11) TO (12), part_document_nonfiction FOR VALUES FROM (99) TO (100), part_document_satire FOR VALUES FROM (55) TO (56) +Parallel DML: default SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname; schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2fa00a3c29..ea8a737c1c 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -3180,6 +3180,7 @@ Rules: r3 AS ON DELETE TO rules_src DO NOTIFY rules_src_deletion +Parallel DML: default -- -- Ensure an aliased target relation for insert is correctly deparsed. @@ -3208,6 +3209,7 @@ Rules: r5 AS ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text WHERE trgt.f1 = new.f1 +Parallel DML: default -- -- Also check multiassignment deparsing. @@ -3231,6 +3233,7 @@ Rules: WHERE trgt.f1 = new.f1 RETURNING new.f1, new.f2 +Parallel DML: default drop table rule_t1, rule_dest; -- diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index a7f12e989d..06c1d25326 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -156,6 +156,7 @@ ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1; b | integer | | | | plain | | Statistics objects: "public.ab1_a_b_stats" ON a, b FROM ab1 +Parallel DML: default -- partial analyze doesn't build stats either ANALYZE ab1 (a); diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5d124cf96f..13e0547302 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -3483,6 +3483,7 @@ alter trigger parenttrig on parent rename to anothertrig; Triggers: parenttrig AFTER INSERT ON child FOR EACH ROW EXECUTE FUNCTION f() Inherits: parent +Parallel DML: default drop table parent, child; drop function f(); diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index c809f88f54..3b981ae2aa 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -753,6 +753,7 @@ create table part_def partition of range_parted default; e | character varying | | | | extended | | Partition of: range_parted DEFAULT Partition constraint: (NOT ((a IS NOT NULL) AND (b IS NOT NULL) AND (((a = 'a'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'a'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'b'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '20'::bigint) AND (b < '30'::bigint))))) +Parallel DML: default insert into range_parted values ('c', 9); -- ok diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source index 1bbe7e0323..8d17677072 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -339,6 +339,7 @@ Indexes: "part_a_idx" btree (a), tablespace "regress_tblspace" Partitions: testschema.part1 FOR VALUES IN (1), testschema.part2 FOR VALUES IN (2) +Parallel DML: default \d testschema.part1 Table "testschema.part1" @@ -358,6 +359,7 @@ Partition of: testschema.part FOR VALUES IN (1) Partition constraint: ((a IS NOT NULL) AND (a = 1)) Indexes: "part1_a_idx" btree (a), tablespace "regress_tblspace" +Parallel DML: default \d testschema.part_a_idx Partitioned index "testschema.part_a_idx" diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 7be89178f0..daf0bad4d5 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -96,6 +96,7 @@ test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 # run by itself so it can run parallel workers test: select_parallel test: write_parallel +test: insert_parallel # no relation related tests can be put in this group test: publication subscription diff --git a/src/test/regress/sql/insert_parallel.sql b/src/test/regress/sql/insert_parallel.sql new file mode 100644 index 0000000000..65ab8b79d0 --- /dev/null +++ b/src/test/regress/sql/insert_parallel.sql @@ -0,0 +1,381 @@ +-- +-- PARALLEL +-- + +-- +-- START: setup some tables and data needed by the tests. +-- + +-- Setup - index expressions test + +create function pg_class_relname(Oid) +returns name language sql parallel unsafe +as 'select relname from pg_class where $1 = oid'; + +-- For testing purposes, we'll mark this function as parallel-unsafe +create or replace function fullname_parallel_unsafe(f text, l text) returns text as $$ + begin + return f || l; + end; +$$ language plpgsql immutable parallel unsafe; + +create or replace function fullname_parallel_restricted(f text, l text) returns text as $$ + begin + return f || l; + end; +$$ language plpgsql immutable parallel restricted; + +create table names(index int, first_name text, last_name text); +create table names2(index int, first_name text, last_name text); +create index names2_fullname_idx on names2 (fullname_parallel_unsafe(first_name, last_name)); +create table names4(index int, first_name text, last_name text); +create index names4_fullname_idx on names4 (fullname_parallel_restricted(first_name, last_name)); + + +insert into names values + (1, 'albert', 'einstein'), + (2, 'niels', 'bohr'), + (3, 'erwin', 'schrodinger'), + (4, 'leonhard', 'euler'), + (5, 'stephen', 'hawking'), + (6, 'isaac', 'newton'), + (7, 'alan', 'turing'), + (8, 'richard', 'feynman'); + +-- Setup - column default tests + +create or replace function bdefault_unsafe () +returns int language plpgsql parallel unsafe as $$ +begin + RETURN 5; +end $$; + +create or replace function cdefault_restricted () +returns int language plpgsql parallel restricted as $$ +begin + RETURN 10; +end $$; + +create or replace function ddefault_safe () +returns int language plpgsql parallel safe as $$ +begin + RETURN 20; +end $$; + +create table testdef(a int, b int default bdefault_unsafe(), c int default cdefault_restricted(), d int default ddefault_safe()); +create table test_data(a int); +insert into test_data select * from generate_series(1,10); + +-- +-- END: setup some tables and data needed by the tests. +-- + +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; + +create table para_insert_p1 ( + unique1 int4 PRIMARY KEY, + stringu1 name +); + +create table para_insert_f1 ( + unique1 int4 REFERENCES para_insert_p1(unique1), + stringu1 name +); + +create table para_insert_with_parallel_unsafe( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml unsafe; + +create table para_insert_with_parallel_restricted( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml restricted; + +create table para_insert_with_parallel_safe( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml safe; + +create table para_insert_with_parallel_auto( + unique1 int4 PRIMARY KEY, + stringu1 name +) parallel dml default; + +-- Check FK trigger +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('para_insert_f1'); +select pg_get_table_max_parallel_dml_hazard('para_insert_f1'); + +-- +-- Test INSERT with underlying query. +-- Set parallel dml safe. +-- (should create plan with parallel SELECT, Gather parent node) +-- +alter table para_insert_p1 parallel dml safe; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; +insert into para_insert_p1 select unique1, stringu1 from tenk1; +-- select some values to verify that the parallel insert worked +select count(*), sum(unique1) from para_insert_p1; +-- verify that the same transaction has been used by all parallel workers +select count(*) from (select distinct cmin,xmin from para_insert_p1) as dt; +explain (costs off) insert into para_insert_with_parallel_safe select unique1, stringu1 from tenk1; + +-- +-- Set parallel dml unsafe. +-- (should not create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml unsafe; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; +explain (costs off) insert into para_insert_with_parallel_unsafe select unique1, stringu1 from tenk1; + +-- +-- Set parallel dml restricted. +-- (should create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml restricted; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; +explain (costs off) insert into para_insert_with_parallel_restricted select unique1, stringu1 from tenk1; + +-- +-- Reset parallel dml. +-- (should create plan with parallel SELECT) +-- +alter table para_insert_p1 parallel dml default; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; +explain (costs off) insert into para_insert_with_parallel_auto select unique1, stringu1 from tenk1; + +-- +-- Test INSERT with ordered underlying query. +-- (should create plan with parallel SELECT, GatherMerge parent node) +-- +truncate para_insert_p1 cascade; +explain (costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 order by unique1; +insert into para_insert_p1 select unique1, stringu1 from tenk1 order by unique1; +-- select some values to verify that the parallel insert worked +select count(*), sum(unique1) from para_insert_p1; +-- verify that the same transaction has been used by all parallel workers +select count(*) from (select distinct cmin,xmin from para_insert_p1) as dt; + +-- +-- Test INSERT with RETURNING clause. +-- (should create plan with parallel SELECT, Gather parent node) +-- +create table test_data1(like test_data); +explain (costs off) insert into test_data1 select * from test_data where a = 10 returning a as data; +insert into test_data1 select * from test_data where a = 10 returning a as data; + +-- +-- Test INSERT into a table with a foreign key. +-- (Insert into a table with a foreign key is parallel-restricted, +-- as doing this in a parallel worker would create a new commandId +-- and within a worker this is not currently supported) +-- +explain (costs off) insert into para_insert_f1 select unique1, stringu1 from tenk1; +insert into para_insert_f1 select unique1, stringu1 from tenk1; +-- select some values to verify that the insert worked +select count(*), sum(unique1) from para_insert_f1; + +-- +-- Test INSERT with ON CONFLICT ... DO UPDATE ... +-- (should not create a parallel plan) +-- +create table test_conflict_table(id serial primary key, somedata int); +explain (costs off) insert into test_conflict_table(id, somedata) select a, a from test_data; +insert into test_conflict_table(id, somedata) select a, a from test_data; +explain (costs off) insert into test_conflict_table(id, somedata) select a, a from test_data ON CONFLICT(id) DO UPDATE SET somedata = EXCLUDED.somedata + 1; + +-- +-- Test INSERT with parallel-unsafe index expression +-- (should not create a parallel plan) +-- +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names2'); +select pg_get_table_max_parallel_dml_hazard('names2'); +explain (costs off) insert into names2 select * from names; + +-- +-- Test INSERT with parallel-restricted index expression +-- (should create a parallel plan) +-- +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names4'); +select pg_get_table_max_parallel_dml_hazard('names4'); +explain (costs off) insert into names4 select * from names; + +-- +-- Test INSERT with underlying query - and RETURNING (no projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names5 (like names); +explain (costs off) insert into names5 select * from names returning *; + +-- +-- Test INSERT with underlying ordered query - and RETURNING (no projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names6 (like names); +explain (costs off) insert into names6 select * from names order by last_name returning *; +insert into names6 select * from names order by last_name returning *; + +-- +-- Test INSERT with underlying ordered query - and RETURNING (with projection) +-- (should create a parallel plan; parallel SELECT) +-- +create table names7 (like names); +explain (costs off) insert into names7 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; +insert into names7 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; + + +-- +-- Test INSERT into temporary table with underlying query. +-- (Insert into a temp table is parallel-restricted; +-- should create a parallel plan; parallel SELECT) +-- +create temporary table temp_names (like names); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('temp_names'); +select pg_get_table_max_parallel_dml_hazard('temp_names'); +explain (costs off) insert into temp_names select * from names; +insert into temp_names select * from names; + +-- +-- Test INSERT with column defaults +-- +-- + +-- +-- Parallel INSERT with unsafe column default, should not use a parallel plan +-- +explain (costs off) insert into testdef(a,c,d) select a,a*4,a*8 from test_data; + +-- +-- Parallel INSERT with restricted column default, should use parallel SELECT +-- +explain (costs off) insert into testdef(a,b,d) select a,a*2,a*8 from test_data; +insert into testdef(a,b,d) select a,a*2,a*8 from test_data; +select * from testdef order by a; +truncate testdef; + +-- +-- Parallel INSERT with restricted and unsafe column defaults, should not use a parallel plan +-- +explain (costs off) insert into testdef(a,d) select a,a*8 from test_data; + +-- +-- Test INSERT into partition with underlying query. +-- +create table parttable1 (a int, b name) partition by range (a); +create table parttable1_1 partition of parttable1 for values from (0) to (5000); +create table parttable1_2 partition of parttable1 for values from (5000) to (10000); + +alter table parttable1 parallel dml safe; + +explain (costs off) insert into parttable1 select unique1,stringu1 from tenk1; +insert into parttable1 select unique1,stringu1 from tenk1; +select count(*) from parttable1_1; +select count(*) from parttable1_2; + +-- +-- Test table with parallel-unsafe check constraint +-- +create or replace function check_b_unsafe(b name) returns boolean as $$ + begin + return (b <> 'XXXXXX'); + end; +$$ language plpgsql parallel unsafe; + +create table table_check_b(a int4, b name check (check_b_unsafe(b)), c name); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('table_check_b'); +select pg_get_table_max_parallel_dml_hazard('table_check_b'); +explain (costs off) insert into table_check_b(a,b,c) select unique1, unique2, stringu1 from tenk1; + +-- +-- Test table with parallel-safe before stmt-level triggers +-- (should create a parallel SELECT plan; triggers should fire) +-- +create table names_with_safe_trigger (like names); + +create or replace function insert_before_trigger_safe() returns trigger as $$ + begin + raise notice 'hello from insert_before_trigger_safe'; + return new; + end; +$$ language plpgsql parallel safe; +create trigger insert_before_trigger_safe before insert on names_with_safe_trigger + for each statement execute procedure insert_before_trigger_safe(); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names_with_safe_trigger'); +select pg_get_table_max_parallel_dml_hazard('names_with_safe_trigger'); +explain (costs off) insert into names_with_safe_trigger select * from names; +insert into names_with_safe_trigger select * from names; + +-- +-- Test table with parallel-unsafe before stmt-level triggers +-- (should not create a parallel plan; triggers should fire) +-- +create table names_with_unsafe_trigger (like names); +create or replace function insert_before_trigger_unsafe() returns trigger as $$ + begin + raise notice 'hello from insert_before_trigger_unsafe'; + return new; + end; +$$ language plpgsql parallel unsafe; +create trigger insert_before_trigger_unsafe before insert on names_with_unsafe_trigger + for each statement execute procedure insert_before_trigger_unsafe(); +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names_with_unsafe_trigger'); +select pg_get_table_max_parallel_dml_hazard('names_with_unsafe_trigger'); +explain (costs off) insert into names_with_unsafe_trigger select * from names; +insert into names_with_unsafe_trigger select * from names; + +-- +-- Test partition with parallel-unsafe trigger +-- (should not create a parallel plan) +-- +create table part_unsafe_trigger (a int4, b name) partition by range (a); +create table part_unsafe_trigger_1 partition of part_unsafe_trigger for values from (0) to (5000); +create table part_unsafe_trigger_2 partition of part_unsafe_trigger for values from (5000) to (10000); +create trigger part_insert_before_trigger_unsafe before insert on part_unsafe_trigger_1 + for each statement execute procedure insert_before_trigger_unsafe(); + +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('part_unsafe_trigger'); +select pg_get_table_max_parallel_dml_hazard('part_unsafe_trigger'); +explain (costs off) insert into part_unsafe_trigger select unique1, stringu1 from tenk1; + +-- +-- Test DOMAIN column with a CHECK constraint +-- +create function sql_is_distinct_from_u(anyelement, anyelement) +returns boolean language sql parallel unsafe +as 'select $1 is distinct from $2 limit 1'; + +create domain inotnull_u int + check (sql_is_distinct_from_u(value, null)); + +create table dom_table_u (x inotnull_u, y int); + +-- Test DOMAIN column with parallel-unsafe CHECK constraint +select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('dom_table_u'); +select pg_get_table_max_parallel_dml_hazard('dom_table_u'); +explain (costs off) insert into dom_table_u select unique1, unique2 from tenk1; + +rollback; + +-- +-- Clean up anything not created in the transaction +-- + +drop table names; +drop index names2_fullname_idx; +drop table names2; +drop index names4_fullname_idx; +drop table names4; +drop table testdef; +drop table test_data; + +drop function bdefault_unsafe; +drop function cdefault_restricted; +drop function ddefault_safe; +drop function fullname_parallel_unsafe; +drop function fullname_parallel_restricted; -- 2.18.4