From 61641393e4c1d90a47f2a070d6e9e020e6f014e4 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddy@enterprisedb.com>
Date: Mon, 28 Dec 2020 16:49:43 +0530
Subject: [PATCH v19 3/4] Tests And Docs For Parallel Inserts in CTAS

---
 doc/src/sgml/ref/create_table_as.sgml        |  31 +-
 src/test/regress/expected/write_parallel.out | 559 +++++++++++++++++++
 src/test/regress/sql/write_parallel.sql      | 211 +++++++
 3 files changed, 796 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml
index 07558ab56c..35903701ed 100644
--- a/doc/src/sgml/ref/create_table_as.sgml
+++ b/doc/src/sgml/ref/create_table_as.sgml
@@ -37,11 +37,13 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
 
   <para>
    <command>CREATE TABLE AS</command> creates a table and fills it
-   with data computed by a <command>SELECT</command> command.
-   The table columns have the
-   names and data types associated with the output columns of the
-   <command>SELECT</command> (except that you can override the column
-   names by giving an explicit list of new column names).
+   with data computed by a <command>SELECT</command> command. When the
+   node at the top of the <command>SELECT</command> plan is
+   <literal>Gather</literal> and there are no projections to be performed
+   by it, then the created table can be filled by the workers in parallel.
+   The table columns have the names and data types associated with the output
+   columns of the <command>SELECT</command> (except that you can override the
+   column names by giving an explicit list of new column names).
   </para>
 
   <para>
@@ -297,6 +299,25 @@ PREPARE recentfilms(date) AS
 CREATE TEMP TABLE films_recent ON COMMIT DROP AS
   EXECUTE recentfilms('2002-01-01');
 </programlisting></para>
+
+  <para>
+   Here is an example of a query plan when the data into the created table can
+   be filled by the workers in parallel:
+
+<programlisting>
+EXPLAIN CREATE TABLE bar AS SELECT * FROM foo WHERE i > 5;
+
+                             QUERY PLAN
+-------------------------------------------------------------------&zwsp;--
+Gather  (cost=0.00..23.28 rows=850 width=4)
+   Workers Planned: 2
+ ->  Create bar
+   ->  Parallel Seq Scan on foo  (cost=0.00..23.28 rows=354 width=4)
+         Filter: (i > 5)
+(5 rows)
+</programlisting>
+  </para>
+
  </refsect1>
 
  <refsect1>
diff --git a/src/test/regress/expected/write_parallel.out b/src/test/regress/expected/write_parallel.out
index 0c4da2591a..38a18c5a9b 100644
--- a/src/test/regress/expected/write_parallel.out
+++ b/src/test/regress/expected/write_parallel.out
@@ -76,4 +76,563 @@ explain (costs off) create table parallel_write as execute prep_stmt;
 
 create table parallel_write as execute prep_stmt;
 drop table parallel_write;
