From 2ea397a0c31f835a508770cd290e6e95ba149c86 Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@cn.fujitsu.com>
Date: Tue, 2 Feb 2021 09:07:02 +0800
Subject: [PATCH] reloption parallel_dml_enabled

Add new table option parallel_dml_enabled.

In addition to the GUC option, the user may want a mechanism for
specifying parallel dml with finer granularity, to specify the
use of parallel dml for specific tables.
The new table option parallel_dml_enabled allows this.

The default is true.
---
 src/backend/access/common/reloptions.c | 25 +++++++++---
 src/backend/optimizer/plan/planner.c   |  3 +-
 src/backend/optimizer/util/clauses.c   | 70 ++++++++++++++++++++++++++++------
 src/bin/psql/tab-complete.c            |  1 +
 src/include/optimizer/clauses.h        |  1 +
 src/include/utils/rel.h                | 23 +++++++++++
 6 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index c687d3e..938131a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -168,6 +168,15 @@ static relopt_bool boolRelOpts[] =
 		},
 		true
 	},
+	{
+		{
+			"parallel_dml_enabled",
+			"Enables \"parallel dml\" feature for this table",
+			RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED,
+			ShareUpdateExclusiveLock
+		},
+		true
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1859,7 +1868,9 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		{"vacuum_index_cleanup", RELOPT_TYPE_BOOL,
 		offsetof(StdRdOptions, vacuum_index_cleanup)},
 		{"vacuum_truncate", RELOPT_TYPE_BOOL,
-		offsetof(StdRdOptions, vacuum_truncate)}
+		offsetof(StdRdOptions, vacuum_truncate)},
+		{"parallel_dml_enabled", RELOPT_TYPE_BOOL,
+		offsetof(StdRdOptions, parallel_dml_enabled)}
 	};
 
 	return (bytea *) build_reloptions(reloptions, validate, kind,
@@ -1961,13 +1972,15 @@ build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
 bytea *
 partitioned_table_reloptions(Datum reloptions, bool validate)
 {
-	/*
-	 * There are no options for partitioned tables yet, but this is able to do
-	 * some validation.
-	 */
+	static const relopt_parse_elt tab[] = {
+		{"parallel_dml_enabled", RELOPT_TYPE_BOOL,
+		offsetof(PartitionedOptions, parallel_dml_enabled)}
+	};
+
 	return (bytea *) build_reloptions(reloptions, validate,
 									  RELOPT_KIND_PARTITIONED,
-									  0, NULL, 0);
+									  sizeof(PartitionedOptions),
+									  tab, lengthof(tab));
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f6ac972..cac11a9 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -339,8 +339,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
 		IsUnderPostmaster &&
 		(parse->commandType == CMD_SELECT ||
-		(enable_parallel_dml &&
-		IsModifySupportedInParallelMode(parse->commandType))) &&
+		is_parallel_possible_for_modify(parse)) &&
 		!parse->hasModifyingCTE &&
 		max_parallel_workers_per_gather > 0 &&
 		!IsParallelWorker())
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 02a7e05..30f6a54 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1169,23 +1169,34 @@ rel_max_parallel_hazard_for_modify(Relation rel,
 }
 
 /*
- * max_parallel_hazard_for_modify
+ * is_parallel_possible_for_modify
  *
- * Determines the worst parallel-mode hazard level for the specified
- * table-modification statement, based on the statement attributes and
- * target table. An initial max parallel hazard level may optionally be
- * supplied. The search returns the earliest in the following list:
- * PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE
+ * Check at a high-level if parallel mode is able to be used for the specified
+ * table-modification statement.
+ * It's not possible in the following cases:
+ *
+ *  1) enable_parallel_dml is off
+ *  2) UPDATE or DELETE command
+ *  3) INSERT...ON CONFLICT...DO UPDATE
+ *  4) INSERT without SELECT on a relation
+ *  5) the reloption parallel_dml_enabled is not set for the target table
+ *
+ * (Note: we don't do in-depth parallel-safety checks here, we do only the
+ * cheaper tests that can quickly exclude obvious cases for which
+ * parallelism isn't supported, to avoid having to do further parallel-safety
+ * checks for these)
  */
