From 443246db38c9c3e3547d043c54b658b17d65e8fd Mon Sep 17 00:00:00 2001 From: "houzj.fnst" Date: Tue, 27 Apr 2021 17:42:24 +0800 Subject: [PATCH] CREATE/ALTER TABLE PARALLEL DML Enabling users to declare that the table allows parallel data modification, Add a table property that represents parallel safety of the table for DML statement execution. Users specify it as follows: CREATE TABLE table_name PARALLEL DML { UNSAFE | RESTRICTED | SAFE }; ALTER TABLE table_name PARALLEL DML { UNSAFE | RESTRICTED | SAFE }; This property is recorded in pg_class's relparallel column as 'u', 'r', or 's', just like pg_proc's proparallel. The default is UNSAFE. The planner assumes that all of the table, its descendant partitions, and their ancillary objects have the specified parallel safety or safer one. The user is responsible for its correctness. If the parallel processes find an object that is less safer than the assumed parallel safety during statement execution, it throws an ERROR and abort the statement execution. --- src/backend/bootstrap/bootparse.y | 3 + src/backend/catalog/heap.c | 7 +- src/backend/catalog/index.c | 2 + src/backend/catalog/toasting.c | 1 + src/backend/commands/cluster.c | 1 + src/backend/commands/createas.c | 1 + src/backend/commands/sequence.c | 1 + src/backend/commands/tablecmds.c | 87 +++++++++++++++++++ src/backend/commands/typecmds.c | 1 + src/backend/commands/view.c | 1 + src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 2 + src/backend/nodes/outfuncs.c | 2 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/util/clauses.c | 25 +++++- src/backend/parser/gram.y | 64 ++++++++++---- src/backend/utils/cache/relcache.c | 6 +- src/bin/pg_dump/pg_dump.c | 47 ++++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/include/catalog/heap.h | 2 + src/include/catalog/pg_class.h | 3 + src/include/nodes/parsenodes.h | 4 +- src/include/nodes/primnodes.h | 1 + src/include/parser/kwlist.h | 1 + src/include/utils/relcache.h | 3 +- .../test_ddl_deparse/test_ddl_deparse.c | 3 + 26 files changed, 238 insertions(+), 33 deletions(-) diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 5fcd004e1b..88fcd57082 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -25,6 +25,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_class.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "catalog/toasting.h" #include "commands/defrem.h" @@ -208,6 +209,7 @@ Boot_CreateStmt: tupdesc, RELKIND_RELATION, RELPERSISTENCE_PERMANENT, + PROPARALLEL_UNSAFE, shared_relation, mapped_relation, true, @@ -231,6 +233,7 @@ Boot_CreateStmt: NIL, RELKIND_RELATION, RELPERSISTENCE_PERMANENT, + PROPARALLEL_UNSAFE, shared_relation, mapped_relation, ONCOMMIT_NOOP, diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 42ff175bc8..3bf4d9eff5 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -301,6 +301,7 @@ heap_create(const char *relname, TupleDesc tupDesc, char relkind, char relpersistence, + char relparalleldml, bool shared_relation, bool mapped_relation, bool allow_system_table_mods, @@ -403,7 +404,8 @@ heap_create(const char *relname, shared_relation, mapped_relation, relpersistence, - relkind); + relkind, + relparalleldml); /* * Have the storage manager create the relation's disk file, if needed. @@ -960,6 +962,7 @@ InsertPgClassTuple(Relation pg_class_desc, values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass); values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated); values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident); + values[Anum_pg_class_relparalleldml - 1] = CharGetDatum(rd_rel->relparalleldml); values[Anum_pg_class_relispartition - 1] = BoolGetDatum(rd_rel->relispartition); values[Anum_pg_class_relrewrite - 1] = ObjectIdGetDatum(rd_rel->relrewrite); values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid); @@ -1153,6 +1156,7 @@ heap_create_with_catalog(const char *relname, List *cooked_constraints, char relkind, char relpersistence, + char relparalleldml, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, @@ -1300,6 +1304,7 @@ heap_create_with_catalog(const char *relname, tupdesc, relkind, relpersistence, + relparalleldml, shared_relation, mapped_relation, allow_system_table_mods, diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index a628b3281c..6ba64d8dbb 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -50,6 +50,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -935,6 +936,7 @@ index_create(Relation heapRelation, indexTupDesc, relkind, relpersistence, + PROPARALLEL_UNSAFE, shared_relation, mapped_relation, allow_system_table_mods, diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 933a0734d1..e4a05205be 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -253,6 +253,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, + rel->rd_rel->relparalleldml, shared_relation, mapped_relation, ONCOMMIT_NOOP, diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 6487a9e3fc..2151121066 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -691,6 +691,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence, NIL, RELKIND_RELATION, relpersistence, + OldHeap->rd_rel->relparalleldml, false, RelationIsMapped(OldHeap), ONCOMMIT_NOOP, diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index dce882012e..45aacc8b7a 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -107,6 +107,7 @@ create_ctas_internal(List *attrList, IntoClause *into) create->options = into->options; create->oncommit = into->onCommit; create->tablespacename = into->tableSpaceName; + create->paralleldmlsafety = into->paralleldmlsafety; create->if_not_exists = false; create->accessMethod = into->accessMethod; diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0415df9ccb..6f25c231e9 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -211,6 +211,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) stmt->options = NIL; stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; + stmt->paralleldmlsafety = NULL; stmt->if_not_exists = seq->if_not_exists; address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7d00f4eb25..ca4528eb06 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -40,6 +40,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_statistic_ext.h" #include "catalog/pg_trigger.h" @@ -602,6 +603,7 @@ static List *GetParentedForeignKeyRefs(Relation partition); static void ATDetachCheckNoForeignKeyRefs(Relation partition); static void ATExecAlterCollationRefreshVersion(Relation rel, List *coll); static char GetAttributeCompression(Form_pg_attribute att, char *compression); +static void ATExecParallelDMLSafety(Relation rel, Node *def); /* ---------------------------------------------------------------- @@ -647,6 +649,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, LOCKMODE parentLockmode; const char *accessMethod = NULL; Oid accessMethodId = InvalidOid; + char relparalleldml = PROPARALLEL_UNSAFE; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -933,6 +936,28 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (accessMethod != NULL) accessMethodId = get_table_am_oid(accessMethod, false); + if (stmt->paralleldmlsafety != NULL) + { + if (strcmp(stmt->paralleldmlsafety, "safe") == 0) + { + if (relkind == RELKIND_FOREIGN_TABLE || + stmt->relation->relpersistence == RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot support parallel data modification on foreign or temporary table"))); + + relparalleldml = PROPARALLEL_SAFE; + } + else if (strcmp(stmt->paralleldmlsafety, "restricted") == 0) + relparalleldml = PROPARALLEL_RESTRICTED; + else if (strcmp(stmt->paralleldmlsafety, "unsafe") == 0) + relparalleldml = PROPARALLEL_UNSAFE; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"parallel dml\" must be SAFE, RESTRICTED, or UNSAFE"))); + } + /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be @@ -951,6 +976,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, old_constraints), relkind, stmt->relation->relpersistence, + relparalleldml, false, false, stmt->oncommit, @@ -4206,6 +4232,7 @@ AlterTableGetLockLevel(List *cmds) case AT_SetIdentity: case AT_DropExpression: case AT_SetCompression: + case AT_ParallelDMLSafety: cmd_lockmode = AccessExclusiveLock; break; @@ -4748,6 +4775,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* No command-specific prep needed */ pass = AT_PASS_MISC; break; + case AT_ParallelDMLSafety: + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); @@ -5155,6 +5187,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Assert(rel->rd_rel->relkind == RELKIND_INDEX); ATExecAlterCollationRefreshVersion(rel, cmd->object); break; + case AT_ParallelDMLSafety: + ATExecParallelDMLSafety(rel, cmd->def); + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); @@ -18499,3 +18534,55 @@ GetAttributeCompression(Form_pg_attribute att, char *compression) return cmethod; } + +static void +ATExecParallelDMLSafety(Relation rel, Node *def) +{ + Relation pg_class; + Oid relid; + HeapTuple tuple; + char relparallel = PROPARALLEL_SAFE; + char *parallel = strVal((Value *) def); + + if (parallel) + { + if (strcmp(parallel, "safe") == 0) + { + /* + * We can't support table modification in a parallel worker if it's a + * foreign table/partition (no FDW API for supporting parallel access) or + * a temporary table. + */ + if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE || + RelationUsesLocalBuffers(rel)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot support parallel data modification on foreign or temporary table"))); + + relparallel = PROPARALLEL_SAFE; + } + else if (strcmp(parallel, "restricted") == 0) + relparallel = PROPARALLEL_RESTRICTED; + else if (strcmp(parallel, "unsafe") == 0) + relparallel = PROPARALLEL_UNSAFE; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"parallel dml\" must be SAFE, RESTRICTED, or UNSAFE"))); + } + + relid = RelationGetRelid(rel); + + pg_class = table_open(RelationRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + ((Form_pg_class) GETSTRUCT(tuple))->relparalleldml = relparallel; + CatalogTupleUpdate(pg_class, &tuple->t_self, tuple); + + table_close(pg_class, RowExclusiveLock); + heap_freetuple(tuple); +} diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 036fa69d17..ec3834fae9 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2540,6 +2540,7 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist) createStmt->options = NIL; createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; + createStmt->paralleldmlsafety = NULL; createStmt->if_not_exists = false; /* diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index f2642dba6c..2d77a88726 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -227,6 +227,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, createStmt->options = options; createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; + createStmt->paralleldmlsafety = NULL; createStmt->if_not_exists = false; /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 632cc31a04..cb07c93f21 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3521,6 +3521,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode) COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); COPY_STRING_FIELD(accessMethod); + COPY_STRING_FIELD(paralleldmlsafety); COPY_SCALAR_FIELD(if_not_exists); } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index a410a29a17..b527c6ed0b 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -146,6 +146,7 @@ _equalIntoClause(const IntoClause *a, const IntoClause *b) COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(onCommit); COMPARE_STRING_FIELD(tableSpaceName); + COMPARE_STRING_FIELD(paralleldmlsafety); COMPARE_NODE_FIELD(viewQuery); COMPARE_SCALAR_FIELD(skipData); @@ -1276,6 +1277,7 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b) COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); COMPARE_STRING_FIELD(accessMethod); + COMPARE_STRING_FIELD(paralleldmlsafety); COMPARE_SCALAR_FIELD(if_not_exists); return true; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c723f6d635..b0f4ea9a2e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1106,6 +1106,7 @@ _outIntoClause(StringInfo str, const IntoClause *node) WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(onCommit, OnCommitAction); WRITE_STRING_FIELD(tableSpaceName); + WRITE_STRING_FIELD(paralleldmlsafety); WRITE_NODE_FIELD(viewQuery); WRITE_BOOL_FIELD(skipData); } @@ -2702,6 +2703,7 @@ _outCreateStmtInfo(StringInfo str, const CreateStmt *node) WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); WRITE_STRING_FIELD(accessMethod); + WRITE_STRING_FIELD(paralleldmlsafety); WRITE_BOOL_FIELD(if_not_exists); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3746668f52..16ddc6699e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -563,6 +563,7 @@ _readIntoClause(void) READ_NODE_FIELD(options); READ_ENUM_FIELD(onCommit, OnCommitAction); READ_STRING_FIELD(tableSpaceName); + READ_STRING_FIELD(paralleldmlsafety); READ_NODE_FIELD(viewQuery); READ_BOOL_FIELD(skipData); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 3ec0bf26cb..93344a6b1a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -177,7 +177,7 @@ static Query *substitute_actual_srf_parameters(Query *expr, int nargs, List *args); static Node *substitute_actual_srf_parameters_mutator(Node *node, substitute_actual_srf_parameters_context *context); - +static bool max_parallel_hazard_test(char proparallel, max_parallel_hazard_context *context); static safety_object *make_safety_object(Oid objid, Oid classid, char proparallel); /***************************************************************************** @@ -645,6 +645,7 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context) char max_parallel_hazard(Query *parse) { + bool max_hazard_found; max_parallel_hazard_context context; context.max_hazard = PROPARALLEL_SAFE; @@ -654,7 +655,27 @@ max_parallel_hazard(Query *parse) context.func_oids = NIL; context.partition_directory = NULL; - (void) max_parallel_hazard_walker((Node *) parse, &context); + max_hazard_found = max_parallel_hazard_walker((Node *) parse, &context); + + if (!max_hazard_found && + IsModifySupportedInParallelMode(parse->commandType)) + { + RangeTblEntry *rte; + Relation target_rel; + + rte = rt_fetch(parse->resultRelation, parse->rtable); + + /* + * The target table is already locked by the caller (this is done in the + * parse/analyze phase), and remains locked until end-of-transaction. + */ + target_rel = table_open(rte->relid, NoLock); + + (void) max_parallel_hazard_test(target_rel->rd_rel->relparalleldml, + &context); + table_close(target_rel, NoLock); + } + return context.max_hazard; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index b4ab4014c8..841db357a0 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -610,6 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type hash_partbound_elem %type optColumnCompression +%type ParallelDMLSafety /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -654,7 +655,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC - DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P + DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DML DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT @@ -2690,6 +2691,14 @@ alter_table_cmd: n->object = $3; $$ = (Node *)n; } + /* ALTER TABLE PARALLEL SAFE */ + | PARALLEL DML ColId + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ParallelDMLSafety; + n->def = (Node *)makeString($3); + $$ = (Node *)n; + } | alter_generic_options { AlterTableCmd *n = makeNode(AlterTableCmd); @@ -3275,7 +3284,7 @@ copy_generic_opt_arg_list_item: CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause OptWith - OnCommitOption OptTableSpace + OnCommitOption OptTableSpace ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $4->relpersistence = $2; @@ -3289,12 +3298,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $11; n->oncommit = $12; n->tablespacename = $13; + n->paralleldmlsafety = $14; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '(' OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause - OptWith OnCommitOption OptTableSpace + OptWith OnCommitOption OptTableSpace ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $7->relpersistence = $2; @@ -3308,12 +3318,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $14; n->oncommit = $15; n->tablespacename = $16; + n->paralleldmlsafety = $17; n->if_not_exists = true; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF any_name OptTypedTableElementList OptPartitionSpec table_access_method_clause - OptWith OnCommitOption OptTableSpace + OptWith OnCommitOption OptTableSpace ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $4->relpersistence = $2; @@ -3328,12 +3339,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $10; n->oncommit = $11; n->tablespacename = $12; + n->paralleldmlsafety = $13; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name OptTypedTableElementList OptPartitionSpec table_access_method_clause - OptWith OnCommitOption OptTableSpace + OptWith OnCommitOption OptTableSpace ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $7->relpersistence = $2; @@ -3348,12 +3360,14 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $13; n->oncommit = $14; n->tablespacename = $15; + n->paralleldmlsafety = $16; n->if_not_exists = true; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec table_access_method_clause OptWith OnCommitOption OptTableSpace + ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $4->relpersistence = $2; @@ -3368,12 +3382,14 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $12; n->oncommit = $13; n->tablespacename = $14; + n->paralleldmlsafety = $15; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec table_access_method_clause OptWith OnCommitOption OptTableSpace + ParallelDMLSafety { CreateStmt *n = makeNode(CreateStmt); $7->relpersistence = $2; @@ -3388,6 +3404,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->options = $15; n->oncommit = $16; n->tablespacename = $17; + n->paralleldmlsafety = $18; n->if_not_exists = true; $$ = (Node *)n; } @@ -4086,6 +4103,10 @@ OptTableSpace: TABLESPACE name { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; +ParallelDMLSafety: PARALLEL DML name { $$ = $3; } + | /*EMPTY*/ { $$ = NULL; } + ; + OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; } | /*EMPTY*/ { $$ = NULL; } ; @@ -4233,7 +4254,7 @@ CreateAsStmt: create_as_target: qualified_name opt_column_list table_access_method_clause - OptWith OnCommitOption OptTableSpace + OptWith OnCommitOption OptTableSpace ParallelDMLSafety { $$ = makeNode(IntoClause); $$->rel = $1; @@ -4242,6 +4263,7 @@ create_as_target: $$->options = $4; $$->onCommit = $5; $$->tableSpaceName = $6; + $$->paralleldmlsafety = $7; $$->viewQuery = NULL; $$->skipData = false; /* might get changed later */ } @@ -5021,7 +5043,7 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o CreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name '(' OptTableElementList ')' - OptInherit SERVER name create_generic_options + OptInherit ParallelDMLSafety SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT; @@ -5033,15 +5055,16 @@ CreateForeignTableStmt: n->base.options = NIL; n->base.oncommit = ONCOMMIT_NOOP; n->base.tablespacename = NULL; + n->base.paralleldmlsafety = $9; n->base.if_not_exists = false; /* FDW-specific data */ - n->servername = $10; - n->options = $11; + n->servername = $11; + n->options = $12; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name '(' OptTableElementList ')' - OptInherit SERVER name create_generic_options + OptInherit ParallelDMLSafety SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT; @@ -5053,15 +5076,16 @@ CreateForeignTableStmt: n->base.options = NIL; n->base.oncommit = ONCOMMIT_NOOP; n->base.tablespacename = NULL; + n->base.paralleldmlsafety = $12; n->base.if_not_exists = true; /* FDW-specific data */ - n->servername = $13; - n->options = $14; + n->servername = $14; + n->options = $15; $$ = (Node *) n; } | CREATE FOREIGN TABLE qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec - SERVER name create_generic_options + ParallelDMLSafety SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT; @@ -5074,15 +5098,16 @@ CreateForeignTableStmt: n->base.options = NIL; n->base.oncommit = ONCOMMIT_NOOP; n->base.tablespacename = NULL; + n->base.paralleldmlsafety = $10; n->base.if_not_exists = false; /* FDW-specific data */ - n->servername = $11; - n->options = $12; + n->servername = $12; + n->options = $13; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec - SERVER name create_generic_options + ParallelDMLSafety SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT; @@ -5095,10 +5120,11 @@ CreateForeignTableStmt: n->base.options = NIL; n->base.oncommit = ONCOMMIT_NOOP; n->base.tablespacename = NULL; + n->base.paralleldmlsafety = $13; n->base.if_not_exists = true; /* FDW-specific data */ - n->servername = $14; - n->options = $15; + n->servername = $15; + n->options = $16; $$ = (Node *) n; } ; @@ -15553,6 +15579,7 @@ unreserved_keyword: | DICTIONARY | DISABLE_P | DISCARD + | DML | DOCUMENT_P | DOMAIN_P | DOUBLE_P @@ -16093,6 +16120,7 @@ bare_label_keyword: | DISABLE_P | DISCARD | DISTINCT + | DML | DO | DOCUMENT_P | DOMAIN_P diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 29702d6eab..9b8d6a049c 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1868,6 +1868,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->relkind = RELKIND_RELATION; relation->rd_rel->relnatts = (int16) natts; relation->rd_rel->relam = HEAP_TABLE_AM_OID; + relation->rd_rel->relparalleldml = PROPARALLEL_UNSAFE; /* * initialize attribute tuple form @@ -3337,7 +3338,8 @@ RelationBuildLocalRelation(const char *relname, bool shared_relation, bool mapped_relation, char relpersistence, - char relkind) + char relkind, + char relparalleldml) { Relation rel; MemoryContext oldcxt; @@ -3487,6 +3489,8 @@ RelationBuildLocalRelation(const char *relname, else rel->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING; + rel->rd_rel->relparalleldml = relparalleldml; + /* * Insert relation physical and logical identifiers (OIDs) into the right * places. For a mapped relation, we set relfilenode to zero and rely on diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 3cb3598f2b..5eb19ca07d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6291,6 +6291,7 @@ getTables(Archive *fout, int *numTables) int i_relpersistence; int i_relispopulated; int i_relreplident; + int i_relproparalleldml; int i_owning_tab; int i_owning_col; int i_reltablespace; @@ -6395,7 +6396,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "tc.relminmxid AS tminmxid, " "c.relpersistence, c.relispopulated, " - "c.relreplident, c.relpages, am.amname, " + "c.relreplident, c.relparalleldml, c.relpages, am.amname, " "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " "ELSE 0 END AS foreignserver, " @@ -6487,7 +6488,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "tc.relminmxid AS tminmxid, " "c.relpersistence, c.relispopulated, " - "c.relreplident, c.relpages, " + "c.relreplident, c.relparalleldml, c.relpages, " "NULL AS amname, " "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " @@ -6540,7 +6541,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "tc.relminmxid AS tminmxid, " "c.relpersistence, c.relispopulated, " - "c.relreplident, c.relpages, " + "c.relreplident, c.relparalleldml, c.relpages, " "NULL AS amname, " "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " @@ -6593,7 +6594,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "tc.relminmxid AS tminmxid, " "c.relpersistence, c.relispopulated, " - "'d' AS relreplident, c.relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, c.relpages, " "NULL AS amname, " "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " @@ -6646,7 +6647,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "0 AS tminmxid, " "c.relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, c.relpages, " "NULL AS amname, " "CASE WHEN c.relkind = 'f' THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " @@ -6697,7 +6698,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "0 AS tminmxid, " "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, c.relpages, " "NULL AS amname, " "NULL AS foreignserver, " "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " @@ -6745,7 +6746,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "0 AS tminmxid, " "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, c.relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6793,7 +6794,7 @@ getTables(Archive *fout, int *numTables) "tc.relfrozenxid AS tfrozenxid, " "0 AS tminmxid, " "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, c.relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, c.relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6840,7 +6841,7 @@ getTables(Archive *fout, int *numTables) "0 AS toid, " "0 AS tfrozenxid, 0 AS tminmxid," "'p' AS relpersistence, 't' as relispopulated, " - "'d' AS relreplident, relpages, " + "'d' AS relreplident, 'u' AS relparalleldml, relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6909,6 +6910,7 @@ getTables(Archive *fout, int *numTables) i_relpersistence = PQfnumber(res, "relpersistence"); i_relispopulated = PQfnumber(res, "relispopulated"); i_relreplident = PQfnumber(res, "relreplident"); + i_relproparalleldml = PQfnumber(res, "relparalleldml"); i_relpages = PQfnumber(res, "relpages"); i_foreignserver = PQfnumber(res, "foreignserver"); i_owning_tab = PQfnumber(res, "owning_tab"); @@ -6964,6 +6966,7 @@ getTables(Archive *fout, int *numTables) tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0); tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident)); + tblinfo[i].relparalleldml = *(PQgetvalue(res, i, i_relproparalleldml)); tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages)); tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid)); @@ -16542,6 +16545,32 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) } } + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || + tbinfo->relkind == RELKIND_FOREIGN_TABLE) + { + appendPQExpBuffer(q, "\nALTER %sTABLE %s PARALLEL ", + tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "", + qualrelname); + + switch (tbinfo->relparalleldml) + { + case 's': + appendPQExpBuffer(q, "SAFE;\n"); + break; + case 'r': + appendPQExpBuffer(q, "RESTRICTED;\n"); + break; + case 'u': + appendPQExpBuffer(q, "UNSAFE;\n"); + break; + default: + /* should not reach here */ + appendPQExpBuffer(q, "UNSAFE;\n"); + break; + } + } + if (tbinfo->forcerowsec) appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n", qualrelname); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 5340843081..c85113c8ed 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -268,6 +268,7 @@ typedef struct _tableInfo char relpersistence; /* relation persistence */ bool relispopulated; /* relation is populated */ char relreplident; /* replica identifier */ + char relparalleldml; /* parallel safety of dml on the relation */ char *reltablespace; /* relation tablespace */ char *reloptions; /* options specified by WITH (...) */ char *checkoption; /* WITH CHECK OPTION, if any */ diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 6ce480b49c..b59975919b 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -55,6 +55,7 @@ extern Relation heap_create(const char *relname, TupleDesc tupDesc, char relkind, char relpersistence, + char relparalleldml, bool shared_relation, bool mapped_relation, bool allow_system_table_mods, @@ -73,6 +74,7 @@ extern Oid heap_create_with_catalog(const char *relname, List *cooked_constraints, char relkind, char relpersistence, + char relparalleldml, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 3e37729436..af280b5095 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -116,6 +116,9 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat /* see REPLICA_IDENTITY_xxx constants */ char relreplident BKI_DEFAULT(n); + /* parallel safety of the dml on the relation */ + char relparalleldml BKI_DEFAULT(u); + /* is relation a partition? */ bool relispartition BKI_DEFAULT(f); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 615dfa26aa..2b3fd010d8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1934,7 +1934,8 @@ typedef enum AlterTableType AT_SetIdentity, /* SET identity column options */ AT_DropIdentity, /* DROP IDENTITY */ AT_AlterCollationRefreshVersion, /* ALTER COLLATION ... REFRESH VERSION */ - AT_ReAddStatistics /* internal to commands/tablecmds.c */ + AT_ReAddStatistics, /* internal to commands/tablecmds.c */ + AT_ParallelDMLSafety /* PARALLEL DML SAFE/RESTRICTED/UNSAFE */ } AlterTableType; typedef struct ReplicaIdentityStmt @@ -2159,6 +2160,7 @@ typedef struct CreateStmt OnCommitAction oncommit; /* what do we do at COMMIT? */ char *tablespacename; /* table space to use, or NULL */ char *accessMethod; /* table access method */ + char *paralleldmlsafety; /* parallel dml safety */ bool if_not_exists; /* just do nothing if it already exists? */ } CreateStmt; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 9ae851d847..6b532b034a 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -115,6 +115,7 @@ typedef struct IntoClause List *options; /* options from WITH clause */ OnCommitAction onCommit; /* what do we do at COMMIT? */ char *tableSpaceName; /* table space to use, or NULL */ + char *paralleldmlsafety; /* parallel dml safety */ Node *viewQuery; /* materialized view's SELECT query */ bool skipData; /* true for WITH NO DATA */ } IntoClause; diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index f836acf876..05222faccd 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -139,6 +139,7 @@ PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("distinct", DISTINCT, RESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("dml", DML, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("do", DO, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("document", DOCUMENT_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD, BARE_LABEL) diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 2fcdf79323..aa970375ae 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -106,7 +106,8 @@ extern Relation RelationBuildLocalRelation(const char *relname, bool shared_relation, bool mapped_relation, char relpersistence, - char relkind); + char relkind, + char relparalleldml); /* * Routines to manage assignment of new relfilenode to a relation diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c index 1bae1e5438..e1f5678eef 100644 --- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c +++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c @@ -276,6 +276,9 @@ get_altertable_subcmdtypes(PG_FUNCTION_ARGS) case AT_NoForceRowSecurity: strtype = "NO FORCE ROW SECURITY"; break; + case AT_ParallelDMLSafety: + strtype = "PARALLEL DML SAFETY"; + break; case AT_GenericOptions: strtype = "SET OPTIONS"; break; -- 2.18.4