From 5d8709c9c3bc9462b865196394cef8a8d29edb07 Mon Sep 17 00:00:00 2001 From: Ajin Cherian Date: Thu, 16 Feb 2023 01:04:53 -0500 Subject: [PATCH v71 1/8] Infrastructure to support DDL deparsing. Infrastructure to support DDL deparsing. 1) Some of the event trigger structures were moved from event_trigger.c to event_trigger.h as these will be required for publication event trigger creation. 2) Some of the prototypes and structures were moved from pg_publication.h to publicationcmds.h. This was because one of the later patch required the inclusion of pg_publication.h and these prototype had references to server header files. 3) Change the object identity to match with the syntax in objectaddress.c This is so that the deparse logic can use this identity directly in the deparse logic. --- src/backend/catalog/aclchk.c | 9 +- src/backend/commands/collationcmds.c | 21 +- src/backend/commands/event_trigger.c | 73 ++--- src/backend/commands/seclabel.c | 6 + src/backend/commands/sequence.c | 43 +++ src/backend/replication/pgoutput/pgoutput.c | 1 + src/backend/tcop/utility.c | 110 ++++++- src/backend/utils/adt/format_type.c | 4 +- src/backend/utils/adt/regproc.c | 53 ++++ src/backend/utils/adt/ruleutils.c | 324 +++++++++++++------- src/include/catalog/pg_publication.h | 17 +- src/include/commands/collationcmds.h | 3 +- src/include/commands/event_trigger.h | 41 +++ src/include/commands/publicationcmds.h | 14 + src/include/commands/sequence.h | 9 + src/include/tcop/deparse_utility.h | 10 +- src/include/tcop/utility.h | 2 + src/include/utils/acl.h | 2 + src/include/utils/aclchk_internal.h | 1 + src/include/utils/builtins.h | 2 + src/include/utils/rel.h | 2 + src/include/utils/ruleutils.h | 19 ++ src/tools/pgindent/typedefs.list | 1 + 23 files changed, 598 insertions(+), 169 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index c4232344aa..36a6677113 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -129,7 +129,6 @@ static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, AclMode *col_privileges, int num_col_privileges); static AclMode string_to_privilege(const char *privname); -static const char *privilege_to_string(AclMode privilege); static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, AclMode privileges, Oid objectId, Oid grantorId, @@ -385,11 +384,10 @@ ExecuteGrantStmt(GrantStmt *stmt) ListCell *cell; const char *errormsg; AclMode all_privileges; + Oid grantor = InvalidOid; if (stmt->grantor) { - Oid grantor; - grantor = get_rolespec_oid(stmt->grantor, false); /* @@ -408,6 +406,9 @@ ExecuteGrantStmt(GrantStmt *stmt) istmt.is_grant = stmt->is_grant; istmt.objtype = stmt->objtype; + /* Copy the grantor id needed for DDL deparsing of Grant */ + istmt.grantor_uid = grantor; + /* Collect the OIDs of the target objects */ switch (stmt->targtype) { @@ -2628,7 +2629,7 @@ string_to_privilege(const char *privname) return 0; /* appease compiler */ } -static const char * +const char * privilege_to_string(AclMode privilege) { switch (privilege) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index eb62d285ea..56b97d4370 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -50,9 +50,21 @@ typedef struct /* * CREATE COLLATION + * + * pstate: parse state. + * names: qualified collation names (a list of String). + * parameters: collation attributes (a list of DefElem). + * if_not_exists: if true, don't fail on duplicate name, just print a notice + * and return InvalidOid. + * from_existing_collid: output argument which, if not NULL, is set to the + * address of existing collation that was used to create in case of + * CREATE COLLATION any_name FROM existing_collation. + * + * If successful, returns the address of the new collation. */ ObjectAddress -DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists) +DefineCollation(ParseState *pstate, List *names, List *parameters, + bool if_not_exists, ObjectAddress *from_existing_collid) { char *collName; Oid collNamespace; @@ -139,6 +151,13 @@ DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_e if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for collation %u", collid); + /* + * Make from existing collationid available to callers for statements such as + * CREATE COLLATION any_name FROM any_name + */ + if (from_existing_collid && OidIsValid(collid)) + ObjectAddressSet(*from_existing_collid, CollationRelationId, collid); + collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider; collisdeterministic = ((Form_pg_collation) GETSTRUCT(tp))->collisdeterministic; collencoding = ((Form_pg_collation) GETSTRUCT(tp))->collencoding; diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index d4b00d1a82..b380e3939b 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -48,45 +48,7 @@ #include "utils/rel.h" #include "utils/syscache.h" -typedef struct EventTriggerQueryState -{ - /* memory context for this state's objects */ - MemoryContext cxt; - - /* sql_drop */ - slist_head SQLDropList; - bool in_sql_drop; - - /* table_rewrite */ - Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite - * event */ - int table_rewrite_reason; /* AT_REWRITE reason */ - - /* Support for command collection */ - bool commandCollectionInhibited; - CollectedCommand *currentCommand; - List *commandList; /* list of CollectedCommand; see - * deparse_utility.h */ - struct EventTriggerQueryState *previous; -} EventTriggerQueryState; - -static EventTriggerQueryState *currentEventTriggerState = NULL; - -/* Support for dropped objects */ -typedef struct SQLDropObject -{ - ObjectAddress address; - const char *schemaname; - const char *objname; - const char *objidentity; - const char *objecttype; - List *addrnames; - List *addrargs; - bool original; - bool normal; - bool istemp; - slist_node next; -} SQLDropObject; +EventTriggerQueryState *currentEventTriggerState = NULL; static void AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, @@ -1812,6 +1774,36 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt) MemoryContextSwitchTo(oldcxt); } +/* + * EventTriggerCollectSecLabel + * Save data about an SECURITY LABEL command being executed + */ +void +EventTriggerCollectSecLabel(ObjectAddress address, char *provider, + SecLabelStmt *stmt) +{ + MemoryContext oldcxt; + CollectedCommand *command; + + /* ignore if event trigger context not set, or collection disabled */ + if (!currentEventTriggerState || + currentEventTriggerState->commandCollectionInhibited) + return; + + oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt); + + command = palloc0(sizeof(CollectedCommand)); + command->type = SCT_SecurityLabel; + command->in_extension = creating_extension; + command->d.seclabel.address = address; + command->d.seclabel.provider = provider; + command->parsetree = (Node *) copyObject(stmt); + + currentEventTriggerState->commandList = + lappend(currentEventTriggerState->commandList, command); + MemoryContextSwitchTo(oldcxt); +} + /* * In a ddl_command_end event trigger, this function reports the DDL commands * being run. @@ -1863,6 +1855,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) case SCT_AlterOpFamily: case SCT_CreateOpClass: case SCT_AlterTSConfig: + case SCT_SecurityLabel: { char *identity; char *type; @@ -1880,6 +1873,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) addr = cmd->d.createopc.address; else if (cmd->type == SCT_AlterTSConfig) addr = cmd->d.atscfg.address; + else if (cmd->type == SCT_SecurityLabel) + addr = cmd->d.seclabel.address; /* * If an object was dropped in the same command we may end diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 7ff16e3276..7ee54cde8e 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -18,6 +18,7 @@ #include "catalog/indexing.h" #include "catalog/pg_seclabel.h" #include "catalog/pg_shseclabel.h" +#include "commands/event_trigger.h" #include "commands/seclabel.h" #include "miscadmin.h" #include "utils/builtins.h" @@ -213,6 +214,11 @@ ExecSecLabelStmt(SecLabelStmt *stmt) if (relation != NULL) relation_close(relation, NoLock); + /* Pass the info to event triggers about the SECURITY LABEL. */ + if (EventTriggerSupportsObjectType(stmt->objtype)) + EventTriggerCollectSecLabel(address, pstrdup(provider->provider_name), + stmt); + return address; } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index bfe279cddf..2969d3df01 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1708,6 +1708,49 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity) relation_close(tablerel, NoLock); } +/* + * Return sequence parameters, detailed + */ +Sequence_values * +get_sequence_values(Oid sequenceId) +{ + Buffer buf; + SeqTable elm; + Relation seqrel; + HeapTuple seqtuple; + HeapTupleData seqtupledata; + Form_pg_sequence seqform; + Form_pg_sequence_data seq; + Sequence_values *seqvalues; + + seqtuple = SearchSysCache1(SEQRELID, sequenceId); + if (!HeapTupleIsValid(seqtuple)) + elog(ERROR, "cache lookup failed for sequence %u", sequenceId); + seqform = (Form_pg_sequence) GETSTRUCT(seqtuple); + + ReleaseSysCache(seqtuple); + + /* Open and lock sequence */ + init_sequence(sequenceId, &elm, &seqrel); + + if (pg_class_aclcheck(sequenceId, GetUserId(), + ACL_SELECT | ACL_USAGE) != ACLCHECK_OK) + ereport(ERROR, + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel))); + + seq = read_seq_tuple(seqrel, &buf, &seqtupledata); + + seqvalues = (Sequence_values *) palloc(sizeof(Sequence_values)); + seqvalues->last_value = seq->last_value; + seqvalues->seqform = seqform; + + UnlockReleaseBuffer(buf); + relation_close(seqrel, NoLock); + + return seqvalues; +} /* * Return sequence parameters in a list of the form created by the parser. diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 98377c094b..12b5fd72f7 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -18,6 +18,7 @@ #include "catalog/pg_publication_rel.h" #include "catalog/pg_subscription.h" #include "commands/defrem.h" +#include "commands/publicationcmds.h" #include "commands/subscriptioncmds.h" #include "executor/executor.h" #include "fmgr.h" diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index c7d9d96b45..6fd3d471fd 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1446,7 +1446,8 @@ ProcessUtilitySlow(ParseState *pstate, address = DefineCollation(pstate, stmt->defnames, stmt->definition, - stmt->if_not_exists); + stmt->if_not_exists, + &secondaryObject); break; default: elog(ERROR, "unrecognized define stmt type: %d", @@ -1826,6 +1827,7 @@ ProcessUtilitySlow(ParseState *pstate, case T_SecLabelStmt: address = ExecSecLabelStmt((SecLabelStmt *) parsetree); + commandCollected = true; break; case T_CreateAmStmt: @@ -2196,6 +2198,112 @@ UtilityContainsQuery(Node *parsetree) } } +/* + * Return the given object type as a string. + * + * If isgrant is true, then this function is called while deparsing GRANT + * statement and some object names are replaced. + */ +const char * +stringify_objtype(ObjectType objtype, bool isgrant) +{ + switch (objtype) + { + case OBJECT_AGGREGATE: + return "AGGREGATE"; + case OBJECT_CAST: + return "CAST"; + case OBJECT_COLLATION: + return "COLLATION"; + case OBJECT_COLUMN: + return isgrant ? "TABLE" : "COLUMN"; + case OBJECT_CONVERSION: + return "CONVERSION"; + case OBJECT_DATABASE: + return "DATABASE"; + case OBJECT_DOMAIN: + return "DOMAIN"; + case OBJECT_EVENT_TRIGGER: + return "EVENT TRIGGER"; + case OBJECT_EXTENSION: + return "EXTENSION"; + case OBJECT_FDW: + return "FOREIGN DATA WRAPPER"; + case OBJECT_FOREIGN_SERVER: + return isgrant ? "FOREIGN SERVER" : "SERVER"; + case OBJECT_FOREIGN_TABLE: + return "FOREIGN TABLE"; + case OBJECT_FUNCTION: + return "FUNCTION"; + case OBJECT_INDEX: + return "INDEX"; + case OBJECT_LANGUAGE: + return "LANGUAGE"; + case OBJECT_LARGEOBJECT: + return "LARGE OBJECT"; + case OBJECT_MATVIEW: + return "MATERIALIZED VIEW"; + case OBJECT_OPCLASS: + return "OPERATOR CLASS"; + case OBJECT_OPERATOR: + return "OPERATOR"; + case OBJECT_OPFAMILY: + return "OPERATOR FAMILY"; + case OBJECT_POLICY: + return "POLICY"; + case OBJECT_PROCEDURE: + return "PROCEDURE"; + case OBJECT_ROLE: + return "ROLE"; + case OBJECT_ROUTINE: + return "ROUTINE"; + case OBJECT_RULE: + return "RULE"; + case OBJECT_SCHEMA: + return "SCHEMA"; + case OBJECT_SEQUENCE: + return "SEQUENCE"; + case OBJECT_STATISTIC_EXT: + return "STATISTICS"; + case OBJECT_TABLE: + return "TABLE"; + case OBJECT_TABLESPACE: + return "TABLESPACE"; + case OBJECT_TRIGGER: + return "TRIGGER"; + case OBJECT_TSCONFIGURATION: + return "TEXT SEARCH CONFIGURATION"; + case OBJECT_TSDICTIONARY: + return "TEXT SEARCH DICTIONARY"; + case OBJECT_TSPARSER: + return "TEXT SEARCH PARSER"; + case OBJECT_TSTEMPLATE: + return "TEXT SEARCH TEMPLATE"; + case OBJECT_TYPE: + return "TYPE"; + case OBJECT_USER_MAPPING: + return "USER MAPPING"; + case OBJECT_VIEW: + return "VIEW"; + case OBJECT_ACCESS_METHOD: + case OBJECT_AMOP: + case OBJECT_AMPROC: + case OBJECT_ATTRIBUTE: + case OBJECT_DEFAULT: + case OBJECT_DEFACL: + case OBJECT_DOMCONSTRAINT: + case OBJECT_PARAMETER_ACL: + case OBJECT_PUBLICATION: + case OBJECT_PUBLICATION_NAMESPACE: + case OBJECT_PUBLICATION_REL: + case OBJECT_SUBSCRIPTION: + case OBJECT_TABCONSTRAINT: + case OBJECT_TRANSFORM: + elog(ERROR, "unsupported object type %d", objtype); + } + + return "???"; /* keep compiler quiet */ +} /* * AlterObjectTypeCommandTag diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 12402a0637..7b476adb23 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -27,8 +27,6 @@ #include "utils/numeric.h" #include "utils/syscache.h" -static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); - /* * SQL function: format_type(type_oid, typemod) @@ -363,7 +361,7 @@ format_type_with_typemod(Oid type_oid, int32 typemod) /* * Add typmod decoration to the basic type name */ -static char * +char * printTypmod(const char *typname, int32 typmod, Oid typmodout) { char *res; diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 296930eb3b..ab1af55c04 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -49,6 +49,8 @@ static bool parseNameAndArgTypes(const char *string, bool allowNone, List **names, int *nargs, Oid *argtypes, Node *escontext); +static void format_procedure_args_internal(Form_pg_proc procform, + StringInfo buf, bool force_qualify); /***************************************************************************** * USER I/O ROUTINES * @@ -307,6 +309,29 @@ format_procedure_qualified(Oid procedure_oid) return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY); } +/* + * format_procedure_args - converts proc OID to "(args)" + */ +char * +format_procedure_args(Oid procedure_oid, bool force_qualify) +{ + StringInfoData buf; + HeapTuple proctup; + Form_pg_proc procform; + + proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid)); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for procedure %u", procedure_oid); + procform = (Form_pg_proc) GETSTRUCT(proctup); + + initStringInfo(&buf); + format_procedure_args_internal(procform, &buf, force_qualify); + + ReleaseSysCache(proctup); + + return buf.data; +} + /* * format_procedure_extended - converts procedure OID to "pro_name(args)" * @@ -2016,3 +2041,31 @@ parseNameAndArgTypes(const char *string, bool allowNone, List **names, return true; } + +/* + * Append the parenthesized arguments of the given pg_proc row into the output + * buffer. force_qualify indicates whether to schema-qualify type names + * regardless of visibility. + */ +static void +format_procedure_args_internal(Form_pg_proc procform, StringInfo buf, + bool force_qualify) +{ + int i; + char* (*func[2])(Oid) = {format_type_be, format_type_be_qualified}; + + appendStringInfoChar(buf, '('); + for (i = 0; i < procform->pronargs; i++) + { + Oid thisargtype = procform->proargtypes.values[i]; + char *argtype; + + if (i > 0) + appendStringInfoChar(buf, ','); + + argtype = func[force_qualify](thisargtype); + appendStringInfoString(buf, argtype); + pfree(argtype); + } + appendStringInfoChar(buf, ')'); +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9ac42efdbc..7c15fa3655 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -35,6 +35,7 @@ #include "catalog/pg_operator.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" +#include "catalog/pg_rewrite.h" #include "catalog/pg_statistic_ext.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -341,6 +342,8 @@ static char *pg_get_triggerdef_worker(Oid trigid, bool pretty); static int decompile_column_index_array(Datum column_index_array, Oid relId, StringInfo buf); static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags); +static void pg_get_rule_whereclause(char *qualstr, Query *query, + StringInfo buf, int prettyFlags); static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool keysOnly, @@ -358,7 +361,6 @@ static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults); static void print_function_rettype(StringInfo buf, HeapTuple proctup); static void print_function_trftypes(StringInfo buf, HeapTuple proctup); -static void print_function_sqlbody(StringInfo buf, HeapTuple proctup); static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used); static void set_deparse_for_query(deparse_namespace *dpns, Query *query, @@ -482,22 +484,15 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc, deparse_context *context); static void get_tablesample_def(TableSampleClause *tablesample, deparse_context *context); -static void get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf); static Node *processIndirection(Node *node, deparse_context *context); static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context); static char *get_relation_name(Oid relid); static char *generate_relation_name(Oid relid, List *namespaces); static char *generate_qualified_relation_name(Oid relid); -static char *generate_function_name(Oid funcid, int nargs, - List *argnames, Oid *argtypes, - bool has_variadic, bool *use_variadic_p, - ParseExprKind special_exprkind); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static void add_cast_to(StringInfo buf, Oid typid); static char *generate_qualified_type_name(Oid typid); static text *string_to_text(char *str); -static char *flatten_reloptions(Oid relid); static void get_reloptions(StringInfo buf, Datum reloptions); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -545,6 +540,80 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(string_to_text(res)); } +/* + * Given a pair of Datum corresponding to a rule's pg_rewrite.ev_qual and + * ev_action columns, return their text representation; ev_qual as a single + * string in whereClause and ev_action as a List of strings (which might be + * NIL, signalling NOTHING) in actions. + */ +void +pg_get_ruledef_detailed(Datum ev_qual, Datum ev_action, + char **whereClause, List **actions) +{ + int prettyFlags = 0; + char *qualstr = TextDatumGetCString(ev_qual); + char *actionstr = TextDatumGetCString(ev_action); + List *actionNodeList = (List *) stringToNode(actionstr); + StringInfoData buf; + + *whereClause = NULL; + *actions = NIL; + initStringInfo(&buf); + + if (strcmp(qualstr, "<>") != 0) + { + Query *query = (Query *) linitial(actionNodeList); + + pg_get_rule_whereclause(qualstr, query, &buf, 0); + *whereClause = pstrdup(buf.data); + } + + if (list_length(actionNodeList) > 0) + { + ListCell *cell; + + foreach(cell, actionNodeList) + { + Query *query = (Query *) lfirst(cell); + + resetStringInfo(&buf); + get_query_def(query, &buf, NIL, NULL, true, + prettyFlags, WRAP_COLUMN_DEFAULT, 0); + *actions = lappend(*actions, pstrdup(buf.data)); + } + } +} + +/* + * To get the rewrite rule of a view when the CREATE VIEW command execution is + * still in progress: we search the system cache RULERELNAME to get the rewrite + * rule of the view as opposed to querying pg_rewrite as in pg_get_viewdef_worker(), + * which will return empty result. + */ +char * +pg_get_viewdef_string(Oid viewoid) +{ + StringInfoData buf; + Relation pg_rewrite; + HeapTuple ruletup; + TupleDesc rulettc; + + initStringInfo(&buf); + pg_rewrite = table_open(RewriteRelationId, AccessShareLock); + + ruletup = SearchSysCache2(RULERELNAME, + ObjectIdGetDatum(viewoid), + PointerGetDatum(ViewSelectRuleName)); + if (!HeapTupleIsValid(ruletup)) + elog(ERROR, "cache lookup failed for rewrite rule for view with OID %u", viewoid); + + rulettc = pg_rewrite->rd_att; + make_viewdef(&buf, ruletup, rulettc, 0, WRAP_COLUMN_DEFAULT); + ReleaseSysCache(ruletup); + table_close(pg_rewrite, AccessShareLock); + + return buf.data; +} static char * pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) @@ -1015,65 +1084,12 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) if (!isnull) { Node *qual; - char relkind; - deparse_context context; - deparse_namespace dpns; - RangeTblEntry *oldrte; - RangeTblEntry *newrte; - - appendStringInfoString(&buf, "WHEN ("); + char *qualstr; qual = stringToNode(TextDatumGetCString(value)); + qualstr = pg_get_trigger_whenclause(trigrec, qual, pretty); - relkind = get_rel_relkind(trigrec->tgrelid); - - /* Build minimal OLD and NEW RTEs for the rel */ - oldrte = makeNode(RangeTblEntry); - oldrte->rtekind = RTE_RELATION; - oldrte->relid = trigrec->tgrelid; - oldrte->relkind = relkind; - oldrte->rellockmode = AccessShareLock; - oldrte->alias = makeAlias("old", NIL); - oldrte->eref = oldrte->alias; - oldrte->lateral = false; - oldrte->inh = false; - oldrte->inFromCl = true; - - newrte = makeNode(RangeTblEntry); - newrte->rtekind = RTE_RELATION; - newrte->relid = trigrec->tgrelid; - newrte->relkind = relkind; - newrte->rellockmode = AccessShareLock; - newrte->alias = makeAlias("new", NIL); - newrte->eref = newrte->alias; - newrte->lateral = false; - newrte->inh = false; - newrte->inFromCl = true; - - /* Build two-element rtable */ - memset(&dpns, 0, sizeof(dpns)); - dpns.rtable = list_make2(oldrte, newrte); - dpns.subplans = NIL; - dpns.ctes = NIL; - dpns.appendrels = NULL; - set_rtable_names(&dpns, NIL, NULL); - set_simple_column_names(&dpns); - - /* Set up context with one-deep namespace stack */ - context.buf = &buf; - context.namespaces = list_make1(&dpns); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = true; - context.prettyFlags = GET_PRETTY_FLAGS(pretty); - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = PRETTYINDENT_STD; - context.special_exprkind = EXPR_KIND_NONE; - context.appendparents = NULL; - - get_rule_expr(qual, &context, false); - - appendStringInfoString(&buf, ") "); + appendStringInfo(&buf, "WHEN (%s) ", qualstr); } appendStringInfo(&buf, "EXECUTE FUNCTION %s(", @@ -1114,6 +1130,74 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) return buf.data; } +/* + * Pass back the TriggerWhen clause of a trigger given the pg_trigger record and + * the expression tree (in nodeToString() representation) from pg_trigger.tgqual + * for the trigger's WHEN condition. + */ +char * +pg_get_trigger_whenclause(Form_pg_trigger trigrec, Node *whenClause, bool pretty) +{ + StringInfoData buf; + char relkind; + deparse_context context; + deparse_namespace dpns; + RangeTblEntry *oldrte; + RangeTblEntry *newrte; + + initStringInfo(&buf); + + relkind = get_rel_relkind(trigrec->tgrelid); + + /* Build minimal OLD and NEW RTEs for the rel */ + oldrte = makeNode(RangeTblEntry); + oldrte->rtekind = RTE_RELATION; + oldrte->relid = trigrec->tgrelid; + oldrte->relkind = relkind; + oldrte->rellockmode = AccessShareLock; + oldrte->alias = makeAlias("old", NIL); + oldrte->eref = oldrte->alias; + oldrte->lateral = false; + oldrte->inh = false; + oldrte->inFromCl = true; + + newrte = makeNode(RangeTblEntry); + newrte->rtekind = RTE_RELATION; + newrte->relid = trigrec->tgrelid; + newrte->relkind = relkind; + newrte->rellockmode = AccessShareLock; + newrte->alias = makeAlias("new", NIL); + newrte->eref = newrte->alias; + newrte->lateral = false; + newrte->inh = false; + newrte->inFromCl = true; + + /* Build two-element rtable */ + memset(&dpns, 0, sizeof(dpns)); + dpns.rtable = list_make2(oldrte, newrte); + dpns.subplans = NIL; + dpns.ctes = NIL; + dpns.appendrels = NULL; + set_rtable_names(&dpns, NIL, NULL); + set_simple_column_names(&dpns); + + /* Set up context with one-deep namespace stack */ + context.buf = &buf; + context.namespaces = list_make1(&dpns); + context.windowClause = NIL; + context.windowTList = NIL; + context.varprefix = true; + context.prettyFlags = GET_PRETTY_FLAGS(pretty); + context.wrapColumn = WRAP_COLUMN_DEFAULT; + context.indentLevel = PRETTYINDENT_STD; + context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; + + get_rule_expr(whenClause, &context, false); + + return buf.data; +} + /* ---------- * pg_get_indexdef - Get the definition of an index * @@ -1880,6 +1964,14 @@ pg_get_partkeydef_columns(Oid relid, bool pretty) return pg_get_partkeydef_worker(relid, prettyFlags, true, false); } +/* Internal version that reports the full partition key definition */ +char * +pg_get_partkeydef_string(Oid relid) +{ + return pg_get_partkeydef_worker(relid, GET_PRETTY_FLAGS(false), false, + false); +} + /* * Internal workhorse to decompile a partition key definition. */ @@ -2131,6 +2223,16 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(string_to_text(res)); } +/* + * Internal version that returns the definition of a CONSTRAINT command + */ +char * +pg_get_constraintdef_string(Oid constraintId) +{ + return pg_get_constraintdef_worker(constraintId, false, + GET_PRETTY_FLAGS(false), false); +} + /* * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command */ @@ -3501,7 +3603,12 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(string_to_text(str)); } -static void +/* + * Produce the formatted SQL body (not the whole function definition) + * of a function given the pg_proc tuple. Save the formatted SQL in the + * given StringInfo. + */ +void print_function_sqlbody(StringInfo buf, HeapTuple proctup) { int numargs; @@ -5248,48 +5355,18 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, /* If the rule has an event qualification, add it */ if (strcmp(ev_qual, "<>") != 0) { - Node *qual; - Query *query; - deparse_context context; - deparse_namespace dpns; - - if (prettyFlags & PRETTYFLAG_INDENT) - appendStringInfoString(buf, "\n "); - appendStringInfoString(buf, " WHERE "); - - qual = stringToNode(ev_qual); - /* * We need to make a context for recognizing any Vars in the qual * (which can only be references to OLD and NEW). Use the rtable of * the first query in the action list for this purpose. */ - query = (Query *) linitial(actions); + Query *query = (Query *) linitial(actions); - /* - * If the action is INSERT...SELECT, OLD/NEW have been pushed down - * into the SELECT, and that's what we need to look at. (Ugly kluge - * ... try to fix this when we redesign querytrees.) - */ - query = getInsertSelectQuery(query, NULL); - - /* Must acquire locks right away; see notes in get_query_def() */ - AcquireRewriteLocks(query, false, false); - - context.buf = buf; - context.namespaces = list_make1(&dpns); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = (list_length(query->rtable) != 1); - context.prettyFlags = prettyFlags; - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = PRETTYINDENT_STD; - context.special_exprkind = EXPR_KIND_NONE; - context.appendparents = NULL; - - set_deparse_for_query(&dpns, query, NIL); + if (prettyFlags & PRETTYFLAG_INDENT) + appendStringInfoString(buf, "\n "); + appendStringInfoString(buf, " WHERE "); - get_rule_expr(qual, &context, false); + pg_get_rule_whereclause(ev_qual, query, buf, prettyFlags); } appendStringInfoString(buf, " DO "); @@ -5330,6 +5407,47 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, table_close(ev_relation, AccessShareLock); } +/* + * Given a string corresponding to a rule's pg_rewrite.ev_qual and a query + * parsetree, append ev_qual's text representation into the output buf. + * + * Tries to pretty up the output according to prettyFlags. + */ +static void +pg_get_rule_whereclause(char *qualstr, Query *query, StringInfo buf, + int prettyFlags) +{ + Node *qual; + deparse_context context; + deparse_namespace dpns; + + qual = stringToNode(qualstr); + + /* + * If the action is INSERT...SELECT, OLD/NEW have been pushed down + * into the SELECT, and that's what we need to look at. (Ugly kluge + * ... try to fix this when we redesign querytrees.) + */ + query = getInsertSelectQuery(query, NULL); + + /* Must acquire locks right away; see notes in get_query_def() */ + AcquireRewriteLocks(query, false, false); + + context.buf = buf; + context.namespaces = list_make1(&dpns); + context.windowClause = NIL; + context.windowTList = NIL; + context.varprefix = (list_length(query->rtable) != 1); + context.prettyFlags = prettyFlags; + context.wrapColumn = WRAP_COLUMN_DEFAULT; + context.indentLevel = PRETTYINDENT_STD; + context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; + + set_deparse_for_query(&dpns, query, NIL); + + get_rule_expr(qual, &context, false); +} /* ---------- * make_viewdef - reconstruct the SELECT part of a @@ -11357,7 +11475,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context) * actual_datatype. (If you don't want this behavior, just pass * InvalidOid for actual_datatype.) */ -static void +void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf) { @@ -11751,7 +11869,7 @@ generate_qualified_relation_name(Oid relid) * * The result includes all necessary quoting and schema-prefixing. */ -static char * +char * generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, bool has_variadic, bool *use_variadic_p, ParseExprKind special_exprkind) @@ -12137,7 +12255,7 @@ get_reloptions(StringInfo buf, Datum reloptions) /* * Generate a C string representing a relation's reloptions, or NULL if none. */ -static char * +char * flatten_reloptions(Oid relid) { char *result = NULL; diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h index 6ecaa2a01e..715f2a2e36 100644 --- a/src/include/catalog/pg_publication.h +++ b/src/include/catalog/pg_publication.h @@ -18,8 +18,8 @@ #define PG_PUBLICATION_H #include "catalog/genbki.h" -#include "catalog/objectaddress.h" #include "catalog/pg_publication_d.h" +#include "nodes/pg_list.h" /* ---------------- * pg_publication definition. cpp turns this into @@ -103,12 +103,6 @@ typedef struct Publication PublicationActions pubactions; } Publication; -typedef struct PublicationRelInfo -{ - Relation relation; - Node *whereClause; - List *columns; -} PublicationRelInfo; extern Publication *GetPublication(Oid pubid); extern Publication *GetPublicationByName(const char *pubname, bool missing_ok); @@ -144,15 +138,6 @@ extern List *GetPubPartitionOptionRelations(List *result, Oid relid); extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level); - -extern bool is_publishable_relation(Relation rel); extern bool is_schema_publication(Oid pubid); -extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri, - bool if_not_exists); -extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid, - bool if_not_exists); - -extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, - MemoryContext mcxt); #endif /* PG_PUBLICATION_H */ diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h index b76c7b3dc3..53c4a1a8c2 100644 --- a/src/include/commands/collationcmds.h +++ b/src/include/commands/collationcmds.h @@ -18,7 +18,8 @@ #include "catalog/objectaddress.h" #include "parser/parse_node.h" -extern ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists); +extern ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, + bool if_not_exists, ObjectAddress *from_collid); extern void IsThereCollationInNamespace(const char *collname, Oid nspOid); extern ObjectAddress AlterCollation(AlterCollationStmt *stmt); diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index 5ed6ece555..a97e8b7df8 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -16,6 +16,7 @@ #include "catalog/dependency.h" #include "catalog/objectaddress.h" #include "catalog/pg_event_trigger.h" +#include "lib/ilist.h" #include "nodes/parsenodes.h" #include "tcop/cmdtag.h" #include "tcop/deparse_utility.h" @@ -29,6 +30,44 @@ typedef struct EventTriggerData CommandTag tag; } EventTriggerData; +typedef struct EventTriggerQueryState +{ + /* memory context for this state's objects */ + MemoryContext cxt; + + /* sql_drop */ + slist_head SQLDropList; + bool in_sql_drop; + + /* table_rewrite */ + Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite + * event */ + int table_rewrite_reason; /* AT_REWRITE reason */ + + /* Support for command collection */ + bool commandCollectionInhibited; + CollectedCommand *currentCommand; + List *commandList; /* list of CollectedCommand; see + * deparse_utility.h */ + struct EventTriggerQueryState *previous; +} EventTriggerQueryState; + +/* Support for dropped objects */ +typedef struct SQLDropObject +{ + ObjectAddress address; + const char *schemaname; + const char *objname; + const char *objidentity; + const char *objecttype; + List *addrnames; + List *addrargs; + bool original; + bool normal; + bool istemp; + slist_node next; +} SQLDropObject; + #define AT_REWRITE_ALTER_PERSISTENCE 0x01 #define AT_REWRITE_DEFAULT_VAL 0x02 #define AT_REWRITE_COLUMN_REWRITE 0x04 @@ -84,5 +123,7 @@ extern void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, extern void EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId, Oid *dictIds, int ndicts); extern void EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt); +extern void EventTriggerCollectSecLabel(ObjectAddress address, char *provider, + SecLabelStmt *stmt); #endif /* EVENT_TRIGGER_H */ diff --git a/src/include/commands/publicationcmds.h b/src/include/commands/publicationcmds.h index 70d5e3680a..a3fca9e64c 100644 --- a/src/include/commands/publicationcmds.h +++ b/src/include/commands/publicationcmds.h @@ -22,6 +22,13 @@ /* Same as MAXNUMMESSAGES in sinvaladt.c */ #define MAX_RELCACHE_INVAL_MSGS 4096 +typedef struct PublicationRelInfo +{ + Relation relation; + Node *whereClause; + List *columns; +} PublicationRelInfo; + extern ObjectAddress CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt); extern void AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt); extern void RemovePublicationById(Oid pubid); @@ -35,5 +42,12 @@ extern bool pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors, bool pubviaroot); extern bool pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestors, bool pubviaroot); +extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, + MemoryContext mcxt); +extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri, + bool if_not_exists); +extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid, + bool if_not_exists); +extern bool is_publishable_relation(Relation rel); #endif /* PUBLICATIONCMDS_H */ diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 7db7b3da7b..c0a39596ac 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -15,6 +15,7 @@ #include "access/xlogreader.h" #include "catalog/objectaddress.h" +#include "catalog/pg_sequence.h" #include "fmgr.h" #include "lib/stringinfo.h" #include "nodes/parsenodes.h" @@ -51,9 +52,17 @@ typedef struct xl_seq_rec /* SEQUENCE TUPLE DATA FOLLOWS AT THE END */ } xl_seq_rec; +/* Information needed to define a sequence. */ +typedef struct Sequence_values +{ + Form_pg_sequence seqform; + int64 last_value; +} Sequence_values; + extern int64 nextval_internal(Oid relid, bool check_permissions); extern Datum nextval(PG_FUNCTION_ARGS); extern List *sequence_options(Oid relid); +extern Sequence_values *get_sequence_values(Oid sequenceId); extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *seq); extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt); diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h index b585810b9a..a1e06dab52 100644 --- a/src/include/tcop/deparse_utility.h +++ b/src/include/tcop/deparse_utility.h @@ -29,7 +29,8 @@ typedef enum CollectedCommandType SCT_AlterOpFamily, SCT_AlterDefaultPrivileges, SCT_CreateOpClass, - SCT_AlterTSConfig + SCT_AlterTSConfig, + SCT_SecurityLabel } CollectedCommandType; /* @@ -100,6 +101,13 @@ typedef struct CollectedCommand { ObjectType objtype; } defprivs; + + /* SECURITY LABEL */ + struct + { + ObjectAddress address; + char *provider; + } seclabel; } d; struct CollectedCommand *parent; /* when nested */ diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h index 59e64aea07..a68ce3d336 100644 --- a/src/include/tcop/utility.h +++ b/src/include/tcop/utility.h @@ -99,6 +99,8 @@ extern Query *UtilityContainsQuery(Node *parsetree); extern CommandTag CreateCommandTag(Node *parsetree); +extern const char *stringify_objtype(ObjectType objtype, bool isgrant); + static inline const char * CreateCommandName(Node *parsetree) { diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index f8e1238fa2..f05578d322 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -209,6 +209,8 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how); extern int aclmembers(const Acl *acl, Oid **roleids); +extern const char *privilege_to_string(AclMode privilege); + extern bool has_privs_of_role(Oid member, Oid role); extern bool member_can_set_role(Oid member, Oid role); extern void check_can_set_role(Oid member, Oid role); diff --git a/src/include/utils/aclchk_internal.h b/src/include/utils/aclchk_internal.h index 55af624fb3..946545f53f 100644 --- a/src/include/utils/aclchk_internal.h +++ b/src/include/utils/aclchk_internal.h @@ -39,6 +39,7 @@ typedef struct List *grantees; bool grant_option; DropBehavior behavior; + Oid grantor_uid; } InternalGrant; diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2f8b46d6da..48b8bfd79a 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -123,10 +123,12 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS); #define FORMAT_TYPE_FORCE_QUALIFY 0x04 /* force qualification of type */ #define FORMAT_TYPE_INVALID_AS_NULL 0x08 /* NULL if undefined */ extern char *format_type_extended(Oid type_oid, int32 typemod, bits16 flags); +extern char *format_procedure_args(Oid procedure_oid, bool force_qualify); extern char *format_type_be(Oid type_oid); extern char *format_type_be_qualified(Oid type_oid); extern char *format_type_with_typemod(Oid type_oid, int32 typemod); +extern char *printTypmod(const char *typname, int32 typmod, Oid typmodout); extern int32 type_maximum_size(Oid type_oid, int32 typemod); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 67f994cb3e..22ca7a80a6 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -16,10 +16,12 @@ #include "access/tupdesc.h" #include "access/xlog.h" +#include "catalog/objectaddress.h" #include "catalog/pg_class.h" #include "catalog/pg_index.h" #include "catalog/pg_publication.h" #include "nodes/bitmapset.h" +#include "nodes/lockoptions.h" #include "partitioning/partdefs.h" #include "rewrite/prs2lock.h" #include "storage/block.h" diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h index 1a42d9f39b..b6e20e7ee4 100644 --- a/src/include/utils/ruleutils.h +++ b/src/include/utils/ruleutils.h @@ -13,9 +13,12 @@ #ifndef RULEUTILS_H #define RULEUTILS_H +#include "access/htup.h" +#include "catalog/pg_trigger.h" #include "nodes/nodes.h" #include "nodes/parsenodes.h" #include "nodes/pg_list.h" +#include "parser/parse_node.h" struct Plan; /* avoid including plannodes.h here */ struct PlannedStmt; @@ -23,12 +26,20 @@ struct PlannedStmt; extern char *pg_get_indexdef_string(Oid indexrelid); extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty); +extern char *pg_get_trigger_whenclause(Form_pg_trigger trigrec, + Node *whenClause, bool pretty); extern char *pg_get_querydef(Query *query, bool pretty); +extern char *pg_get_viewdef_string(Oid viewoid); extern char *pg_get_partkeydef_columns(Oid relid, bool pretty); +extern char *pg_get_partkeydef_string(Oid relid); extern char *pg_get_partconstrdef_string(Oid partitionId, char *aliasname); extern char *pg_get_constraintdef_command(Oid constraintId); +extern char *pg_get_constraintdef_string(Oid constraintId); +extern void pg_get_ruledef_detailed(Datum ev_qual, Datum ev_action, + char **whereClause, List **actions); + extern char *deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit); extern List *deparse_context_for(const char *aliasname, Oid relid); @@ -40,8 +51,16 @@ extern List *select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used); extern char *generate_collation_name(Oid collid); extern char *generate_opclass_name(Oid opclass); +extern char *generate_function_name(Oid funcid, int nargs, List *argnames, + Oid *argtypes, bool has_variadic, + bool *use_variadic_p, + ParseExprKind special_exprkind); extern char *get_range_partbound_string(List *bound_datums); +extern void get_opclass_name(Oid opclass, Oid actual_datatype, + StringInfo buf); +extern char *flatten_reloptions(Oid relid); extern char *pg_get_statisticsobjdef_string(Oid statextid); +extern void print_function_sqlbody(StringInfo buf, HeapTuple proctup); #endif /* RULEUTILS_H */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 22ea42c16b..238458c8db 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2454,6 +2454,7 @@ SeqScan SeqScanState SeqTable SeqTableData +Sequence_values SerCommitSeqNo SerialControl SerializableXactHandle -- 2.31.1