From 2cc7fc53834dd49bd2b631ba528d5f4ec7363004 Mon Sep 17 00:00:00 2001 From: Greg Nancarrow Date: Fri, 20 Nov 2020 17:46:01 +1100 Subject: [PATCH v9 4/4] Parallel INSERT and/or SELECT for "INSERT INTO ... SELECT ..." - tests and documentation updates. --- doc/src/sgml/parallel.sgml | 83 ++++++++++++++++-- src/test/regress/expected/insert_parallel.out | 120 +++++++++++++------------- src/test/regress/sql/insert_parallel.sql | 20 ++--- 3 files changed, 145 insertions(+), 78 deletions(-) diff --git a/doc/src/sgml/parallel.sgml b/doc/src/sgml/parallel.sgml index 938d51a..3cc029c 100644 --- a/doc/src/sgml/parallel.sgml +++ b/doc/src/sgml/parallel.sgml @@ -141,14 +141,16 @@ EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; - The query writes any data or locks any database rows. If a query - contains a data-modifying operation either at the top level or within - a CTE, no parallel plans for that query will be generated. As an - exception, the commands CREATE TABLE ... AS, SELECT - INTO, and CREATE MATERIALIZED VIEW which create a new - table and populate it can use a parallel plan. Another exeption is the command - INSERT INTO ... SELECT ... which can use a parallel plan for - the underlying SELECT part of the query. + The query locks any database rows, or writes data within a CTE or using + a parallel-unsupported data-modifying operation. Currently, the only + parallel-supported data-modifying operations are + INSERT INTO ... SELECT ..., and the table creation + and population commands CREATE TABLE ... AS, + SELECT INTO, and + CREATE MATERIALIZED VIEW. If a query contains a + parallel-unsupported data-modifying operation at the top level, or any + data-modifying operation within a CTE, no parallel plans for that query + will be generated. @@ -426,6 +428,71 @@ EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; + + Parallel Insert + + + When an INSERT statement uses an underlying + SELECT query to supply the rows to be inserted, a + parallel query plan may be generated for which the work of both data + retrieval and data insertion is divided amongst the workers. In this + case, each worker handles insertion of its portion of the rows retrieved + by the underlying SELECT query. + + + + Parallel INSERT is not supported in all situations. The + operations invoked by the INSERT statement must be + parallel-safe, including those that are invoked as a result of certain + features of the target table. + + + + For example, any of the following will prevent the use of parallel + INSERT in the query plan: + + + + + + A parallel query plan can't be generated for the underlying + SELECT, because, for example, the + SELECT statement uses a parallel-unsafe function. + + + + + The INSERT statement uses an ON CONFLICT DO UPDATE clause. + + + + + The target table is a foreign or temporary table. + + + + + The target table has a foreign key, or has a parallel-unsafe trigger, + index expression, column default expression or check constraint. + + + + + The target table is a partitioned table with a parallel-unsafe partition + key expression or support function. + + + + + + Where the above target table features are determined to be, at worst, + parallel-restricted, rather than parallel-unsafe, at least a parallel table + scan may be used in the query plan for the INSERT + statement. For more information about Parallel Safety, see + . + + + Parallel Plan Tips diff --git a/src/test/regress/expected/insert_parallel.out b/src/test/regress/expected/insert_parallel.out index 5f0a89f..5755fc1 100644 --- a/src/test/regress/expected/insert_parallel.out +++ b/src/test/regress/expected/insert_parallel.out @@ -70,14 +70,14 @@ create table para_insert_f1 ( ); -- -- Test INSERT with underlying query. --- (should create plan with parallel SELECT, Gather parent node) +-- (should create plan with parallel INSERT+SELECT, Gather parent node) -- explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; QUERY PLAN ---------------------------------------- - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Seq Scan on tenk1 (4 rows) @@ -105,7 +105,7 @@ select * from para_insert_p1 where unique1 >= 9990 order by unique1; -- -- Test INSERT with ordered underlying query. --- (should create plan with parallel SELECT, GatherMerge parent node) +-- (should create plan with INSERT + parallel SELECT, GatherMerge parent node) -- truncate para_insert_p1 cascade; NOTICE: truncate cascades to table "para_insert_f1" @@ -190,9 +190,9 @@ NOTICE: truncate cascades to table "para_insert_f1" explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 where unique1 <= 2500; QUERY PLAN ----------------------------------------- - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Seq Scan on tenk1 Filter: (unique1 <= 2500) (5 rows) @@ -229,9 +229,9 @@ NOTICE: truncate cascades to table "para_insert_f1" explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 where unique1 <= 2500; QUERY PLAN ----------------------------------------- - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Seq Scan on tenk1 Filter: (unique1 <= 2500) (5 rows) @@ -271,9 +271,9 @@ 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 + Gather + Workers Planned: 3 + -> Insert on test_conflict_table -> Parallel Seq Scan on test_data (4 rows) @@ -321,9 +321,9 @@ NOTICE: truncate cascades to table "para_insert_f1" explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 where unique1 >= 7500; QUERY PLAN ------------------------------------------------------ - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Bitmap Heap Scan on tenk1 Recheck Cond: (unique1 >= 7500) -> Bitmap Index Scan on tenk1_unique1 @@ -356,9 +356,9 @@ create table a_star_data(aa int); explain (costs off) insert into a_star_data select aa from a_star where aa > 10; QUERY PLAN -------------------------------------------------------- - Insert on a_star_data - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on a_star_data -> Parallel Append -> Parallel Seq Scan on d_star a_star_4 Filter: (aa > 10) @@ -392,9 +392,9 @@ NOTICE: truncate cascades to table "para_insert_f1" explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1 where unique1 >= 500; QUERY PLAN -------------------------------------------------------------- - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Index Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 >= 500) (5 rows) @@ -430,9 +430,9 @@ NOTICE: truncate cascades to table "para_insert_f1" explain(costs off) insert into para_insert_p1 select unique1 from tenk1 where unique1 >= 500; QUERY PLAN ------------------------------------------------------------------- - Insert on para_insert_p1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on para_insert_p1 -> Parallel Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 >= 500) (5 rows) @@ -470,9 +470,9 @@ reset enable_bitmapscan; explain (costs off) insert into names3 select * from names; QUERY PLAN ---------------------------------------- - Insert on names3 - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on names3 -> Parallel Seq Scan on names (4 rows) @@ -517,21 +517,21 @@ select * from names2 order by fullname_parallel_unsafe(first_name, last_name); -- -- Test INSERT with underlying query - and RETURNING (no projection) --- (should create a parallel plan; parallel SELECT) +-- (should create a parallel plan; parallel INSERT+SELECT) -- create table names4 (like names); explain (costs off) insert into names4 select * from names returning *; QUERY PLAN ---------------------------------------- - Insert on names4 - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on names4 -> Parallel Seq Scan on names (4 rows) -- -- Test INSERT with underlying ordered query - and RETURNING (no projection) --- (should create a parallel plan; parallel SELECT) +-- (should create a parallel plan; INSERT + parallel SELECT) -- create table names5 (like names); explain (costs off) insert into names5 select * from names order by last_name returning *; @@ -560,7 +560,7 @@ insert into names5 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) +-- (should create a parallel plan; INSERT + parallel SELECT) -- create table names6 (like names); explain (costs off) insert into names6 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; @@ -610,14 +610,14 @@ insert into temp_names select * from names; -- d: safe default -- -- --- No column defaults, should use parallel SELECT +-- No column defaults, should use parallel INSERT+SELECT -- explain (costs off) insert into testdef(a,b,c,d) select a,a*2,a*4,a*8 from test_data; QUERY PLAN -------------------------------------------- - Insert on testdef - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on testdef -> Parallel Seq Scan on test_data (4 rows) @@ -666,7 +666,7 @@ select * from testdef order by a; truncate testdef; -- --- Parallel restricted column default, should use parallel SELECT +-- Parallel restricted column default, should use INSERT + parallel SELECT -- explain (costs off) insert into testdef(a,b,d) select a,a*2,a*8 from test_data; QUERY PLAN @@ -695,14 +695,14 @@ select * from testdef order by a; truncate testdef; -- --- Parallel safe column default, should use parallel SELECT +-- Parallel safe column default, should use parallel INSERT+SELECT -- explain (costs off) insert into testdef(a,b,c) select a,a*2,a*4 from test_data; QUERY PLAN -------------------------------------------- - Insert on testdef - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on testdef -> Parallel Seq Scan on test_data (4 rows) @@ -759,9 +759,9 @@ create table parttable1_2 partition of parttable1 for values from (5000) to (100 explain (costs off) insert into parttable1 select unique1,stringu1 from tenk1; QUERY PLAN ---------------------------------------- - Insert on parttable1 - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on parttable1 -> Parallel Seq Scan on tenk1 (4 rows) @@ -811,9 +811,9 @@ create table table_check_a(a int4 check (check_a(a)), b name); explain (costs off) insert into table_check_a select unique1, stringu1 from tenk1; QUERY PLAN ---------------------------------------- - Insert on table_check_a - -> Gather - Workers Planned: 4 + Gather + Workers Planned: 4 + -> Insert on table_check_a -> Parallel Seq Scan on tenk1 (4 rows) @@ -850,7 +850,7 @@ select count(*), sum(a) from table_check_b; -- -- Test INSERT into table with before+after parallel-safe stmt-level triggers --- (should create a parallel SELECT plan; +-- (should create a parallel INSERT+SELECT plan; -- stmt-level before+after triggers should fire) -- create table names_with_safe_trigger (like names); @@ -871,11 +871,11 @@ create trigger insert_before_trigger_safe before insert on names_with_safe_trigg create trigger insert_after_trigger_safe after insert on names_with_safe_trigger for each statement execute procedure insert_after_trigger_safe(); explain (costs off) insert into names_with_safe_trigger select * from names; - QUERY PLAN ----------------------------------------- - Insert on names_with_safe_trigger - -> Gather - Workers Planned: 3 + QUERY PLAN +----------------------------------------- + Gather + Workers Planned: 3 + -> Insert on names_with_safe_trigger -> Parallel Seq Scan on names (4 rows) @@ -916,7 +916,7 @@ NOTICE: hello from insert_before_trigger_unsafe NOTICE: hello from insert_after_trigger_unsafe -- -- Test INSERT into table with before+after parallel-restricted stmt-level trigger --- (should create a parallel plan with parallel SELECT; +-- (should create a parallel plan with INSERT + parallel SELECT; -- stmt-level before+after triggers should fire) -- create table names_with_restricted_trigger (like names); @@ -957,9 +957,9 @@ insert into insert_toast_table_data select i, rpad('T', 16384, 'ABCDEFGH') from explain (costs off) insert into insert_toast_table select index, data from insert_toast_table_data; QUERY PLAN ---------------------------------------------------------- - Insert on insert_toast_table - -> Gather - Workers Planned: 3 + Gather + Workers Planned: 3 + -> Insert on insert_toast_table -> Parallel Seq Scan on insert_toast_table_data (4 rows) diff --git a/src/test/regress/sql/insert_parallel.sql b/src/test/regress/sql/insert_parallel.sql index 9447120..ab231cb 100644 --- a/src/test/regress/sql/insert_parallel.sql +++ b/src/test/regress/sql/insert_parallel.sql @@ -89,7 +89,7 @@ create table para_insert_f1 ( -- -- Test INSERT with underlying query. --- (should create plan with parallel SELECT, Gather parent node) +-- (should create plan with parallel INSERT+SELECT, Gather parent node) -- explain(costs off) insert into para_insert_p1 select unique1, stringu1 from tenk1; insert into para_insert_p1 select unique1, stringu1 from tenk1; @@ -98,7 +98,7 @@ select * from para_insert_p1 where unique1 >= 9990 order by unique1; -- -- Test INSERT with ordered underlying query. --- (should create plan with parallel SELECT, GatherMerge parent node) +-- (should create plan with INSERT + 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; @@ -229,14 +229,14 @@ select * from names2 order by fullname_parallel_unsafe(first_name, last_name); -- -- Test INSERT with underlying query - and RETURNING (no projection) --- (should create a parallel plan; parallel SELECT) +-- (should create a parallel plan; parallel INSERT+SELECT) -- create table names4 (like names); explain (costs off) insert into names4 select * from names returning *; -- -- Test INSERT with underlying ordered query - and RETURNING (no projection) --- (should create a parallel plan; parallel SELECT) +-- (should create a parallel plan; INSERT + parallel SELECT) -- create table names5 (like names); explain (costs off) insert into names5 select * from names order by last_name returning *; @@ -244,7 +244,7 @@ insert into names5 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) +-- (should create a parallel plan; INSERT + parallel SELECT) -- create table names6 (like names); explain (costs off) insert into names6 select * from names order by last_name returning last_name || ', ' || first_name as last_name_then_first_name; @@ -269,7 +269,7 @@ insert into temp_names select * from names; -- -- --- No column defaults, should use parallel SELECT +-- No column defaults, should use parallel INSERT+SELECT -- explain (costs off) insert into testdef(a,b,c,d) select a,a*2,a*4,a*8 from test_data; insert into testdef(a,b,c,d) select a,a*2,a*4,a*8 from test_data; @@ -285,7 +285,7 @@ select * from testdef order by a; truncate testdef; -- --- Parallel restricted column default, should use parallel SELECT +-- Parallel restricted column default, should use INSERT + 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; @@ -293,7 +293,7 @@ select * from testdef order by a; truncate testdef; -- --- Parallel safe column default, should use parallel SELECT +-- Parallel safe column default, should use parallel INSERT+SELECT -- explain (costs off) insert into testdef(a,b,c) select a,a*2,a*4 from test_data; insert into testdef(a,b,c) select a,a*2,a*4 from test_data; @@ -370,7 +370,7 @@ select count(*), sum(a) from table_check_b; -- -- Test INSERT into table with before+after parallel-safe stmt-level triggers --- (should create a parallel SELECT plan; +-- (should create a parallel INSERT+SELECT plan; -- stmt-level before+after triggers should fire) -- create table names_with_safe_trigger (like names); @@ -420,7 +420,7 @@ insert into names_with_unsafe_trigger select * from names; -- -- Test INSERT into table with before+after parallel-restricted stmt-level trigger --- (should create a parallel plan with parallel SELECT; +-- (should create a parallel plan with INSERT + parallel SELECT; -- stmt-level before+after triggers should fire) -- create table names_with_restricted_trigger (like names); -- 1.8.3.1