From 01bdde01fb66e93928cb84b6aeee7dd31ea9ad83 Mon Sep 17 00:00:00 2001 From: Hou Zhijie Date: Tue, 3 Aug 2021 14:13:39 +0800 Subject: [PATCH] CREATE-ALTER-TABLE-PARALLEL-DML Enable users to declare a table's parallel data-modification safety (DEFAULT/SAFE/RESTRICTED/UNSAFE). Add a table property that represents parallel safety of a table for DML statement execution. It can be specified as follows: CREATE TABLE table_name PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE }; ALTER TABLE table_name PARALLEL DML { DEFAULT | UNSAFE | RESTRICTED | SAFE }; This property is recorded in pg_class's relparalleldml column as 'u', 'r', or 's' like pg_proc's proparallel and as 'd' if not set. The default is 'd'. If relparalleldml is specific(safe/restricted/unsafe), then the planner assumes that all of the table, its descendant partitions, and their ancillary objects have, at worst, the specified parallel safety. The user is responsible for its correctness. If relparalleldml is not set or set to DEFAULT, for non-partitioned table, planner will check the parallel safety automatically(see 0004 patch). But for partitioned table, planner will assume that the table is UNSAFE to be modified in parallel mode. --- 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 | 97 +++++++++++++++++++ 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/parser/gram.y | 73 ++++++++++---- src/backend/utils/cache/relcache.c | 6 +- src/bin/pg_dump/pg_dump.c | 50 ++++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 71 ++++++++++++-- src/include/catalog/heap.h | 2 + src/include/catalog/pg_class.h | 3 + src/include/catalog/pg_proc.h | 2 + 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 + 27 files changed, 302 insertions(+), 39 deletions(-) diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 5fcd004e1b..4712536088 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_DEFAULT, shared_relation, mapped_relation, true, @@ -231,6 +233,7 @@ Boot_CreateStmt: NIL, RELKIND_RELATION, RELPERSISTENCE_PERMANENT, + PROPARALLEL_DEFAULT, shared_relation, mapped_relation, ONCOMMIT_NOOP, diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 83746d3fd9..135df961c9 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -302,6 +302,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, @@ -404,7 +405,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. @@ -959,6 +961,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); @@ -1152,6 +1155,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, @@ -1299,6 +1303,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 26bfa74ce7..18f3a51686 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_DEFAULT, shared_relation, mapped_relation, allow_system_table_mods, diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 147b5abc19..b32d2d4132 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -251,6 +251,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 b3d8b6deb0..d1a7603d90 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -693,6 +693,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, 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 0982851715..7607b91ae8 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 72bfdc07a4..384770050a 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 fcd778c62a..5968252648 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" @@ -603,6 +604,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, static List *GetParentedForeignKeyRefs(Relation partition); static void ATDetachCheckNoForeignKeyRefs(Relation partition); static char GetAttributeCompression(Oid atttypid, char *compression); +static void ATExecParallelDMLSafety(Relation rel, Node *def); /* ---------------------------------------------------------------- @@ -648,6 +650,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, LOCKMODE parentLockmode; const char *accessMethod = NULL; Oid accessMethodId = InvalidOid; + char relparalleldml = PROPARALLEL_DEFAULT; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -926,6 +929,32 @@ 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 perform parallel data modification on relation \"%s\"", + relname), + errdetail_relkind_not_supported(relkind))); + + relparalleldml = PROPARALLEL_SAFE; + } + else if (strcmp(stmt->paralleldmlsafety, "restricted") == 0) + relparalleldml = PROPARALLEL_RESTRICTED; + else if (strcmp(stmt->paralleldmlsafety, "unsafe") == 0) + relparalleldml = PROPARALLEL_UNSAFE; + else if (strcmp(stmt->paralleldmlsafety, "default") == 0) + relparalleldml = PROPARALLEL_DEFAULT; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"parallel dml\" must be SAFE, RESTRICTED, UNSAFE or DEFAULT"))); + } + /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be @@ -944,6 +973,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, old_constraints), relkind, stmt->relation->relpersistence, + relparalleldml, false, false, stmt->oncommit, @@ -4187,6 +4217,7 @@ AlterTableGetLockLevel(List *cmds) case AT_SetIdentity: case AT_DropExpression: case AT_SetCompression: + case AT_ParallelDMLSafety: cmd_lockmode = AccessExclusiveLock; break; @@ -4737,6 +4768,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* No command-specific prep needed */ pass = AT_PASS_MISC; break; + case AT_ParallelDMLSafety: + ATSimplePermissions(cmd->subtype, 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); @@ -5142,6 +5178,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, case AT_DetachPartitionFinalize: ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name); break; + case AT_ParallelDMLSafety: + ATExecParallelDMLSafety(rel, cmd->def); + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); @@ -6113,6 +6152,8 @@ alter_table_type_to_string(AlterTableType cmdtype) return "ALTER COLUMN ... DROP IDENTITY"; case AT_ReAddStatistics: return NULL; /* not real grammar */ + case AT_ParallelDMLSafety: + return "PARALLEL DML SAFETY"; } return NULL; @@ -18773,3 +18814,59 @@ GetAttributeCompression(Oid atttypid, char *compression) return cmethod; } + +static void +ATExecParallelDMLSafety(Relation rel, Node *def) +{ + Relation pg_class; + Oid relid; + HeapTuple tuple; + char relparallel = PROPARALLEL_DEFAULT; + char *parallel = strVal(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 perform parallel data modification on relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); + + relparallel = PROPARALLEL_SAFE; + } + else if (strcmp(parallel, "restricted") == 0) + relparallel = PROPARALLEL_RESTRICTED; + else if (strcmp(parallel, "unsafe") == 0) + relparallel = PROPARALLEL_UNSAFE; + else if (strcmp(parallel, "default") == 0) + relparallel = PROPARALLEL_DEFAULT; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"parallel dml\" must be SAFE, RESTRICTED, UNSAFE or DEFAULT"))); + } + + 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 93eeff950b..a2f06c3e79 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2525,6 +2525,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 4df05a0b33..65f33a95d8 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 29020c908e..df41165c5f 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3534,6 +3534,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 8a1762000c..67b1966f18 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); @@ -1292,6 +1293,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 48202d2232..fdc5b63c28 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1107,6 +1107,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); } @@ -2714,6 +2715,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 77d082d8b4..ba725cb290 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/parser/gram.y b/src/backend/parser/gram.y index 39a2849eba..f74a7cac60 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -609,7 +609,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type PartitionBoundSpec %type hash_partbound %type hash_partbound_elem - +%type ParallelDMLSafety /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -654,7 +654,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 @@ -2691,6 +2691,21 @@ alter_table_cmd: n->subtype = AT_NoForceRowSecurity; $$ = (Node *)n; } + /* ALTER TABLE PARALLEL DML SAFE/RESTRICTED/UNSAFE/DEFAULT */ + | PARALLEL DML ColId + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ParallelDMLSafety; + n->def = (Node *)makeString($3); + $$ = (Node *)n; + } + | PARALLEL DML DEFAULT + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ParallelDMLSafety; + n->def = (Node *)makeString("default"); + $$ = (Node *)n; + } | alter_generic_options { AlterTableCmd *n = makeNode(AlterTableCmd); @@ -3276,7 +3291,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; @@ -3290,12 +3305,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; @@ -3309,12 +3325,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; @@ -3329,12 +3346,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; @@ -3349,12 +3367,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; @@ -3369,12 +3389,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; @@ -3389,6 +3411,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; } @@ -4089,6 +4112,11 @@ OptTableSpace: TABLESPACE name { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; +ParallelDMLSafety: PARALLEL DML name { $$ = $3; } + | PARALLEL DML DEFAULT { $$ = pstrdup("default"); } + | /*EMPTY*/ { $$ = NULL; } + ; + OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; } | /*EMPTY*/ { $$ = NULL; } ; @@ -4236,7 +4264,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; @@ -4245,6 +4273,7 @@ create_as_target: $$->options = $4; $$->onCommit = $5; $$->tableSpaceName = $6; + $$->paralleldmlsafety = $7; $$->viewQuery = NULL; $$->skipData = false; /* might get changed later */ } @@ -5024,7 +5053,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; @@ -5036,15 +5065,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; @@ -5056,15 +5086,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; @@ -5077,15 +5108,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; @@ -5098,10 +5130,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; } ; @@ -15547,6 +15580,7 @@ unreserved_keyword: | DICTIONARY | DISABLE_P | DISCARD + | DML | DOCUMENT_P | DOMAIN_P | DOUBLE_P @@ -16087,6 +16121,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 13d9994af3..70d8ecb1dd 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1873,6 +1873,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_DEFAULT; /* * initialize attribute tuple form @@ -3359,7 +3360,8 @@ RelationBuildLocalRelation(const char *relname, bool shared_relation, bool mapped_relation, char relpersistence, - char relkind) + char relkind, + char relparalleldml) { Relation rel; MemoryContext oldcxt; @@ -3509,6 +3511,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 90ac445bcd..5165202e84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6253,6 +6253,7 @@ getTables(Archive *fout, int *numTables) int i_relpersistence; int i_relispopulated; int i_relreplident; + int i_relparalleldml; int i_owning_tab; int i_owning_col; int i_reltablespace; @@ -6358,7 +6359,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, " @@ -6450,7 +6451,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) " @@ -6503,7 +6504,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) " @@ -6556,7 +6557,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, 'd' 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) " @@ -6609,7 +6610,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, 'd' 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) " @@ -6660,7 +6661,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, 'd' 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, " @@ -6708,7 +6709,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, 'd' AS relparalleldml, c.relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6756,7 +6757,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, 'd' AS relparalleldml, c.relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6803,7 +6804,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, 'd' AS relparalleldml, relpages, " "NULL AS amname, " "NULL AS foreignserver, " "NULL AS reloftype, " @@ -6872,6 +6873,7 @@ getTables(Archive *fout, int *numTables) i_relpersistence = PQfnumber(res, "relpersistence"); i_relispopulated = PQfnumber(res, "relispopulated"); i_relreplident = PQfnumber(res, "relreplident"); + i_relparalleldml = PQfnumber(res, "relparalleldml"); i_relpages = PQfnumber(res, "relpages"); i_foreignserver = PQfnumber(res, "foreignserver"); i_owning_tab = PQfnumber(res, "owning_tab"); @@ -6927,6 +6929,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_relparalleldml)); 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)); @@ -16555,6 +16558,35 @@ 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 DML ", + 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; + case 'd': + appendPQExpBuffer(q, "DEFAULT;\n"); + break; + default: + /* should not reach here */ + appendPQExpBuffer(q, "DEFAULT;\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 f5e170e0db..8175a0bc82 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -270,6 +270,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/bin/psql/describe.c b/src/bin/psql/describe.c index 8333558bda..f896fe1793 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -1656,6 +1656,7 @@ describeOneTableDetails(const char *schemaname, char *reloftype; char relpersistence; char relreplident; + char relparalleldml; char *relam; } tableinfo; bool show_column_details = false; @@ -1669,7 +1670,25 @@ describeOneTableDetails(const char *schemaname, initPQExpBuffer(&tmpbuf); /* Get general table info */ - if (pset.sversion >= 120000) + if (pset.sversion >= 150000) + { + printfPQExpBuffer(&buf, + "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " + "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, " + "false AS relhasoids, c.relispartition, %s, c.reltablespace, " + "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " + "c.relpersistence, c.relreplident, am.amname, c.relparalleldml\n" + "FROM pg_catalog.pg_class c\n " + "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" + "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n" + "WHERE c.oid = '%s';", + (verbose ? + "pg_catalog.array_to_string(c.reloptions || " + "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" + : "''"), + oid); + } + else if (pset.sversion >= 120000) { printfPQExpBuffer(&buf, "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " @@ -1853,6 +1872,8 @@ describeOneTableDetails(const char *schemaname, (char *) NULL : pg_strdup(PQgetvalue(res, 0, 14)); else tableinfo.relam = NULL; + tableinfo.relparalleldml = (pset.sversion >= 150000) ? + *(PQgetvalue(res, 0, 15)) : 0; PQclear(res); res = NULL; @@ -3630,6 +3651,21 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam); printTableAddFooter(&cont, buf.data); } + + if (verbose && + (tableinfo.relkind == RELKIND_RELATION || + tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_FOREIGN_TABLE) && + tableinfo.relparalleldml != 0) + { + printfPQExpBuffer(&buf, _("Parallel DML: %s"), + tableinfo.relparalleldml == 'd' ? "default" : + tableinfo.relparalleldml == 'u' ? "unsafe" : + tableinfo.relparalleldml == 'r' ? "restricted" : + tableinfo.relparalleldml == 's' ? "safe" : + "???"); + printTableAddFooter(&cont, buf.data); + } } /* reloptions, if verbose */ @@ -4005,7 +4041,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys PGresult *res; printQueryOpt myopt = pset.popt; int cols_so_far; - bool translate_columns[] = {false, false, true, false, false, false, false, false, false}; + bool translate_columns[] = {false, false, true, false, false, false, false, false, false, false}; /* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */ if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign)) @@ -4073,22 +4109,43 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("unlogged"), gettext_noop("Persistence")); translate_columns[cols_so_far] = true; + cols_so_far++; } - /* - * We don't bother to count cols_so_far below here, as there's no need - * to; this might change with future additions to the output columns. - */ - /* * Access methods exist for tables, materialized views and indexes. * This has been introduced in PostgreSQL 12 for tables. */ if (pset.sversion >= 120000 && !pset.hide_tableam && (showTables || showMatViews || showIndexes)) + { appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"", gettext_noop("Access method")); + cols_so_far++; + } + + /* + * Show whether the data in the relation is default('d') unsafe('u'), + * restricted('r'), or safe('s') can be modified in parallel mode. + * This has been introduced in PostgreSQL 15 for tables. + */ + if (pset.sversion >= 150000) + { + appendPQExpBuffer(&buf, + ",\n CASE c.relparalleldml WHEN 'd' THEN '%s' WHEN 'u' THEN '%s' WHEN 'r' THEN '%s' WHEN 's' THEN '%s' END as \"%s\"", + gettext_noop("default"), + gettext_noop("unsafe"), + gettext_noop("restricted"), + gettext_noop("safe"), + gettext_noop("Parallel DML")); + translate_columns[cols_so_far] = true; + } + + /* + * We don't bother to count cols_so_far below here, as there's no need + * to; this might change with future additions to the output columns. + */ /* * As of PostgreSQL 9.0, use pg_table_size() to show a more accurate 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 fef9945ed8..244eac6bd8 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(d); + /* is relation a partition? */ bool relispartition BKI_DEFAULT(f); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index b33b8b0134..cd52c0e254 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -171,6 +171,8 @@ DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index, 2691, ProcedureNameArgsNspI #define PROPARALLEL_RESTRICTED 'r' /* can run in parallel leader only */ #define PROPARALLEL_UNSAFE 'u' /* banned while in parallel mode */ +#define PROPARALLEL_DEFAULT 'd' /* only used for parallel dml safety */ + /* * Symbolic values for proargmodes column. Note that these must agree with * the FunctionParameterMode enum in parsenodes.h; we declare them here to diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e28248af32..0352e41c6e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1934,7 +1934,8 @@ typedef enum AlterTableType AT_AddIdentity, /* ADD IDENTITY */ AT_SetIdentity, /* SET identity column options */ AT_DropIdentity, /* DROP IDENTITY */ - AT_ReAddStatistics /* internal to commands/tablecmds.c */ + AT_ReAddStatistics, /* internal to commands/tablecmds.c */ + AT_ParallelDMLSafety /* PARALLEL DML SAFE/RESTRICTED/UNSAFE/DEFAULT */ } AlterTableType; typedef struct ReplicaIdentityStmt @@ -2180,6 +2181,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 c04282f91f..6e679d9f97 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 f772855ac6..5ea225ac2d 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -108,7 +108,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.27.0