-char
-max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard)
+bool
+is_parallel_possible_for_modify(Query *parse)
 {
 	RangeTblEntry *rte;
 	ListCell *lc;
 	bool hasSubQueryOnRelation;
-	max_parallel_hazard_context context;
 	Relation rel;
 
+	if (!enable_parallel_dml ||
+		!IsModifySupportedInParallelMode(parse->commandType))
+		return false;
 
 	/*
 	 * UPDATE is not currently supported in parallel-mode, so prohibit
@@ -1197,7 +1208,7 @@ max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard)
 	 * combo-cid and it needs to be propagated to the workers.
 	 */
 	if (parse->onConflict != NULL && parse->onConflict->action == ONCONFLICT_UPDATE)
-		return PROPARALLEL_UNSAFE;
+		return false;
 
 	/*
 	 * If there is no underlying query on a relation, a parallel
@@ -1226,7 +1237,44 @@ max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard)
 		}
 	}
 	if (!hasSubQueryOnRelation)
-		return PROPARALLEL_UNSAFE;
+		return false;
+
+	rte = rt_fetch(parse->resultRelation, parse->rtable);
+	rel = table_open(rte->relid, NoLock);
+
+	/*
+	 * Check if parallel_dml_enabled is enabled for the target table,
+	 * if not, skip the safety checks.
+	 *
+	 * (Note: if the target table is partitioned, the parallel_dml_enabled
+	 * option setting of the partitions are ignored).
+	 */
+	if (!RelationGetParallelDML(rel, true))
+	{
+		table_close(rel, NoLock);
+		return false;
+	}
+
+	table_close(rel, NoLock);
+
+	return true;
+}
+
+/*
+ * max_parallel_hazard_for_modify
+ *
+ * Determines the worst parallel-mode hazard level for the specified
+ * table-modification statement, based on the statement attributes and
+ * target table. An initial max parallel hazard level may optionally be
+ * supplied. The search returns the earliest in the following list:
+ * PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE
+ */
+char
+max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard)
+{
+	RangeTblEntry *rte;
+	max_parallel_hazard_context context;
+	Relation rel;
 
 	/*
 	 * Setup the context used in finding the max parallel-mode hazard.
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 17f7265..89b5fe5 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1110,6 +1110,7 @@ static const char *const table_storage_parameters[] = {
 	"autovacuum_vacuum_threshold",
 	"fillfactor",
 	"log_autovacuum_min_duration",
+	"parallel_dml_enabled",
 	"parallel_workers",
 	"toast.autovacuum_enabled",
 	"toast.autovacuum_freeze_max_age",
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 3cdddbf..ccbc35a 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -53,5 +53,6 @@ extern void CommuteOpExpr(OpExpr *clause);
 extern Query *inline_set_returning_function(PlannerInfo *root,
 											RangeTblEntry *rte);
 extern char max_parallel_hazard_for_modify(Query *parse, char initial_max_parallel_hazard);
+extern bool is_parallel_possible_for_modify(Query *parse);
 
 #endif							/* CLAUSES_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 2a41a00..f8d0778 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -307,6 +307,7 @@ typedef struct StdRdOptions
 	int			parallel_workers;	/* max number of parallel workers */
 	bool		vacuum_index_cleanup;	/* enables index vacuuming and cleanup */
 	bool		vacuum_truncate;	/* enables vacuum to truncate a relation */
+	bool		parallel_dml_enabled;	/* enables planner's use of parallel DML */
 } StdRdOptions;
 
 #define HEAP_MIN_FILLFACTOR			10
@@ -425,6 +426,28 @@ typedef struct ViewOptions
 	  VIEW_OPTION_CHECK_OPTION_CASCADED)
 
 /*
+ * PartitionedOptions
+ *		Contents of rd_options for partitioned tables
+ */
+typedef struct PartitionedOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	bool		parallel_dml_enabled;	/* enables planner's use of parallel DML */
+} PartitionedOptions;
+
+/*
+ * RelationGetParallelDML
+ *		Returns the relation's parallel_dml_enabled reloption setting.
+ *		Note multiple eval of argument!
+ */
+#define RelationGetParallelDML(relation, defaultpd) 						\
+	((relation)->rd_options ?												\
+	 (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?				\
+	 ((PartitionedOptions *) (relation)->rd_options)->parallel_dml_enabled :\
+	 ((StdRdOptions *) (relation)->rd_options)->parallel_dml_enabled) :		\
+	 (defaultpd))
+
+/*
  * RelationIsValid
  *		True iff relation descriptor is valid.
  */
-- 
2.7.2.windows.1

