From ea00eb1b2c8c2442096e18f44094ed0ee679c523 Mon Sep 17 00:00:00 2001
From: test <test>
Date: Fri, 8 May 2026 02:41:28 +0200
Subject: [PATCH v15 4/5] regression-test-and-doc-updates

---
 doc/src/sgml/func/func-info.sgml              |  61 ++
 doc/src/sgml/ref/alter_foreign_table.sgml     |  13 +
 doc/src/sgml/ref/alter_table.sgml             |  12 +
 doc/src/sgml/ref/create_foreign_table.sgml    |  37 ++
 doc/src/sgml/ref/create_table.sgml            |  38 ++
 doc/src/sgml/ref/create_table_as.sgml         |  23 +
 src/test/regress/expected/insert_parallel.out | 588 ++++++++++++++++++
 src/test/regress/sql/insert_parallel.sql      | 345 ++++++++++
 8 files changed, 1117 insertions(+)
 create mode 100644 src/test/regress/expected/insert_parallel.out
 create mode 100644 src/test/regress/sql/insert_parallel.sql

diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml
index 00f64f50ceb..cc9c43e905c 100644
--- a/doc/src/sgml/func/func-info.sgml
+++ b/doc/src/sgml/func/func-info.sgml
@@ -2553,6 +2553,67 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id'));
         Undefined objects are identified with <literal>NULL</literal> values.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_get_table_parallel_dml_safety</primary>
+        </indexterm>
+        <function>pg_get_table_parallel_dml_safety</function> ( <parameter>table_name</parameter> <type>regclass</type> )
+        <returnvalue>record</returnvalue>
+        ( <parameter>objid</parameter> <type>oid</type>,
+        <parameter>classid</parameter> <type>oid</type>,
+        <parameter>proparallel</parameter> <type>char</type> )
+       </para>
+       <para>
+        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.
+        <parameter>classid</parameter> is the OID of the system catalog
+        containing the object;
+        <parameter>objid</parameter> is the OID of the object itself.
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_get_table_max_parallel_dml_hazard</primary>
+        </indexterm>
+        <function>pg_get_table_max_parallel_dml_hazard</function> ( <type>regclass</type> )
+        <returnvalue>char</returnvalue>
+       </para>
+       <para>
+        Returns the worst parallel DML safety hazard that can be found in the
+        given relation:
+         <itemizedlist>
+          <listitem>
+           <para>
+            <literal>s</literal> safe
+           </para>
+          </listitem>
+          <listitem>
+           <para>
+            <literal>r</literal> restricted
+           </para>
+          </listitem>
+          <listitem>
+           <para>
+            <literal>u</literal> unsafe
+           </para>
+          </listitem>
+         </itemizedlist>
+       </para>
+       <para>
+        Users can use this function to do a quick check without caring about
+        specific parallel-related objects.
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
index 228067f087c..74c0efeb540 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 ] <replaceable class="parameter">name</replaceab
     RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
     SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
+    PARALLEL { UNSAFE | RESTRICTED | SAFE }
 
 <phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
 
@@ -303,6 +305,17 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceab
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>PARALLEL</literal></term>
+    <listitem>
+     <para>
+      Change whether the data in the table can be modified in parallel mode.
+      See the similar form of <link linkend="sql-altertable"><command>ALTER TABLE</command></link>
+      for more details.
+     </para>
+    </listitem>
+   </varlistentry>
+
   </variablelist>
   </para>
 
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 1f9a456fd33..b6b08ebe622 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -43,6 +43,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
     SPLIT PARTITION <replaceable class="parameter">partition_name</replaceable> INTO
         (PARTITION <replaceable class="parameter">partition_name1</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT },
          PARTITION <replaceable class="parameter">partition_name2</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT } [, ...])
+ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
+    PARALLEL { UNSAFE | RESTRICTED | SAFE }
 
 <phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
 
@@ -1378,6 +1380,16 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>PARALLEL</literal></term>
+    <listitem>
+     <para>
+      Change whether the data in the table can be modified in parallel mode.
+      See <link linkend="sql-createtable"><command>CREATE TABLE</command></link> for details.
+     </para>
+    </listitem>
+   </varlistentry>
+
   </variablelist>
   </para>
 
diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml
index 083f16772b7..3ae6f758d57 100644
--- a/doc/src/sgml/ref/create_foreign_table.sgml
+++ b/doc/src/sgml/ref/create_foreign_table.sgml
@@ -28,6 +28,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name
     [, ... ]
 ] )
 [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+[ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
   SERVER <replaceable class="parameter">server_name</replaceable>
 [ OPTIONS ( <replaceable class="parameter">option</replaceable> '<replaceable class="parameter">value</replaceable>' [, ... ] ) ]
 
@@ -38,6 +39,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name
     [, ... ]
 ) ]
 { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }
+[ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
   SERVER <replaceable class="parameter">server_name</replaceable>
 [ OPTIONS ( <replaceable class="parameter">option</replaceable> '<replaceable class="parameter">value</replaceable>' [, ... ] ) ]
 
@@ -419,6 +421,41 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>PARALLEL DML { UNSAFE | RESTRICTED | SAFE } </literal></term>
+    <listitem>
+     <para>
+      <literal>PARALLEL DML UNSAFE</literal> 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.
+      <literal>PARALLEL DML RESTRICTED</literal> indicates that the data in the
+      table can be modified in parallel mode, but the modification is
+      restricted to the parallel group leader.
+      <literal>PARALLEL DML SAFE</literal> indicates that the data in the table
+      can be modified in parallel mode without restriction. Note that
+      <productname>PostgreSQL</productname> currently does not support data
+      modification by parallel workers.
+     </para>
+
+     <para>
+      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.).
+     </para>
+
+     <para>
+      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
+      <link linkend="functions-info-object-table">
+      <function>pg_get_table_parallel_dml_safety()</function></link> and
+      <link linkend="functions-info-object-table">
+      <function>pg_get_table_max_parallel_dml_hazard()</function></link> for more information.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
    <varlistentry id="sql-createforeigntable-parms-server">
     <term><replaceable class="parameter">server_name</replaceable></term>
     <listitem>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e342585c7f0..f6b47e1d0a6 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -33,6 +33,7 @@ CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <
 [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
 [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
 [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
+[ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
 
 CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name</replaceable>
     OF <replaceable class="parameter">type_name</replaceable> [ (
@@ -45,6 +46,7 @@ CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <
 [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
 [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
 [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
+[ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
 
 CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name</replaceable>
     PARTITION OF <replaceable class="parameter">parent_table</replaceable> [ (
@@ -57,6 +59,7 @@ CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <
 [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
 [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
 [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
+[ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
 
 <phrase>where <replaceable>persistence_mode</replaceable> is:</phrase>
 
@@ -1559,6 +1562,41 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry id="sql-createtable-paralleldmlsafety">
+    <term><literal>PARALLEL DML { UNSAFE | RESTRICTED | SAFE } </literal></term>
+    <listitem>
+     <para>
+      <literal>PARALLEL DML UNSAFE</literal> 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.
+      <literal>PARALLEL DML RESTRICTED</literal> indicates that the data in the
+      table can be modified in parallel mode, but the modification is
+      restricted to the parallel group leader.
+      <literal>PARALLEL DML SAFE</literal> indicates that the data in the table
+      can be modified in parallel mode without restriction. Note that
+      <productname>PostgreSQL</productname> currently does not support data
+      modification by parallel workers.
+     </para>
+
+     <para>
+      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.).
+     </para>
+
+     <para>
+      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
+      <link linkend="functions-info-object-table">
+      <function>pg_get_table_parallel_dml_safety()</function></link> and
+      <link linkend="functions-info-object-table">
+      <function>pg_get_table_max_parallel_dml_hazard()</function></link> for more information.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry id="sql-createtable-parms-using-index-tablespace">
     <term><literal>USING INDEX TABLESPACE <replaceable class="parameter">tablespace_name</replaceable></literal></term>
     <listitem>
diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml
index 0492933ff38..e707af5a507 100644
--- a/doc/src/sgml/ref/create_table_as.sgml
+++ b/doc/src/sgml/ref/create_table_as.sgml
@@ -27,6 +27,7 @@ CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
     [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
+    [ PARALLEL DML { UNSAFE | RESTRICTED | SAFE } ]
     AS <replaceable>query</replaceable>
     [ WITH [ NO ] DATA ]
 
@@ -226,6 +227,28 @@ CREATE [ <replaceable>persistence_mode</replaceable> ] TABLE [ IF NOT EXISTS ] <
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>PARALLEL DML { UNSAFE | RESTRICTED | SAFE } </literal></term>
+    <listitem>
+     <para>
+      <literal>PARALLEL DML UNSAFE</literal> indicates that the data in table
+      can't be modified in parallel mode. This is the default.
+      <literal>PARALLEL DML RESTRICTED</literal> indicates that the data in
+      table can be modified in parallel mode, but the modification is
+      restricted to parallel group leader. <literal>PARALLEL DML SAFE</literal>
+      indicates that the table is safe to be modified in parallel mode without
+      restriction. But note that <productname>PostgreSQL</productname>
+      does not support data modification in parallel worker for now.
+     </para>
+
+     <para>
+      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 ...).
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable>query</replaceable></term>
     <listitem>
diff --git a/src/test/regress/expected/insert_parallel.out b/src/test/regress/expected/insert_parallel.out
new file mode 100644
index 00000000000..f5d22ce6497
--- /dev/null
+++ b/src/test/regress/expected/insert_parallel.out
@@ -0,0 +1,588 @@
+--
+-- 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));
+alter table names2 parallel dml safe;
+alter table names4 parallel dml safe;
+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
+);
+-- 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)
+
+--
+-- 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)
+
+--
+-- 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"
+alter table para_insert_p1 parallel dml safe;
+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);
+alter table test_data1 parallel dml safe;
+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)
+--
+alter table para_insert_f1 parallel dml restricted;
+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);
+alter table test_conflict_table parallel dml safe;
+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
+--
+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)
+
+--
+-- Test INSERT with parallel-restricted index expression
+--
+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)
+
+--
+-- Test INSERT with underlying query - and RETURNING (no projection)
+-- (should create a parallel plan; parallel SELECT)
+--
+create table names5 (like names);
+alter table names5 parallel dml safe;
+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);
+alter table names6 parallel dml safe;
+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);
+alter table names7 parallel dml safe;
+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);
+alter table temp_names parallel dml restricted;
+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
+--
+alter table testdef parallel dml safe;
+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)
+
+--
+-- 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);
+alter table names_with_safe_trigger parallel dml safe;
+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)
+
+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
+--
+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)
+
+--
+-- Test partition with parallel-unsafe trigger
+--
+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)
+
+--
+-- 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)
+
+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/sql/insert_parallel.sql b/src/test/regress/sql/insert_parallel.sql
new file mode 100644
index 00000000000..b4565f78ab7
--- /dev/null
+++ b/src/test/regress/sql/insert_parallel.sql
@@ -0,0 +1,345 @@
+--
+-- 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));
+
+alter table names2 parallel dml safe;
+alter table names4 parallel dml safe;
+
+
+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
+);
+
+-- 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;
+
+--
+-- 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;
+
+--
+-- Test INSERT with ordered underlying query.
+-- (should create plan with parallel SELECT, GatherMerge parent node)
+--
+truncate para_insert_p1 cascade;
+alter table para_insert_p1 parallel dml safe;
+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);
+alter table test_data1 parallel dml safe;
+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)
+--
+alter table para_insert_f1 parallel dml restricted;
+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);
+alter table test_conflict_table parallel dml safe;
+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
+--
+select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names2');
+select pg_get_table_max_parallel_dml_hazard('names2');
+
+--
+-- Test INSERT with parallel-restricted index expression
+--
+select pg_class_relname(classid), proparallel from pg_get_table_parallel_dml_safety('names4');
+select pg_get_table_max_parallel_dml_hazard('names4');
+
+--
+-- Test INSERT with underlying query - and RETURNING (no projection)
+-- (should create a parallel plan; parallel SELECT)
+--
+create table names5 (like names);
+alter table names5 parallel dml safe;
+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);
+alter table names6 parallel dml safe;
+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);
+alter table names7 parallel dml safe;
+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);
+alter table temp_names parallel dml restricted;
+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
+--
+alter table testdef parallel dml safe;
+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');
+
+--
+-- 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);
+alter table names_with_safe_trigger parallel dml safe;
+
+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');
+insert into names_with_safe_trigger select * from names;
+
+--
+-- Test table with parallel-unsafe before stmt-level triggers
+--
+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');
+
+--
+-- Test partition with parallel-unsafe trigger
+--
+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');
+
+--
+-- 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');
+
+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.53.0