+--
+-- Test parallel inserts in create table as/select into/create materialized
+-- view.
+--
+-- Parallel queries won't necessarily get as many workers as the planner
+-- asked for.  This affects not only the "Workers Launched:" field of EXPLAIN
+-- results, but also row counts and loop counts for parallel scans, Gathers,
+-- and everything in between.  This function filters out the values we can't
+-- rely on to be stable.
+-- This removes enough info that you might wonder why bother with EXPLAIN
+-- ANALYZE at all.  The answer is that we need to see whether the parallel
+-- inserts are being done by the workers, the only way is that
+-- Create <<tbl_name>> appears in the explain output.
+create function explain_pictas(text) returns setof text
+language plpgsql as
+$$
+declare
+    ln text;
+begin
+    for ln in
+        execute format('explain (analyze, costs off, summary off, timing off) %s',
+            $1)
+    loop
+        ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N');
+        ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N loops=N');
+        ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed by Filter: N');
+        return next ln;
+    end loop;
+end;
+$$;
+-- parallel inserts must occur as the CTAS creates a normal table
+select explain_pictas(
+'create table parallel_write as select length(stringu1) from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur as the table is temporary
+select explain_pictas(
+'create temporary table parallel_write as select length(stringu1) from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(4 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must occur as the CTAS creates an unlogged table
+select explain_pictas(
+'create unlogged table parallel_write as select length(stringu1) from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must occur as the select into creates a normal table
+select explain_pictas(
+'select length(stringu1) into parallel_write from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur as the table is temporary
+select explain_pictas(
+'select length(stringu1) into temporary parallel_write from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(4 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must occur as the select into creates an unlogged table
+select explain_pictas(
+'select length(stringu1) into unlogged parallel_write from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur as the parallelism will not be picked
+-- for select part because of for update clause
+select explain_pictas(
+'create table parallel_write as select length(stringu1) from tenk1 for update;');
+                 explain_pictas                  
+-------------------------------------------------
+ LockRows (actual rows=N loops=N)
+   ->  Seq Scan on tenk1 (actual rows=N loops=N)
+(2 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must occur as the materialized view is being created here
+select explain_pictas(
+'create materialized view parallel_mat_view as
+    select length(stringu1) from tenk1;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_mat_view
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_mat_view;
+ count 
+-------
+ 10000
+(1 row)
+
+drop materialized view parallel_mat_view;
+-- parallel inserts must occur as the CTAS creates the table using prepared
+-- statement for which parallelism would have been picked
+prepare parallel_write_prep as select length(stringu1) from tenk1;
+select explain_pictas(
+'create table parallel_write as execute parallel_write_prep;');
+                      explain_pictas                      
+----------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+deallocate parallel_write_prep;
+drop table parallel_write;
+-- parallel inserts must not occur as the parallelism will not be picked
+-- for select part because of the parallel unsafe function
+create sequence parallel_write_sequence;
+select explain_pictas(
+E'create table parallel_write as
+    select nextval(\'parallel_write_sequence\'), four from tenk1;');
+              explain_pictas               
+-------------------------------------------
+ Seq Scan on tenk1 (actual rows=N loops=N)
+(1 row)
+
+select count(*) from parallel_write;
+ count 
+-------
+ 10000
+(1 row)
+
+drop table parallel_write;
+drop sequence parallel_write_sequence;
+-- parallel inserts must occur, as there is init plan that gets executed by
+-- each parallel worker
+select explain_pictas(
+'create table parallel_write as select two col1,
+    (select two from (select * from tenk2) as tt limit 1) col2
+    from tenk1  where tenk1.four = 3;');
+                             explain_pictas                             
+------------------------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Params Evaluated: $1
+   Workers Launched: N
+ ->  Create parallel_write
+   InitPlan 1 (returns $1)
+     ->  Limit (actual rows=N loops=N)
+           ->  Gather (actual rows=N loops=N)
+                 Workers Planned: 4
+                 Workers Launched: N
+                 ->  Parallel Seq Scan on tenk2 (actual rows=N loops=N)
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+         Filter: (four = 3)
+         Rows Removed by Filter: N
+(14 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+  2500
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is sub plan that gets executed by
+-- the Gather node in leader
+select explain_pictas(
+'create table parallel_write as select two col1,
+    (select tenk1.two from generate_series(1,1)) col2
+    from tenk1  where tenk1.four = 3;');
+                          explain_pictas                          
+------------------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 4
+   Workers Launched: N
+   ->  Parallel Seq Scan on tenk1 (actual rows=N loops=N)
+         Filter: (four = 3)
+         Rows Removed by Filter: N
+   SubPlan 1
+     ->  Function Scan on generate_series (actual rows=N loops=N)
+(8 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+  2500
+(1 row)
+
+drop table parallel_write;
+create table temp1(col1) as select * from generate_series(1,5);
+create table temp2(col2) as select * from temp1;
+create table temp3(col3) as select * from temp1;
+-- parallel inserts must not occur, as there is a limit clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 limit 4;');
+                         explain_pictas                         
+----------------------------------------------------------------
+ Limit (actual rows=N loops=N)
+   ->  Gather (actual rows=N loops=N)
+         Workers Planned: 3
+         Workers Launched: N
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(5 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     4
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is an order by clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 order by 1;');
+                         explain_pictas                         
+----------------------------------------------------------------
+ Gather Merge (actual rows=N loops=N)
+   Workers Planned: 3
+   Workers Launched: N
+   ->  Sort (actual rows=N loops=N)
+         Sort Key: col1
+         Sort Method: quicksort  Memory: 25kB
+         Worker 0:  Sort Method: quicksort  Memory: 25kB
+         Worker 1:  Sort Method: quicksort  Memory: 25kB
+         Worker 2:  Sort Method: quicksort  Memory: 25kB
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(10 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is an order by clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 order by 1;');
+                         explain_pictas                         
+----------------------------------------------------------------
+ Gather Merge (actual rows=N loops=N)
+   Workers Planned: 3
+   Workers Launched: N
+   ->  Sort (actual rows=N loops=N)
+         Sort Key: col1
+         Sort Method: quicksort  Memory: 25kB
+         Worker 0:  Sort Method: quicksort  Memory: 25kB
+         Worker 1:  Sort Method: quicksort  Memory: 25kB
+         Worker 2:  Sort Method: quicksort  Memory: 25kB
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(10 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is a distinct clause
+select explain_pictas(
+'create table parallel_write as select distinct * from temp1;');
+                         explain_pictas                         
+----------------------------------------------------------------
+ HashAggregate (actual rows=N loops=N)
+   Group Key: col1
+   Batches: 1  Memory Usage: 40kB
+   ->  Gather (actual rows=N loops=N)
+         Workers Planned: 3
+         Workers Launched: N
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(7 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is an aggregate and group clause
+select explain_pictas(
+'create table parallel_write as select count(*) from temp1 group by col1;');
+                            explain_pictas                            
+----------------------------------------------------------------------
+ Finalize HashAggregate (actual rows=N loops=N)
+   Group Key: col1
+   Batches: 1  Memory Usage: 40kB
+   ->  Gather (actual rows=N loops=N)
+         Workers Planned: 3
+         Workers Launched: N
+         ->  Partial HashAggregate (actual rows=N loops=N)
+               Group Key: col1
+               Batches: 1  Memory Usage: 40kB
+               Worker 0:  Batches: 1  Memory Usage: 40kB
+               Worker 1:  Batches: 1  Memory Usage: 40kB
+               Worker 2:  Batches: 1  Memory Usage: 40kB
+               ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(13 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is an aggregate, group and having
+-- clauses
+select explain_pictas(
+'create table parallel_write as
+    select count(col1), (select col3 from
+        (select * from temp3) as tt limit 1) col4 from temp1, temp2
+    where temp1.col1 = temp2.col2 group by col4 having count(col1) > 0;');
+                                  explain_pictas                                  
+----------------------------------------------------------------------------------
+ Finalize GroupAggregate (actual rows=N loops=N)
+   Group Key: ($1)
+   Filter: (count(temp1.col1) > 0)
+   InitPlan 1 (returns $1)
+     ->  Limit (actual rows=N loops=N)
+           ->  Gather (actual rows=N loops=N)
+                 Workers Planned: 3
+                 Workers Launched: N
+                 ->  Parallel Seq Scan on temp3 (actual rows=N loops=N)
+   ->  Gather (actual rows=N loops=N)
+         Workers Planned: 3
+         Params Evaluated: $1
+         Workers Launched: N
+         ->  Partial GroupAggregate (actual rows=N loops=N)
+               Group Key: $1
+               ->  Parallel Hash Join (actual rows=N loops=N)
+                     Hash Cond: (temp1.col1 = temp2.col2)
+                     ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+                     ->  Parallel Hash (actual rows=N loops=N)
+                           Buckets: 4096  Batches: 1  Memory Usage: 64kB
+                           ->  Parallel Seq Scan on temp2 (actual rows=N loops=N)
+(21 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     1
+(1 row)
+
+drop table parallel_write;
+-- parallel inserts must not occur, as there is a window function
+select explain_pictas(
+'create table parallel_write as
+    select avg(col1) OVER (PARTITION BY col1) from temp1;');
+                            explain_pictas                            
+----------------------------------------------------------------------
+ WindowAgg (actual rows=N loops=N)
+   ->  Gather Merge (actual rows=N loops=N)
+         Workers Planned: 3
+         Workers Launched: N
+         ->  Sort (actual rows=N loops=N)
+               Sort Key: col1
+               Sort Method: quicksort  Memory: 25kB
+               Worker 0:  Sort Method: quicksort  Memory: 25kB
+               Worker 1:  Sort Method: quicksort  Memory: 25kB
+               Worker 2:  Sort Method: quicksort  Memory: 25kB
+               ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+(11 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- nested loop join is the top node under which Gather node exists, so parallel
+-- inserts must not occur
+set enable_nestloop to on;
+set enable_mergejoin to off;
+set enable_hashjoin to off;
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+                            explain_pictas                            
+----------------------------------------------------------------------
+ Nested Loop (actual rows=N loops=N)
+   Join Filter: (temp1.col1 = temp2.col2)
+   Rows Removed by Join Filter: 20
+   ->  Gather (actual rows=N loops=N)
+         Workers Planned: 3
+         Workers Launched: N
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+   ->  Materialize (actual rows=N loops=N)
+         ->  Gather (actual rows=N loops=N)
+               Workers Planned: 3
+               Workers Launched: N
+               ->  Parallel Seq Scan on temp2 (actual rows=N loops=N)
+(12 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- the top node is Gather under which merge join happens, so parallel inserts
+-- must occur
+set enable_nestloop to off;
+set enable_mergejoin to on;
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+                            explain_pictas                            
+----------------------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 3
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Merge Join (actual rows=N loops=N)
+         Merge Cond: (temp1.col1 = temp2.col2)
+         ->  Sort (actual rows=N loops=N)
+               Sort Key: temp1.col1
+               Sort Method: quicksort  Memory: 25kB
+               Worker 0:  Sort Method: quicksort  Memory: 25kB
+               Worker 1:  Sort Method: quicksort  Memory: 25kB
+               Worker 2:  Sort Method: quicksort  Memory: 25kB
+               ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+         ->  Sort (actual rows=N loops=N)
+               Sort Key: temp2.col2
+               Sort Method: quicksort  Memory: 25kB
+               ->  Seq Scan on temp2 (actual rows=N loops=N)
+(17 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+-- parallel hash join happens under Gather node, so parallel inserts must occur
+set enable_mergejoin to off;
+set enable_hashjoin to on;
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+                            explain_pictas                            
+----------------------------------------------------------------------
+ Gather (actual rows=N loops=N)
+   Workers Planned: 3
+   Workers Launched: N
+ ->  Create parallel_write
+   ->  Parallel Hash Join (actual rows=N loops=N)
+         Hash Cond: (temp1.col1 = temp2.col2)
+         ->  Parallel Seq Scan on temp1 (actual rows=N loops=N)
+         ->  Parallel Hash (actual rows=N loops=N)
+               Buckets: 4096  Batches: 1  Memory Usage: 64kB
+               ->  Parallel Seq Scan on temp2 (actual rows=N loops=N)
+(10 rows)
+
+select count(*) from parallel_write;
+ count 
+-------
+     5
+(1 row)
+
+drop table parallel_write;
+reset enable_nestloop;
+reset enable_mergejoin;
+reset enable_hashjoin;
+drop table temp1;
+drop table temp2;
+drop table temp3;
+drop function explain_pictas(text);
 rollback;
diff --git a/src/test/regress/sql/write_parallel.sql b/src/test/regress/sql/write_parallel.sql
index 78b479cedf..40aadafc2a 100644
--- a/src/test/regress/sql/write_parallel.sql
+++ b/src/test/regress/sql/write_parallel.sql
@@ -39,4 +39,215 @@ explain (costs off) create table parallel_write as execute prep_stmt;
 create table parallel_write as execute prep_stmt;
 drop table parallel_write;
 
+--
+-- Test parallel inserts in create table as/select into/create materialized
+-- view.
+--
+
+-- Parallel queries won't necessarily get as many workers as the planner
+-- asked for.  This affects not only the "Workers Launched:" field of EXPLAIN
+-- results, but also row counts and loop counts for parallel scans, Gathers,
+-- and everything in between.  This function filters out the values we can't
+-- rely on to be stable.
+-- This removes enough info that you might wonder why bother with EXPLAIN
+-- ANALYZE at all.  The answer is that we need to see whether the parallel
+-- inserts are being done by the workers, the only way is that
+-- Create <<tbl_name>> appears in the explain output.
+create function explain_pictas(text) returns setof text
+language plpgsql as
+$$
+declare
+    ln text;
+begin
+    for ln in
+        execute format('explain (analyze, costs off, summary off, timing off) %s',
+            $1)
+    loop
+        ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N');
+        ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N loops=N');
+        ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed by Filter: N');
+        return next ln;
+    end loop;
+end;
+$$;
+
+-- parallel inserts must occur as the CTAS creates a normal table
+select explain_pictas(
+'create table parallel_write as select length(stringu1) from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur as the table is temporary
+select explain_pictas(
+'create temporary table parallel_write as select length(stringu1) from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must occur as the CTAS creates an unlogged table
+select explain_pictas(
+'create unlogged table parallel_write as select length(stringu1) from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must occur as the select into creates a normal table
+select explain_pictas(
+'select length(stringu1) into parallel_write from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur as the table is temporary
+select explain_pictas(
+'select length(stringu1) into temporary parallel_write from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must occur as the select into creates an unlogged table
+select explain_pictas(
+'select length(stringu1) into unlogged parallel_write from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur as the parallelism will not be picked
+-- for select part because of for update clause
+select explain_pictas(
+'create table parallel_write as select length(stringu1) from tenk1 for update;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must occur as the materialized view is being created here
+select explain_pictas(
+'create materialized view parallel_mat_view as
+    select length(stringu1) from tenk1;');
+select count(*) from parallel_mat_view;
+drop materialized view parallel_mat_view;
+
+-- parallel inserts must occur as the CTAS creates the table using prepared
+-- statement for which parallelism would have been picked
+prepare parallel_write_prep as select length(stringu1) from tenk1;
+select explain_pictas(
+'create table parallel_write as execute parallel_write_prep;');
+select count(*) from parallel_write;
+deallocate parallel_write_prep;
+drop table parallel_write;
+
+-- parallel inserts must not occur as the parallelism will not be picked
+-- for select part because of the parallel unsafe function
+create sequence parallel_write_sequence;
+select explain_pictas(
+E'create table parallel_write as
+    select nextval(\'parallel_write_sequence\'), four from tenk1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+drop sequence parallel_write_sequence;
+
+-- parallel inserts must occur, as there is init plan that gets executed by
+-- each parallel worker
+select explain_pictas(
+'create table parallel_write as select two col1,
+    (select two from (select * from tenk2) as tt limit 1) col2
+    from tenk1  where tenk1.four = 3;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is sub plan that gets executed by
+-- the Gather node in leader
+select explain_pictas(
+'create table parallel_write as select two col1,
+    (select tenk1.two from generate_series(1,1)) col2
+    from tenk1  where tenk1.four = 3;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+create table temp1(col1) as select * from generate_series(1,5);
+create table temp2(col2) as select * from temp1;
+create table temp3(col3) as select * from temp1;
+
+-- parallel inserts must not occur, as there is a limit clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 limit 4;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is an order by clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 order by 1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is an order by clause
+select explain_pictas(
+'create table parallel_write as select * from temp1 order by 1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is a distinct clause
+select explain_pictas(
+'create table parallel_write as select distinct * from temp1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is an aggregate and group clause
+select explain_pictas(
+'create table parallel_write as select count(*) from temp1 group by col1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is an aggregate, group and having
+-- clauses
+select explain_pictas(
+'create table parallel_write as
+    select count(col1), (select col3 from
+        (select * from temp3) as tt limit 1) col4 from temp1, temp2
+    where temp1.col1 = temp2.col2 group by col4 having count(col1) > 0;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel inserts must not occur, as there is a window function
+select explain_pictas(
+'create table parallel_write as
+    select avg(col1) OVER (PARTITION BY col1) from temp1;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- nested loop join is the top node under which Gather node exists, so parallel
+-- inserts must not occur
+set enable_nestloop to on;
+set enable_mergejoin to off;
+set enable_hashjoin to off;
+
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- the top node is Gather under which merge join happens, so parallel inserts
+-- must occur
+set enable_nestloop to off;
+set enable_mergejoin to on;
+
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+-- parallel hash join happens under Gather node, so parallel inserts must occur
+set enable_mergejoin to off;
+set enable_hashjoin to on;
+
+select explain_pictas(
+'create table parallel_write as
+    select * from temp1, temp2  where temp1.col1 = temp2.col2;');
+select count(*) from parallel_write;
+drop table parallel_write;
+
+reset enable_nestloop;
+reset enable_mergejoin;
+reset enable_hashjoin;
+
+drop table temp1;
+drop table temp2;
+drop table temp3;
+drop function explain_pictas(text);
 rollback;
-- 
2.25.1

