LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 665 788 84.4 %
Date: 2025-09-18 21:17:27 Functions: 44 46 95.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * event_trigger.c
       4             :  *    PostgreSQL EVENT TRIGGER support code.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/commands/event_trigger.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/heapam.h"
      17             : #include "access/htup_details.h"
      18             : #include "access/table.h"
      19             : #include "access/xact.h"
      20             : #include "catalog/catalog.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_attrdef.h"
      25             : #include "catalog/pg_authid.h"
      26             : #include "catalog/pg_auth_members.h"
      27             : #include "catalog/pg_database.h"
      28             : #include "catalog/pg_event_trigger.h"
      29             : #include "catalog/pg_namespace.h"
      30             : #include "catalog/pg_opclass.h"
      31             : #include "catalog/pg_opfamily.h"
      32             : #include "catalog/pg_parameter_acl.h"
      33             : #include "catalog/pg_policy.h"
      34             : #include "catalog/pg_proc.h"
      35             : #include "catalog/pg_tablespace.h"
      36             : #include "catalog/pg_trigger.h"
      37             : #include "catalog/pg_ts_config.h"
      38             : #include "catalog/pg_type.h"
      39             : #include "commands/event_trigger.h"
      40             : #include "commands/extension.h"
      41             : #include "commands/trigger.h"
      42             : #include "funcapi.h"
      43             : #include "lib/ilist.h"
      44             : #include "miscadmin.h"
      45             : #include "parser/parse_func.h"
      46             : #include "pgstat.h"
      47             : #include "storage/lmgr.h"
      48             : #include "tcop/deparse_utility.h"
      49             : #include "tcop/utility.h"
      50             : #include "utils/acl.h"
      51             : #include "utils/builtins.h"
      52             : #include "utils/evtcache.h"
      53             : #include "utils/fmgroids.h"
      54             : #include "utils/fmgrprotos.h"
      55             : #include "utils/lsyscache.h"
      56             : #include "utils/memutils.h"
      57             : #include "utils/rel.h"
      58             : #include "utils/snapmgr.h"
      59             : #include "utils/syscache.h"
      60             : 
      61             : typedef struct EventTriggerQueryState
      62             : {
      63             :     /* memory context for this state's objects */
      64             :     MemoryContext cxt;
      65             : 
      66             :     /* sql_drop */
      67             :     slist_head  SQLDropList;
      68             :     bool        in_sql_drop;
      69             : 
      70             :     /* table_rewrite */
      71             :     Oid         table_rewrite_oid;  /* InvalidOid, or set for table_rewrite
      72             :                                      * event */
      73             :     int         table_rewrite_reason;   /* AT_REWRITE reason */
      74             : 
      75             :     /* Support for command collection */
      76             :     bool        commandCollectionInhibited;
      77             :     CollectedCommand *currentCommand;
      78             :     List       *commandList;    /* list of CollectedCommand; see
      79             :                                  * deparse_utility.h */
      80             :     struct EventTriggerQueryState *previous;
      81             : } EventTriggerQueryState;
      82             : 
      83             : static EventTriggerQueryState *currentEventTriggerState = NULL;
      84             : 
      85             : /* GUC parameter */
      86             : bool        event_triggers = true;
      87             : 
      88             : /* Support for dropped objects */
      89             : typedef struct SQLDropObject
      90             : {
      91             :     ObjectAddress address;
      92             :     const char *schemaname;
      93             :     const char *objname;
      94             :     const char *objidentity;
      95             :     const char *objecttype;
      96             :     List       *addrnames;
      97             :     List       *addrargs;
      98             :     bool        original;
      99             :     bool        normal;
     100             :     bool        istemp;
     101             :     slist_node  next;
     102             : } SQLDropObject;
     103             : 
     104             : static void AlterEventTriggerOwner_internal(Relation rel,
     105             :                                             HeapTuple tup,
     106             :                                             Oid newOwnerId);
     107             : static void error_duplicate_filter_variable(const char *defname);
     108             : static Datum filter_list_to_array(List *filterlist);
     109             : static Oid  insert_event_trigger_tuple(const char *trigname, const char *eventname,
     110             :                                        Oid evtOwner, Oid funcoid, List *taglist);
     111             : static void validate_ddl_tags(const char *filtervar, List *taglist);
     112             : static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
     113             : static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
     114             : static bool obtain_object_name_namespace(const ObjectAddress *object,
     115             :                                          SQLDropObject *obj);
     116             : static const char *stringify_grant_objtype(ObjectType objtype);
     117             : static const char *stringify_adefprivs_objtype(ObjectType objtype);
     118             : static void SetDatabaseHasLoginEventTriggers(void);
     119             : 
     120             : /*
     121             :  * Create an event trigger.
     122             :  */
     123             : Oid
     124         200 : CreateEventTrigger(CreateEventTrigStmt *stmt)
     125             : {
     126             :     HeapTuple   tuple;
     127             :     Oid         funcoid;
     128             :     Oid         funcrettype;
     129         200 :     Oid         evtowner = GetUserId();
     130             :     ListCell   *lc;
     131         200 :     List       *tags = NULL;
     132             : 
     133             :     /*
     134             :      * It would be nice to allow database owners or even regular users to do
     135             :      * this, but there are obvious privilege escalation risks which would have
     136             :      * to somehow be plugged first.
     137             :      */
     138         200 :     if (!superuser())
     139           6 :         ereport(ERROR,
     140             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     141             :                  errmsg("permission denied to create event trigger \"%s\"",
     142             :                         stmt->trigname),
     143             :                  errhint("Must be superuser to create an event trigger.")));
     144             : 
     145             :     /* Validate event name. */
     146         194 :     if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
     147         106 :         strcmp(stmt->eventname, "ddl_command_end") != 0 &&
     148          66 :         strcmp(stmt->eventname, "sql_drop") != 0 &&
     149          32 :         strcmp(stmt->eventname, "login") != 0 &&
     150          22 :         strcmp(stmt->eventname, "table_rewrite") != 0)
     151           6 :         ereport(ERROR,
     152             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     153             :                  errmsg("unrecognized event name \"%s\"",
     154             :                         stmt->eventname)));
     155             : 
     156             :     /* Validate filter conditions. */
     157         280 :     foreach(lc, stmt->whenclause)
     158             :     {
     159         104 :         DefElem    *def = (DefElem *) lfirst(lc);
     160             : 
     161         104 :         if (strcmp(def->defname, "tag") == 0)
     162             :         {
     163          98 :             if (tags != NULL)
     164           6 :                 error_duplicate_filter_variable(def->defname);
     165          92 :             tags = (List *) def->arg;
     166             :         }
     167             :         else
     168           6 :             ereport(ERROR,
     169             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     170             :                      errmsg("unrecognized filter variable \"%s\"", def->defname)));
     171             :     }
     172             : 
     173             :     /* Validate tag list, if any. */
     174         176 :     if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
     175         100 :          strcmp(stmt->eventname, "ddl_command_end") == 0 ||
     176          60 :          strcmp(stmt->eventname, "sql_drop") == 0)
     177         150 :         && tags != NULL)
     178          86 :         validate_ddl_tags("tag", tags);
     179          90 :     else if (strcmp(stmt->eventname, "table_rewrite") == 0
     180          16 :              && tags != NULL)
     181           0 :         validate_table_rewrite_tags("tag", tags);
     182          90 :     else if (strcmp(stmt->eventname, "login") == 0 && tags != NULL)
     183           0 :         ereport(ERROR,
     184             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     185             :                  errmsg("tag filtering is not supported for login event triggers")));
     186             : 
     187             :     /*
     188             :      * Give user a nice error message if an event trigger of the same name
     189             :      * already exists.
     190             :      */
     191         140 :     tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
     192         140 :     if (HeapTupleIsValid(tuple))
     193           0 :         ereport(ERROR,
     194             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     195             :                  errmsg("event trigger \"%s\" already exists",
     196             :                         stmt->trigname)));
     197             : 
     198             :     /* Find and validate the trigger function. */
     199         140 :     funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
     200         140 :     funcrettype = get_func_rettype(funcoid);
     201         140 :     if (funcrettype != EVENT_TRIGGEROID)
     202           6 :         ereport(ERROR,
     203             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     204             :                  errmsg("function %s must return type %s",
     205             :                         NameListToString(stmt->funcname), "event_trigger")));
     206             : 
     207             :     /* Insert catalog entries. */
     208         134 :     return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
     209             :                                       evtowner, funcoid, tags);
     210             : }
     211             : 
     212             : /*
     213             :  * Validate DDL command tags.
     214             :  */
     215             : static void
     216          86 : validate_ddl_tags(const char *filtervar, List *taglist)
     217             : {
     218             :     ListCell   *lc;
     219             : 
     220         202 :     foreach(lc, taglist)
     221             :     {
     222         152 :         const char *tagstr = strVal(lfirst(lc));
     223         152 :         CommandTag  commandTag = GetCommandTagEnum(tagstr);
     224             : 
     225         152 :         if (commandTag == CMDTAG_UNKNOWN)
     226          12 :             ereport(ERROR,
     227             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     228             :                      errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
     229             :                             tagstr, filtervar)));
     230         140 :         if (!command_tag_event_trigger_ok(commandTag))
     231          24 :             ereport(ERROR,
     232             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     233             :             /* translator: %s represents an SQL statement name */
     234             :                      errmsg("event triggers are not supported for %s",
     235             :                             tagstr)));
     236             :     }
     237          50 : }
     238             : 
     239             : /*
     240             :  * Validate DDL command tags for event table_rewrite.
     241             :  */
     242             : static void
     243           0 : validate_table_rewrite_tags(const char *filtervar, List *taglist)
     244             : {
     245             :     ListCell   *lc;
     246             : 
     247           0 :     foreach(lc, taglist)
     248             :     {
     249           0 :         const char *tagstr = strVal(lfirst(lc));
     250           0 :         CommandTag  commandTag = GetCommandTagEnum(tagstr);
     251             : 
     252           0 :         if (!command_tag_table_rewrite_ok(commandTag))
     253           0 :             ereport(ERROR,
     254             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     255             :             /* translator: %s represents an SQL statement name */
     256             :                      errmsg("event triggers are not supported for %s",
     257             :                             tagstr)));
     258             :     }
     259           0 : }
     260             : 
     261             : /*
     262             :  * Complain about a duplicate filter variable.
     263             :  */
     264             : static void
     265           6 : error_duplicate_filter_variable(const char *defname)
     266             : {
     267           6 :     ereport(ERROR,
     268             :             (errcode(ERRCODE_SYNTAX_ERROR),
     269             :              errmsg("filter variable \"%s\" specified more than once",
     270             :                     defname)));
     271             : }
     272             : 
     273             : /*
     274             :  * Insert the new pg_event_trigger row and record dependencies.
     275             :  */
     276             : static Oid
     277         134 : insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtOwner,
     278             :                            Oid funcoid, List *taglist)
     279             : {
     280             :     Relation    tgrel;
     281             :     Oid         trigoid;
     282             :     HeapTuple   tuple;
     283             :     Datum       values[Natts_pg_event_trigger];
     284             :     bool        nulls[Natts_pg_event_trigger];
     285             :     NameData    evtnamedata,
     286             :                 evteventdata;
     287             :     ObjectAddress myself,
     288             :                 referenced;
     289             : 
     290             :     /* Open pg_event_trigger. */
     291         134 :     tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
     292             : 
     293             :     /* Build the new pg_trigger tuple. */
     294         134 :     trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
     295             :                                  Anum_pg_event_trigger_oid);
     296         134 :     values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
     297         134 :     memset(nulls, false, sizeof(nulls));
     298         134 :     namestrcpy(&evtnamedata, trigname);
     299         134 :     values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
     300         134 :     namestrcpy(&evteventdata, eventname);
     301         134 :     values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
     302         134 :     values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
     303         134 :     values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
     304         134 :     values[Anum_pg_event_trigger_evtenabled - 1] =
     305         134 :         CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
     306         134 :     if (taglist == NIL)
     307          84 :         nulls[Anum_pg_event_trigger_evttags - 1] = true;
     308             :     else
     309          50 :         values[Anum_pg_event_trigger_evttags - 1] =
     310          50 :             filter_list_to_array(taglist);
     311             : 
     312             :     /* Insert heap tuple. */
     313         134 :     tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
     314         134 :     CatalogTupleInsert(tgrel, tuple);
     315         134 :     heap_freetuple(tuple);
     316             : 
     317             :     /*
     318             :      * Login event triggers have an additional flag in pg_database to enable
     319             :      * faster lookups in hot codepaths. Set the flag unless already True.
     320             :      */
     321         134 :     if (strcmp(eventname, "login") == 0)
     322          10 :         SetDatabaseHasLoginEventTriggers();
     323             : 
     324             :     /* Depend on owner. */
     325         134 :     recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
     326             : 
     327             :     /* Depend on event trigger function. */
     328         134 :     myself.classId = EventTriggerRelationId;
     329         134 :     myself.objectId = trigoid;
     330         134 :     myself.objectSubId = 0;
     331         134 :     referenced.classId = ProcedureRelationId;
     332         134 :     referenced.objectId = funcoid;
     333         134 :     referenced.objectSubId = 0;
     334         134 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     335             : 
     336             :     /* Depend on extension, if any. */
     337         134 :     recordDependencyOnCurrentExtension(&myself, false);
     338             : 
     339             :     /* Post creation hook for new event trigger */
     340         134 :     InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
     341             : 
     342             :     /* Close pg_event_trigger. */
     343         134 :     table_close(tgrel, RowExclusiveLock);
     344             : 
     345         134 :     return trigoid;
     346             : }
     347             : 
     348             : /*
     349             :  * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented
     350             :  * by a DefElem whose value is a List of String nodes; in the catalog, we
     351             :  * store the list of strings as a text array.  This function transforms the
     352             :  * former representation into the latter one.
     353             :  *
     354             :  * For cleanliness, we store command tags in the catalog as text.  It's
     355             :  * possible (although not currently anticipated) that we might have
     356             :  * a case-sensitive filter variable in the future, in which case this would
     357             :  * need some further adjustment.
     358             :  */
     359             : static Datum
     360          50 : filter_list_to_array(List *filterlist)
     361             : {
     362             :     ListCell   *lc;
     363             :     Datum      *data;
     364          50 :     int         i = 0,
     365          50 :                 l = list_length(filterlist);
     366             : 
     367          50 :     data = (Datum *) palloc(l * sizeof(Datum));
     368             : 
     369         160 :     foreach(lc, filterlist)
     370             :     {
     371         110 :         const char *value = strVal(lfirst(lc));
     372             :         char       *result,
     373             :                    *p;
     374             : 
     375         110 :         result = pstrdup(value);
     376        1326 :         for (p = result; *p; p++)
     377        1216 :             *p = pg_ascii_toupper((unsigned char) *p);
     378         110 :         data[i++] = PointerGetDatum(cstring_to_text(result));
     379         110 :         pfree(result);
     380             :     }
     381             : 
     382          50 :     return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
     383             : }
     384             : 
     385             : /*
     386             :  * Set pg_database.dathasloginevt flag for current database indicating that
     387             :  * current database has on login event triggers.
     388             :  */
     389             : void
     390          20 : SetDatabaseHasLoginEventTriggers(void)
     391             : {
     392             :     /* Set dathasloginevt flag in pg_database */
     393             :     Form_pg_database db;
     394          20 :     Relation    pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
     395             :     ItemPointerData otid;
     396             :     HeapTuple   tuple;
     397             : 
     398             :     /*
     399             :      * Use shared lock to prevent a conflict with EventTriggerOnLogin() trying
     400             :      * to reset pg_database.dathasloginevt flag.  Note, this lock doesn't
     401             :      * effectively blocks database or other objection.  It's just custom lock
     402             :      * tag used to prevent multiple backends changing
     403             :      * pg_database.dathasloginevt flag.
     404             :      */
     405          20 :     LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
     406             : 
     407          20 :     tuple = SearchSysCacheLockedCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
     408          20 :     if (!HeapTupleIsValid(tuple))
     409           0 :         elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
     410          20 :     otid = tuple->t_self;
     411          20 :     db = (Form_pg_database) GETSTRUCT(tuple);
     412          20 :     if (!db->dathasloginevt)
     413             :     {
     414          10 :         db->dathasloginevt = true;
     415          10 :         CatalogTupleUpdate(pg_db, &otid, tuple);
     416          10 :         CommandCounterIncrement();
     417             :     }
     418          20 :     UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
     419          20 :     table_close(pg_db, RowExclusiveLock);
     420          20 :     heap_freetuple(tuple);
     421          20 : }
     422             : 
     423             : /*
     424             :  * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
     425             :  */
     426             : Oid
     427          48 : AlterEventTrigger(AlterEventTrigStmt *stmt)
     428             : {
     429             :     Relation    tgrel;
     430             :     HeapTuple   tup;
     431             :     Oid         trigoid;
     432             :     Form_pg_event_trigger evtForm;
     433          48 :     char        tgenabled = stmt->tgenabled;
     434             : 
     435          48 :     tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
     436             : 
     437          48 :     tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
     438             :                               CStringGetDatum(stmt->trigname));
     439          48 :     if (!HeapTupleIsValid(tup))
     440           0 :         ereport(ERROR,
     441             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     442             :                  errmsg("event trigger \"%s\" does not exist",
     443             :                         stmt->trigname)));
     444             : 
     445          48 :     evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
     446          48 :     trigoid = evtForm->oid;
     447             : 
     448          48 :     if (!object_ownercheck(EventTriggerRelationId, trigoid, GetUserId()))
     449           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
     450           0 :                        stmt->trigname);
     451             : 
     452             :     /* tuple is a copy, so we can modify it below */
     453          48 :     evtForm->evtenabled = tgenabled;
     454             : 
     455          48 :     CatalogTupleUpdate(tgrel, &tup->t_self, tup);
     456             : 
     457             :     /*
     458             :      * Login event triggers have an additional flag in pg_database to enable
     459             :      * faster lookups in hot codepaths. Set the flag unless already True.
     460             :      */
     461          48 :     if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
     462             :         tgenabled != TRIGGER_DISABLED)
     463          10 :         SetDatabaseHasLoginEventTriggers();
     464             : 
     465          48 :     InvokeObjectPostAlterHook(EventTriggerRelationId,
     466             :                               trigoid, 0);
     467             : 
     468             :     /* clean up */
     469          48 :     heap_freetuple(tup);
     470          48 :     table_close(tgrel, RowExclusiveLock);
     471             : 
     472          48 :     return trigoid;
     473             : }
     474             : 
     475             : /*
     476             :  * Change event trigger's owner -- by name
     477             :  */
     478             : ObjectAddress
     479          14 : AlterEventTriggerOwner(const char *name, Oid newOwnerId)
     480             : {
     481             :     Oid         evtOid;
     482             :     HeapTuple   tup;
     483             :     Form_pg_event_trigger evtForm;
     484             :     Relation    rel;
     485             :     ObjectAddress address;
     486             : 
     487          14 :     rel = table_open(EventTriggerRelationId, RowExclusiveLock);
     488             : 
     489          14 :     tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
     490             : 
     491          14 :     if (!HeapTupleIsValid(tup))
     492           0 :         ereport(ERROR,
     493             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     494             :                  errmsg("event trigger \"%s\" does not exist", name)));
     495             : 
     496          14 :     evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
     497          14 :     evtOid = evtForm->oid;
     498             : 
     499          14 :     AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
     500             : 
     501           8 :     ObjectAddressSet(address, EventTriggerRelationId, evtOid);
     502             : 
     503           8 :     heap_freetuple(tup);
     504             : 
     505           8 :     table_close(rel, RowExclusiveLock);
     506             : 
     507           8 :     return address;
     508             : }
     509             : 
     510             : /*
     511             :  * Change event trigger owner, by OID
     512             :  */
     513             : void
     514           0 : AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId)
     515             : {
     516             :     HeapTuple   tup;
     517             :     Relation    rel;
     518             : 
     519           0 :     rel = table_open(EventTriggerRelationId, RowExclusiveLock);
     520             : 
     521           0 :     tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
     522             : 
     523           0 :     if (!HeapTupleIsValid(tup))
     524           0 :         ereport(ERROR,
     525             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     526             :                  errmsg("event trigger with OID %u does not exist", trigOid)));
     527             : 
     528           0 :     AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
     529             : 
     530           0 :     heap_freetuple(tup);
     531             : 
     532           0 :     table_close(rel, RowExclusiveLock);
     533           0 : }
     534             : 
     535             : /*
     536             :  * Internal workhorse for changing an event trigger's owner
     537             :  */
     538             : static void
     539          14 : AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
     540             : {
     541             :     Form_pg_event_trigger form;
     542             : 
     543          14 :     form = (Form_pg_event_trigger) GETSTRUCT(tup);
     544             : 
     545          14 :     if (form->evtowner == newOwnerId)
     546           2 :         return;
     547             : 
     548          12 :     if (!object_ownercheck(EventTriggerRelationId, form->oid, GetUserId()))
     549           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
     550           0 :                        NameStr(form->evtname));
     551             : 
     552             :     /* New owner must be a superuser */
     553          12 :     if (!superuser_arg(newOwnerId))
     554           6 :         ereport(ERROR,
     555             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     556             :                  errmsg("permission denied to change owner of event trigger \"%s\"",
     557             :                         NameStr(form->evtname)),
     558             :                  errhint("The owner of an event trigger must be a superuser.")));
     559             : 
     560           6 :     form->evtowner = newOwnerId;
     561           6 :     CatalogTupleUpdate(rel, &tup->t_self, tup);
     562             : 
     563             :     /* Update owner dependency reference */
     564           6 :     changeDependencyOnOwner(EventTriggerRelationId,
     565             :                             form->oid,
     566             :                             newOwnerId);
     567             : 
     568           6 :     InvokeObjectPostAlterHook(EventTriggerRelationId,
     569             :                               form->oid, 0);
     570             : }
     571             : 
     572             : /*
     573             :  * get_event_trigger_oid - Look up an event trigger by name to find its OID.
     574             :  *
     575             :  * If missing_ok is false, throw an error if trigger not found.  If
     576             :  * true, just return InvalidOid.
     577             :  */
     578             : Oid
     579         170 : get_event_trigger_oid(const char *trigname, bool missing_ok)
     580             : {
     581             :     Oid         oid;
     582             : 
     583         170 :     oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
     584             :                           CStringGetDatum(trigname));
     585         170 :     if (!OidIsValid(oid) && !missing_ok)
     586          12 :         ereport(ERROR,
     587             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     588             :                  errmsg("event trigger \"%s\" does not exist", trigname)));
     589         158 :     return oid;
     590             : }
     591             : 
     592             : /*
     593             :  * Return true when we want to fire given Event Trigger and false otherwise,
     594             :  * filtering on the session replication role and the event trigger registered
     595             :  * tags matching.
     596             :  */
     597             : static bool
     598        2474 : filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
     599             : {
     600             :     /*
     601             :      * Filter by session replication role, knowing that we never see disabled
     602             :      * items down here.
     603             :      */
     604        2474 :     if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
     605             :     {
     606          54 :         if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
     607          42 :             return false;
     608             :     }
     609             :     else
     610             :     {
     611        2420 :         if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
     612           0 :             return false;
     613             :     }
     614             : 
     615             :     /* Filter by tags, if any were specified. */
     616        2432 :     if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
     617         474 :         return false;
     618             : 
     619             :     /* if we reach that point, we're not filtering out this item */
     620        1958 :     return true;
     621             : }
     622             : 
     623             : static CommandTag
     624        2174 : EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
     625             : {
     626        2174 :     if (event == EVT_Login)
     627         268 :         return CMDTAG_LOGIN;
     628             :     else
     629        1906 :         return CreateCommandTag(parsetree);
     630             : }
     631             : 
     632             : /*
     633             :  * Setup for running triggers for the given event.  Return value is an OID list
     634             :  * of functions to run; if there are any, trigdata is filled with an
     635             :  * appropriate EventTriggerData for them to receive.
     636             :  */
     637             : static List *
     638      160784 : EventTriggerCommonSetup(Node *parsetree,
     639             :                         EventTriggerEvent event, const char *eventstr,
     640             :                         EventTriggerData *trigdata, bool unfiltered)
     641             : {
     642             :     CommandTag  tag;
     643             :     List       *cachelist;
     644             :     ListCell   *lc;
     645      160784 :     List       *runlist = NIL;
     646             : 
     647             :     /*
     648             :      * We want the list of command tags for which this procedure is actually
     649             :      * invoked to match up exactly with the list that CREATE EVENT TRIGGER
     650             :      * accepts.  This debugging cross-check will throw an error if this
     651             :      * function is invoked for a command tag that CREATE EVENT TRIGGER won't
     652             :      * accept.  (Unfortunately, there doesn't seem to be any simple, automated
     653             :      * way to verify that CREATE EVENT TRIGGER doesn't accept extra stuff that
     654             :      * never reaches this control point.)
     655             :      *
     656             :      * If this cross-check fails for you, you probably need to either adjust
     657             :      * standard_ProcessUtility() not to invoke event triggers for the command
     658             :      * type in question, or you need to adjust event_trigger_ok to accept the
     659             :      * relevant command tag.
     660             :      */
     661             : #ifdef USE_ASSERT_CHECKING
     662             :     {
     663             :         CommandTag  dbgtag;
     664             : 
     665             :         dbgtag = EventTriggerGetTag(parsetree, event);
     666             : 
     667             :         if (event == EVT_DDLCommandStart ||
     668             :             event == EVT_DDLCommandEnd ||
     669             :             event == EVT_SQLDrop ||
     670             :             event == EVT_Login)
     671             :         {
     672             :             if (!command_tag_event_trigger_ok(dbgtag))
     673             :                 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
     674             :         }
     675             :         else if (event == EVT_TableRewrite)
     676             :         {
     677             :             if (!command_tag_table_rewrite_ok(dbgtag))
     678             :                 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
     679             :         }
     680             :     }
     681             : #endif
     682             : 
     683             :     /* Use cache to find triggers for this event; fast exit if none. */
     684      160784 :     cachelist = EventCacheLookup(event);
     685      160784 :     if (cachelist == NIL)
     686      158610 :         return NIL;
     687             : 
     688             :     /* Get the command tag. */
     689        2174 :     tag = EventTriggerGetTag(parsetree, event);
     690             : 
     691             :     /*
     692             :      * Filter list of event triggers by command tag, and copy them into our
     693             :      * memory context.  Once we start running the command triggers, or indeed
     694             :      * once we do anything at all that touches the catalogs, an invalidation
     695             :      * might leave cachelist pointing at garbage, so we must do this before we
     696             :      * can do much else.
     697             :      */
     698        4648 :     foreach(lc, cachelist)
     699             :     {
     700        2474 :         EventTriggerCacheItem *item = lfirst(lc);
     701             : 
     702        2474 :         if (unfiltered || filter_event_trigger(tag, item))
     703             :         {
     704             :             /* We must plan to fire this trigger. */
     705        1958 :             runlist = lappend_oid(runlist, item->fnoid);
     706             :         }
     707             :     }
     708             : 
     709             :     /* Don't spend any more time on this if no functions to run */
     710        2174 :     if (runlist == NIL)
     711         420 :         return NIL;
     712             : 
     713        1754 :     trigdata->type = T_EventTriggerData;
     714        1754 :     trigdata->event = eventstr;
     715        1754 :     trigdata->parsetree = parsetree;
     716        1754 :     trigdata->tag = tag;
     717             : 
     718        1754 :     return runlist;
     719             : }
     720             : 
     721             : /*
     722             :  * Fire ddl_command_start triggers.
     723             :  */
     724             : void
     725      228732 : EventTriggerDDLCommandStart(Node *parsetree)
     726             : {
     727             :     List       *runlist;
     728             :     EventTriggerData trigdata;
     729             : 
     730             :     /*
     731             :      * Event Triggers are completely disabled in standalone mode.  There are
     732             :      * (at least) two reasons for this:
     733             :      *
     734             :      * 1. A sufficiently broken event trigger might not only render the
     735             :      * database unusable, but prevent disabling itself to fix the situation.
     736             :      * In this scenario, restarting in standalone mode provides an escape
     737             :      * hatch.
     738             :      *
     739             :      * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
     740             :      * therefore will malfunction if pg_event_trigger's indexes are damaged.
     741             :      * To allow recovery from a damaged index, we need some operating mode
     742             :      * wherein event triggers are disabled.  (Or we could implement
     743             :      * heapscan-and-sort logic for that case, but having disaster recovery
     744             :      * scenarios depend on code that's otherwise untested isn't appetizing.)
     745             :      *
     746             :      * Additionally, event triggers can be disabled with a superuser-only GUC
     747             :      * to make fixing database easier as per 1 above.
     748             :      */
     749      228732 :     if (!IsUnderPostmaster || !event_triggers)
     750      228332 :         return;
     751             : 
     752      157110 :     runlist = EventTriggerCommonSetup(parsetree,
     753             :                                       EVT_DDLCommandStart,
     754             :                                       "ddl_command_start",
     755             :                                       &trigdata, false);
     756      157110 :     if (runlist == NIL)
     757      156710 :         return;
     758             : 
     759             :     /* Run the triggers. */
     760         400 :     EventTriggerInvoke(runlist, &trigdata);
     761             : 
     762             :     /* Cleanup. */
     763         400 :     list_free(runlist);
     764             : 
     765             :     /*
     766             :      * Make sure anything the event triggers did will be visible to the main
     767             :      * command.
     768             :      */
     769         400 :     CommandCounterIncrement();
     770             : }
     771             : 
     772             : /*
     773             :  * Fire ddl_command_end triggers.
     774             :  */
     775             : void
     776      216240 : EventTriggerDDLCommandEnd(Node *parsetree)
     777             : {
     778             :     List       *runlist;
     779             :     EventTriggerData trigdata;
     780             : 
     781             :     /*
     782             :      * See EventTriggerDDLCommandStart for a discussion about why event
     783             :      * triggers are disabled in single user mode or via GUC.
     784             :      */
     785      216240 :     if (!IsUnderPostmaster || !event_triggers)
     786      215358 :         return;
     787             : 
     788             :     /*
     789             :      * Also do nothing if our state isn't set up, which it won't be if there
     790             :      * weren't any relevant event triggers at the start of the current DDL
     791             :      * command.  This test might therefore seem optional, but it's important
     792             :      * because EventTriggerCommonSetup might find triggers that didn't exist
     793             :      * at the time the command started.  Although this function itself
     794             :      * wouldn't crash, the event trigger functions would presumably call
     795             :      * pg_event_trigger_ddl_commands which would fail.  Better to do nothing
     796             :      * until the next command.
     797             :      */
     798      144618 :     if (!currentEventTriggerState)
     799      141902 :         return;
     800             : 
     801        2716 :     runlist = EventTriggerCommonSetup(parsetree,
     802             :                                       EVT_DDLCommandEnd, "ddl_command_end",
     803             :                                       &trigdata, false);
     804        2716 :     if (runlist == NIL)
     805        1834 :         return;
     806             : 
     807             :     /*
     808             :      * Make sure anything the main command did will be visible to the event
     809             :      * triggers.
     810             :      */
     811         882 :     CommandCounterIncrement();
     812             : 
     813             :     /* Run the triggers. */
     814         882 :     EventTriggerInvoke(runlist, &trigdata);
     815             : 
     816             :     /* Cleanup. */
     817         882 :     list_free(runlist);
     818             : }
     819             : 
     820             : /*
     821             :  * Fire sql_drop triggers.
     822             :  */
     823             : void
     824      216258 : EventTriggerSQLDrop(Node *parsetree)
     825             : {
     826             :     List       *runlist;
     827             :     EventTriggerData trigdata;
     828             : 
     829             :     /*
     830             :      * See EventTriggerDDLCommandStart for a discussion about why event
     831             :      * triggers are disabled in single user mode or via a GUC.
     832             :      */
     833      216258 :     if (!IsUnderPostmaster || !event_triggers)
     834      216138 :         return;
     835             : 
     836             :     /*
     837             :      * Use current state to determine whether this event fires at all.  If
     838             :      * there are no triggers for the sql_drop event, then we don't have
     839             :      * anything to do here.  Note that dropped object collection is disabled
     840             :      * if this is the case, so even if we were to try to run, the list would
     841             :      * be empty.
     842             :      */
     843      144636 :     if (!currentEventTriggerState ||
     844        2734 :         slist_is_empty(&currentEventTriggerState->SQLDropList))
     845      144106 :         return;
     846             : 
     847         530 :     runlist = EventTriggerCommonSetup(parsetree,
     848             :                                       EVT_SQLDrop, "sql_drop",
     849             :                                       &trigdata, false);
     850             : 
     851             :     /*
     852             :      * Nothing to do if run list is empty.  Note this typically can't happen,
     853             :      * because if there are no sql_drop events, then objects-to-drop wouldn't
     854             :      * have been collected in the first place and we would have quit above.
     855             :      * But it could occur if event triggers were dropped partway through.
     856             :      */
     857         530 :     if (runlist == NIL)
     858         410 :         return;
     859             : 
     860             :     /*
     861             :      * Make sure anything the main command did will be visible to the event
     862             :      * triggers.
     863             :      */
     864         120 :     CommandCounterIncrement();
     865             : 
     866             :     /*
     867             :      * Make sure pg_event_trigger_dropped_objects only works when running
     868             :      * these triggers.  Use PG_TRY to ensure in_sql_drop is reset even when
     869             :      * one trigger fails.  (This is perhaps not necessary, as the currentState
     870             :      * variable will be removed shortly by our caller, but it seems better to
     871             :      * play safe.)
     872             :      */
     873         120 :     currentEventTriggerState->in_sql_drop = true;
     874             : 
     875             :     /* Run the triggers. */
     876         120 :     PG_TRY();
     877             :     {
     878         120 :         EventTriggerInvoke(runlist, &trigdata);
     879             :     }
     880          18 :     PG_FINALLY();
     881             :     {
     882         120 :         currentEventTriggerState->in_sql_drop = false;
     883             :     }
     884         120 :     PG_END_TRY();
     885             : 
     886             :     /* Cleanup. */
     887         102 :     list_free(runlist);
     888             : }
     889             : 
     890             : /*
     891             :  * Fire login event triggers if any are present.  The dathasloginevt
     892             :  * pg_database flag is left unchanged when an event trigger is dropped to avoid
     893             :  * complicating the codepath in the case of multiple event triggers.  This
     894             :  * function will instead unset the flag if no trigger is defined.
     895             :  */
     896             : void
     897       25592 : EventTriggerOnLogin(void)
     898             : {
     899             :     List       *runlist;
     900             :     EventTriggerData trigdata;
     901             : 
     902             :     /*
     903             :      * See EventTriggerDDLCommandStart for a discussion about why event
     904             :      * triggers are disabled in single user mode or via a GUC.  We also need a
     905             :      * database connection (some background workers don't have it).
     906             :      */
     907       25592 :     if (!IsUnderPostmaster || !event_triggers ||
     908       25458 :         !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
     909       25318 :         return;
     910             : 
     911         274 :     StartTransactionCommand();
     912         274 :     runlist = EventTriggerCommonSetup(NULL,
     913             :                                       EVT_Login, "login",
     914             :                                       &trigdata, false);
     915             : 
     916         274 :     if (runlist != NIL)
     917             :     {
     918             :         /*
     919             :          * Event trigger execution may require an active snapshot.
     920             :          */
     921         268 :         PushActiveSnapshot(GetTransactionSnapshot());
     922             : 
     923             :         /* Run the triggers. */
     924         268 :         EventTriggerInvoke(runlist, &trigdata);
     925             : 
     926             :         /* Cleanup. */
     927         268 :         list_free(runlist);
     928             : 
     929         268 :         PopActiveSnapshot();
     930             :     }
     931             : 
     932             :     /*
     933             :      * There is no active login event trigger, but our
     934             :      * pg_database.dathasloginevt is set. Try to unset this flag.  We use the
     935             :      * lock to prevent concurrent SetDatabaseHasLoginEventTriggers(), but we
     936             :      * don't want to hang the connection waiting on the lock.  Thus, we are
     937             :      * just trying to acquire the lock conditionally.
     938             :      */
     939           6 :     else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
     940             :                                          0, AccessExclusiveLock))
     941             :     {
     942             :         /*
     943             :          * The lock is held.  Now we need to recheck that login event triggers
     944             :          * list is still empty.  Once the list is empty, we know that even if
     945             :          * there is a backend which concurrently inserts/enables a login event
     946             :          * trigger, it will update pg_database.dathasloginevt *afterwards*.
     947             :          */
     948           6 :         runlist = EventTriggerCommonSetup(NULL,
     949             :                                           EVT_Login, "login",
     950             :                                           &trigdata, true);
     951             : 
     952           6 :         if (runlist == NIL)
     953             :         {
     954           6 :             Relation    pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
     955             :             HeapTuple   tuple;
     956             :             void       *state;
     957             :             Form_pg_database db;
     958             :             ScanKeyData key[1];
     959             : 
     960             :             /* Fetch a copy of the tuple to scribble on */
     961           6 :             ScanKeyInit(&key[0],
     962             :                         Anum_pg_database_oid,
     963             :                         BTEqualStrategyNumber, F_OIDEQ,
     964             :                         ObjectIdGetDatum(MyDatabaseId));
     965             : 
     966           6 :             systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
     967             :                                           NULL, 1, key, &tuple, &state);
     968             : 
     969           6 :             if (!HeapTupleIsValid(tuple))
     970           0 :                 elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
     971             : 
     972           6 :             db = (Form_pg_database) GETSTRUCT(tuple);
     973           6 :             if (db->dathasloginevt)
     974             :             {
     975           6 :                 db->dathasloginevt = false;
     976             : 
     977             :                 /*
     978             :                  * Do an "in place" update of the pg_database tuple.  Doing
     979             :                  * this instead of regular updates serves two purposes. First,
     980             :                  * that avoids possible waiting on the row-level lock. Second,
     981             :                  * that avoids dealing with TOAST.
     982             :                  */
     983           6 :                 systable_inplace_update_finish(state, tuple);
     984             :             }
     985             :             else
     986           0 :                 systable_inplace_update_cancel(state);
     987           6 :             table_close(pg_db, RowExclusiveLock);
     988           6 :             heap_freetuple(tuple);
     989             :         }
     990             :         else
     991             :         {
     992           0 :             list_free(runlist);
     993             :         }
     994             :     }
     995         274 :     CommitTransactionCommand();
     996             : }
     997             : 
     998             : 
     999             : /*
    1000             :  * Fire table_rewrite triggers.
    1001             :  */
    1002             : void
    1003         988 : EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
    1004             : {
    1005             :     List       *runlist;
    1006             :     EventTriggerData trigdata;
    1007             : 
    1008             :     /*
    1009             :      * See EventTriggerDDLCommandStart for a discussion about why event
    1010             :      * triggers are disabled in single user mode or via a GUC.
    1011             :      */
    1012         988 :     if (!IsUnderPostmaster || !event_triggers)
    1013         904 :         return;
    1014             : 
    1015             :     /*
    1016             :      * Also do nothing if our state isn't set up, which it won't be if there
    1017             :      * weren't any relevant event triggers at the start of the current DDL
    1018             :      * command.  This test might therefore seem optional, but it's
    1019             :      * *necessary*, because EventTriggerCommonSetup might find triggers that
    1020             :      * didn't exist at the time the command started.
    1021             :      */
    1022         988 :     if (!currentEventTriggerState)
    1023         840 :         return;
    1024             : 
    1025         148 :     runlist = EventTriggerCommonSetup(parsetree,
    1026             :                                       EVT_TableRewrite,
    1027             :                                       "table_rewrite",
    1028             :                                       &trigdata, false);
    1029         148 :     if (runlist == NIL)
    1030          64 :         return;
    1031             : 
    1032             :     /*
    1033             :      * Make sure pg_event_trigger_table_rewrite_oid only works when running
    1034             :      * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
    1035             :      * when one trigger fails. (This is perhaps not necessary, as the
    1036             :      * currentState variable will be removed shortly by our caller, but it
    1037             :      * seems better to play safe.)
    1038             :      */
    1039          84 :     currentEventTriggerState->table_rewrite_oid = tableOid;
    1040          84 :     currentEventTriggerState->table_rewrite_reason = reason;
    1041             : 
    1042             :     /* Run the triggers. */
    1043          84 :     PG_TRY();
    1044             :     {
    1045          84 :         EventTriggerInvoke(runlist, &trigdata);
    1046             :     }
    1047           6 :     PG_FINALLY();
    1048             :     {
    1049          84 :         currentEventTriggerState->table_rewrite_oid = InvalidOid;
    1050          84 :         currentEventTriggerState->table_rewrite_reason = 0;
    1051             :     }
    1052          84 :     PG_END_TRY();
    1053             : 
    1054             :     /* Cleanup. */
    1055          78 :     list_free(runlist);
    1056             : 
    1057             :     /*
    1058             :      * Make sure anything the event triggers did will be visible to the main
    1059             :      * command.
    1060             :      */
    1061          78 :     CommandCounterIncrement();
    1062             : }
    1063             : 
    1064             : /*
    1065             :  * Invoke each event trigger in a list of event triggers.
    1066             :  */
    1067             : static void
    1068        1754 : EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
    1069             : {
    1070             :     MemoryContext context;
    1071             :     MemoryContext oldcontext;
    1072             :     ListCell   *lc;
    1073        1754 :     bool        first = true;
    1074             : 
    1075             :     /* Guard against stack overflow due to recursive event trigger */
    1076        1754 :     check_stack_depth();
    1077             : 
    1078             :     /*
    1079             :      * Let's evaluate event triggers in their own memory context, so that any
    1080             :      * leaks get cleaned up promptly.
    1081             :      */
    1082        1754 :     context = AllocSetContextCreate(CurrentMemoryContext,
    1083             :                                     "event trigger context",
    1084             :                                     ALLOCSET_DEFAULT_SIZES);
    1085        1754 :     oldcontext = MemoryContextSwitchTo(context);
    1086             : 
    1087             :     /* Call each event trigger. */
    1088        3682 :     foreach(lc, fn_oid_list)
    1089             :     {
    1090        1952 :         LOCAL_FCINFO(fcinfo, 0);
    1091        1952 :         Oid         fnoid = lfirst_oid(lc);
    1092             :         FmgrInfo    flinfo;
    1093             :         PgStat_FunctionCallUsage fcusage;
    1094             : 
    1095        1952 :         elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
    1096             : 
    1097             :         /*
    1098             :          * We want each event trigger to be able to see the results of the
    1099             :          * previous event trigger's action.  Caller is responsible for any
    1100             :          * command-counter increment that is needed between the event trigger
    1101             :          * and anything else in the transaction.
    1102             :          */
    1103        1952 :         if (first)
    1104        1754 :             first = false;
    1105             :         else
    1106         198 :             CommandCounterIncrement();
    1107             : 
    1108             :         /* Look up the function */
    1109        1952 :         fmgr_info(fnoid, &flinfo);
    1110             : 
    1111             :         /* Call the function, passing no arguments but setting a context. */
    1112        1952 :         InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
    1113             :                                  InvalidOid, (Node *) trigdata, NULL);
    1114        1952 :         pgstat_init_function_usage(fcinfo, &fcusage);
    1115        1952 :         FunctionCallInvoke(fcinfo);
    1116        1928 :         pgstat_end_function_usage(&fcusage, true);
    1117             : 
    1118             :         /* Reclaim memory. */
    1119        1928 :         MemoryContextReset(context);
    1120             :     }
    1121             : 
    1122             :     /* Restore old memory context and delete the temporary one. */
    1123        1730 :     MemoryContextSwitchTo(oldcontext);
    1124        1730 :     MemoryContextDelete(context);
    1125        1730 : }
    1126             : 
    1127             : /*
    1128             :  * Do event triggers support this object type?
    1129             :  *
    1130             :  * See also event trigger documentation in event-trigger.sgml.
    1131             :  */
    1132             : bool
    1133       95536 : EventTriggerSupportsObjectType(ObjectType obtype)
    1134             : {
    1135       95536 :     switch (obtype)
    1136             :     {
    1137        1314 :         case OBJECT_DATABASE:
    1138             :         case OBJECT_TABLESPACE:
    1139             :         case OBJECT_ROLE:
    1140             :         case OBJECT_PARAMETER_ACL:
    1141             :             /* no support for global objects (except subscriptions) */
    1142        1314 :             return false;
    1143         154 :         case OBJECT_EVENT_TRIGGER:
    1144             :             /* no support for event triggers on event triggers */
    1145         154 :             return false;
    1146       94068 :         default:
    1147       94068 :             return true;
    1148             :     }
    1149             : }
    1150             : 
    1151             : /*
    1152             :  * Do event triggers support this object class?
    1153             :  *
    1154             :  * See also event trigger documentation in event-trigger.sgml.
    1155             :  */
    1156             : bool
    1157        3864 : EventTriggerSupportsObject(const ObjectAddress *object)
    1158             : {
    1159        3864 :     switch (object->classId)
    1160             :     {
    1161           0 :         case DatabaseRelationId:
    1162             :         case TableSpaceRelationId:
    1163             :         case AuthIdRelationId:
    1164             :         case AuthMemRelationId:
    1165             :         case ParameterAclRelationId:
    1166             :             /* no support for global objects (except subscriptions) */
    1167           0 :             return false;
    1168         116 :         case EventTriggerRelationId:
    1169             :             /* no support for event triggers on event triggers */
    1170         116 :             return false;
    1171        3748 :         default:
    1172        3748 :             return true;
    1173             :     }
    1174             : }
    1175             : 
    1176             : /*
    1177             :  * Prepare event trigger state for a new complete query to run, if necessary;
    1178             :  * returns whether this was done.  If it was, EventTriggerEndCompleteQuery must
    1179             :  * be called when the query is done, regardless of whether it succeeds or fails
    1180             :  * -- so use of a PG_TRY block is mandatory.
    1181             :  */
    1182             : bool
    1183      228732 : EventTriggerBeginCompleteQuery(void)
    1184             : {
    1185             :     EventTriggerQueryState *state;
    1186             :     MemoryContext cxt;
    1187             : 
    1188             :     /*
    1189             :      * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
    1190             :      * reason to have event trigger state at all; so if there are none, don't
    1191             :      * install one.
    1192             :      */
    1193      228732 :     if (!trackDroppedObjectsNeeded())
    1194      225834 :         return false;
    1195             : 
    1196        2898 :     cxt = AllocSetContextCreate(TopMemoryContext,
    1197             :                                 "event trigger state",
    1198             :                                 ALLOCSET_DEFAULT_SIZES);
    1199        2898 :     state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
    1200        2898 :     state->cxt = cxt;
    1201        2898 :     slist_init(&(state->SQLDropList));
    1202        2898 :     state->in_sql_drop = false;
    1203        2898 :     state->table_rewrite_oid = InvalidOid;
    1204             : 
    1205        2898 :     state->commandCollectionInhibited = currentEventTriggerState ?
    1206        2898 :         currentEventTriggerState->commandCollectionInhibited : false;
    1207        2898 :     state->currentCommand = NULL;
    1208        2898 :     state->commandList = NIL;
    1209        2898 :     state->previous = currentEventTriggerState;
    1210        2898 :     currentEventTriggerState = state;
    1211             : 
    1212        2898 :     return true;
    1213             : }
    1214             : 
    1215             : /*
    1216             :  * Query completed (or errored out) -- clean up local state, return to previous
    1217             :  * one.
    1218             :  *
    1219             :  * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
    1220             :  * returned false previously.
    1221             :  *
    1222             :  * Note: this might be called in the PG_CATCH block of a failing transaction,
    1223             :  * so be wary of running anything unnecessary.  (In particular, it's probably
    1224             :  * unwise to try to allocate memory.)
    1225             :  */
    1226             : void
    1227        2898 : EventTriggerEndCompleteQuery(void)
    1228             : {
    1229             :     EventTriggerQueryState *prevstate;
    1230             : 
    1231        2898 :     prevstate = currentEventTriggerState->previous;
    1232             : 
    1233             :     /* this avoids the need for retail pfree of SQLDropList items: */
    1234        2898 :     MemoryContextDelete(currentEventTriggerState->cxt);
    1235             : 
    1236        2898 :     currentEventTriggerState = prevstate;
    1237        2898 : }
    1238             : 
    1239             : /*
    1240             :  * Do we need to keep close track of objects being dropped?
    1241             :  *
    1242             :  * This is useful because there is a cost to running with them enabled.
    1243             :  */
    1244             : bool
    1245      261188 : trackDroppedObjectsNeeded(void)
    1246             : {
    1247             :     /*
    1248             :      * true if any sql_drop, table_rewrite, ddl_command_end event trigger
    1249             :      * exists
    1250             :      */
    1251      519376 :     return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
    1252      519376 :         (EventCacheLookup(EVT_TableRewrite) != NIL) ||
    1253      258012 :         (EventCacheLookup(EVT_DDLCommandEnd) != NIL);
    1254             : }
    1255             : 
    1256             : /*
    1257             :  * Support for dropped objects information on event trigger functions.
    1258             :  *
    1259             :  * We keep the list of objects dropped by the current command in current
    1260             :  * state's SQLDropList (comprising SQLDropObject items).  Each time a new
    1261             :  * command is to start, a clean EventTriggerQueryState is created; commands
    1262             :  * that drop objects do the dependency.c dance to drop objects, which
    1263             :  * populates the current state's SQLDropList; when the event triggers are
    1264             :  * invoked they can consume the list via pg_event_trigger_dropped_objects().
    1265             :  * When the command finishes, the EventTriggerQueryState is cleared, and
    1266             :  * the one from the previous command is restored (when no command is in
    1267             :  * execution, the current state is NULL).
    1268             :  *
    1269             :  * All this lets us support the case that an event trigger function drops
    1270             :  * objects "reentrantly".
    1271             :  */
    1272             : 
    1273             : /*
    1274             :  * Register one object as being dropped by the current command.
    1275             :  */
    1276             : void
    1277        3978 : EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
    1278             : {
    1279             :     SQLDropObject *obj;
    1280             :     MemoryContext oldcxt;
    1281             : 
    1282        3978 :     if (!currentEventTriggerState)
    1283         230 :         return;
    1284             : 
    1285             :     Assert(EventTriggerSupportsObject(object));
    1286             : 
    1287        3748 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1288             : 
    1289        3748 :     obj = palloc0(sizeof(SQLDropObject));
    1290        3748 :     obj->address = *object;
    1291        3748 :     obj->original = original;
    1292        3748 :     obj->normal = normal;
    1293             : 
    1294        3748 :     if (object->classId == NamespaceRelationId)
    1295             :     {
    1296             :         /* Special handling is needed for temp namespaces */
    1297          66 :         if (isTempNamespace(object->objectId))
    1298           0 :             obj->istemp = true;
    1299          66 :         else if (isAnyTempNamespace(object->objectId))
    1300             :         {
    1301             :             /* don't report temp schemas except my own */
    1302           0 :             pfree(obj);
    1303           0 :             MemoryContextSwitchTo(oldcxt);
    1304           0 :             return;
    1305             :         }
    1306          66 :         obj->objname = get_namespace_name(object->objectId);
    1307             :     }
    1308        3682 :     else if (object->classId == AttrDefaultRelationId)
    1309             :     {
    1310             :         /* We treat a column default as temp if its table is temp */
    1311             :         ObjectAddress colobject;
    1312             : 
    1313         398 :         colobject = GetAttrDefaultColumnAddress(object->objectId);
    1314         398 :         if (OidIsValid(colobject.objectId))
    1315             :         {
    1316         398 :             if (!obtain_object_name_namespace(&colobject, obj))
    1317             :             {
    1318           0 :                 pfree(obj);
    1319           0 :                 MemoryContextSwitchTo(oldcxt);
    1320           0 :                 return;
    1321             :             }
    1322             :         }
    1323             :     }
    1324        3284 :     else if (object->classId == TriggerRelationId)
    1325             :     {
    1326             :         /* Similarly, a trigger is temp if its table is temp */
    1327             :         /* Sadly, there's no lsyscache.c support for trigger objects */
    1328             :         Relation    pg_trigger_rel;
    1329             :         ScanKeyData skey[1];
    1330             :         SysScanDesc sscan;
    1331             :         HeapTuple   tuple;
    1332             :         Oid         relid;
    1333             : 
    1334             :         /* Fetch the trigger's table OID the hard way */
    1335         108 :         pg_trigger_rel = table_open(TriggerRelationId, AccessShareLock);
    1336         108 :         ScanKeyInit(&skey[0],
    1337             :                     Anum_pg_trigger_oid,
    1338             :                     BTEqualStrategyNumber, F_OIDEQ,
    1339         108 :                     ObjectIdGetDatum(object->objectId));
    1340         108 :         sscan = systable_beginscan(pg_trigger_rel, TriggerOidIndexId, true,
    1341             :                                    NULL, 1, skey);
    1342         108 :         tuple = systable_getnext(sscan);
    1343         108 :         if (HeapTupleIsValid(tuple))
    1344         108 :             relid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid;
    1345             :         else
    1346           0 :             relid = InvalidOid; /* shouldn't happen */
    1347         108 :         systable_endscan(sscan);
    1348         108 :         table_close(pg_trigger_rel, AccessShareLock);
    1349             :         /* Do nothing if we didn't find the trigger */
    1350         108 :         if (OidIsValid(relid))
    1351             :         {
    1352             :             ObjectAddress relobject;
    1353             : 
    1354         108 :             relobject.classId = RelationRelationId;
    1355         108 :             relobject.objectId = relid;
    1356             :             /* Arbitrarily set objectSubId nonzero so as not to fill objname */
    1357         108 :             relobject.objectSubId = 1;
    1358         108 :             if (!obtain_object_name_namespace(&relobject, obj))
    1359             :             {
    1360           0 :                 pfree(obj);
    1361           0 :                 MemoryContextSwitchTo(oldcxt);
    1362           0 :                 return;
    1363             :             }
    1364             :         }
    1365             :     }
    1366        3176 :     else if (object->classId == PolicyRelationId)
    1367             :     {
    1368             :         /* Similarly, a policy is temp if its table is temp */
    1369             :         /* Sadly, there's no lsyscache.c support for policy objects */
    1370             :         Relation    pg_policy_rel;
    1371             :         ScanKeyData skey[1];
    1372             :         SysScanDesc sscan;
    1373             :         HeapTuple   tuple;
    1374             :         Oid         relid;
    1375             : 
    1376             :         /* Fetch the policy's table OID the hard way */
    1377          30 :         pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
    1378          30 :         ScanKeyInit(&skey[0],
    1379             :                     Anum_pg_policy_oid,
    1380             :                     BTEqualStrategyNumber, F_OIDEQ,
    1381          30 :                     ObjectIdGetDatum(object->objectId));
    1382          30 :         sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
    1383             :                                    NULL, 1, skey);
    1384          30 :         tuple = systable_getnext(sscan);
    1385          30 :         if (HeapTupleIsValid(tuple))
    1386          30 :             relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
    1387             :         else
    1388           0 :             relid = InvalidOid; /* shouldn't happen */
    1389          30 :         systable_endscan(sscan);
    1390          30 :         table_close(pg_policy_rel, AccessShareLock);
    1391             :         /* Do nothing if we didn't find the policy */
    1392          30 :         if (OidIsValid(relid))
    1393             :         {
    1394             :             ObjectAddress relobject;
    1395             : 
    1396          30 :             relobject.classId = RelationRelationId;
    1397          30 :             relobject.objectId = relid;
    1398             :             /* Arbitrarily set objectSubId nonzero so as not to fill objname */
    1399          30 :             relobject.objectSubId = 1;
    1400          30 :             if (!obtain_object_name_namespace(&relobject, obj))
    1401             :             {
    1402           0 :                 pfree(obj);
    1403           0 :                 MemoryContextSwitchTo(oldcxt);
    1404           0 :                 return;
    1405             :             }
    1406             :         }
    1407             :     }
    1408             :     else
    1409             :     {
    1410             :         /* Generic handling for all other object classes */
    1411        3146 :         if (!obtain_object_name_namespace(object, obj))
    1412             :         {
    1413             :             /* don't report temp objects except my own */
    1414           0 :             pfree(obj);
    1415           0 :             MemoryContextSwitchTo(oldcxt);
    1416           0 :             return;
    1417             :         }
    1418             :     }
    1419             : 
    1420             :     /* object identity, objname and objargs */
    1421        3748 :     obj->objidentity =
    1422        3748 :         getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
    1423             :                                false);
    1424             : 
    1425             :     /* object type */
    1426        3748 :     obj->objecttype = getObjectTypeDescription(&obj->address, false);
    1427             : 
    1428        3748 :     slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
    1429             : 
    1430        3748 :     MemoryContextSwitchTo(oldcxt);
    1431             : }
    1432             : 
    1433             : /*
    1434             :  * Fill obj->objname, obj->schemaname, and obj->istemp based on object.
    1435             :  *
    1436             :  * Returns true if this object should be reported, false if it should
    1437             :  * be ignored because it is a temporary object of another session.
    1438             :  */
    1439             : static bool
    1440        3682 : obtain_object_name_namespace(const ObjectAddress *object, SQLDropObject *obj)
    1441             : {
    1442             :     /*
    1443             :      * Obtain schema names from the object's catalog tuple, if one exists;
    1444             :      * this lets us skip objects in temp schemas.  We trust that
    1445             :      * ObjectProperty contains all object classes that can be
    1446             :      * schema-qualified.
    1447             :      *
    1448             :      * Currently, this function does nothing for object classes that are not
    1449             :      * in ObjectProperty, but we might sometime add special cases for that.
    1450             :      */
    1451        3682 :     if (is_objectclass_supported(object->classId))
    1452             :     {
    1453             :         Relation    catalog;
    1454             :         HeapTuple   tuple;
    1455             : 
    1456        3682 :         catalog = table_open(object->classId, AccessShareLock);
    1457        3682 :         tuple = get_catalog_object_by_oid(catalog,
    1458        3682 :                                           get_object_attnum_oid(object->classId),
    1459        3682 :                                           object->objectId);
    1460             : 
    1461        3682 :         if (tuple)
    1462             :         {
    1463             :             AttrNumber  attnum;
    1464             :             Datum       datum;
    1465             :             bool        isnull;
    1466             : 
    1467        3682 :             attnum = get_object_attnum_namespace(object->classId);
    1468        3682 :             if (attnum != InvalidAttrNumber)
    1469             :             {
    1470        3632 :                 datum = heap_getattr(tuple, attnum,
    1471             :                                      RelationGetDescr(catalog), &isnull);
    1472        3632 :                 if (!isnull)
    1473             :                 {
    1474             :                     Oid         namespaceId;
    1475             : 
    1476        3632 :                     namespaceId = DatumGetObjectId(datum);
    1477             :                     /* temp objects are only reported if they are my own */
    1478        3632 :                     if (isTempNamespace(namespaceId))
    1479             :                     {
    1480          72 :                         obj->schemaname = "pg_temp";
    1481          72 :                         obj->istemp = true;
    1482             :                     }
    1483        3560 :                     else if (isAnyTempNamespace(namespaceId))
    1484             :                     {
    1485             :                         /* no need to fill any fields of *obj */
    1486           0 :                         table_close(catalog, AccessShareLock);
    1487           0 :                         return false;
    1488             :                     }
    1489             :                     else
    1490             :                     {
    1491        3560 :                         obj->schemaname = get_namespace_name(namespaceId);
    1492        3560 :                         obj->istemp = false;
    1493             :                     }
    1494             :                 }
    1495             :             }
    1496             : 
    1497        3682 :             if (get_object_namensp_unique(object->classId) &&
    1498        3034 :                 object->objectSubId == 0)
    1499             :             {
    1500        2474 :                 attnum = get_object_attnum_name(object->classId);
    1501        2474 :                 if (attnum != InvalidAttrNumber)
    1502             :                 {
    1503        2474 :                     datum = heap_getattr(tuple, attnum,
    1504             :                                          RelationGetDescr(catalog), &isnull);
    1505        2474 :                     if (!isnull)
    1506        2474 :                         obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
    1507             :                 }
    1508             :             }
    1509             :         }
    1510             : 
    1511        3682 :         table_close(catalog, AccessShareLock);
    1512             :     }
    1513             : 
    1514        3682 :     return true;
    1515             : }
    1516             : 
    1517             : /*
    1518             :  * pg_event_trigger_dropped_objects
    1519             :  *
    1520             :  * Make the list of dropped objects available to the user function run by the
    1521             :  * Event Trigger.
    1522             :  */
    1523             : Datum
    1524         138 : pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
    1525             : {
    1526         138 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    1527             :     slist_iter  iter;
    1528             : 
    1529             :     /*
    1530             :      * Protect this function from being called out of context
    1531             :      */
    1532         138 :     if (!currentEventTriggerState ||
    1533         138 :         !currentEventTriggerState->in_sql_drop)
    1534           0 :         ereport(ERROR,
    1535             :                 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
    1536             :                  errmsg("%s can only be called in a sql_drop event trigger function",
    1537             :                         "pg_event_trigger_dropped_objects()")));
    1538             : 
    1539             :     /* Build tuplestore to hold the result rows */
    1540         138 :     InitMaterializedSRF(fcinfo, 0);
    1541             : 
    1542        1362 :     slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
    1543             :     {
    1544             :         SQLDropObject *obj;
    1545        1224 :         int         i = 0;
    1546        1224 :         Datum       values[12] = {0};
    1547        1224 :         bool        nulls[12] = {0};
    1548             : 
    1549        1224 :         obj = slist_container(SQLDropObject, next, iter.cur);
    1550             : 
    1551             :         /* classid */
    1552        1224 :         values[i++] = ObjectIdGetDatum(obj->address.classId);
    1553             : 
    1554             :         /* objid */
    1555        1224 :         values[i++] = ObjectIdGetDatum(obj->address.objectId);
    1556             : 
    1557             :         /* objsubid */
    1558        1224 :         values[i++] = Int32GetDatum(obj->address.objectSubId);
    1559             : 
    1560             :         /* original */
    1561        1224 :         values[i++] = BoolGetDatum(obj->original);
    1562             : 
    1563             :         /* normal */
    1564        1224 :         values[i++] = BoolGetDatum(obj->normal);
    1565             : 
    1566             :         /* is_temporary */
    1567        1224 :         values[i++] = BoolGetDatum(obj->istemp);
    1568             : 
    1569             :         /* object_type */
    1570        1224 :         values[i++] = CStringGetTextDatum(obj->objecttype);
    1571             : 
    1572             :         /* schema_name */
    1573        1224 :         if (obj->schemaname)
    1574        1152 :             values[i++] = CStringGetTextDatum(obj->schemaname);
    1575             :         else
    1576          72 :             nulls[i++] = true;
    1577             : 
    1578             :         /* object_name */
    1579        1224 :         if (obj->objname)
    1580         924 :             values[i++] = CStringGetTextDatum(obj->objname);
    1581             :         else
    1582         300 :             nulls[i++] = true;
    1583             : 
    1584             :         /* object_identity */
    1585        1224 :         if (obj->objidentity)
    1586        1224 :             values[i++] = CStringGetTextDatum(obj->objidentity);
    1587             :         else
    1588           0 :             nulls[i++] = true;
    1589             : 
    1590             :         /* address_names and address_args */
    1591        1224 :         if (obj->addrnames)
    1592             :         {
    1593        1224 :             values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
    1594             : 
    1595        1224 :             if (obj->addrargs)
    1596          60 :                 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
    1597             :             else
    1598        1164 :                 values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
    1599             :         }
    1600             :         else
    1601             :         {
    1602           0 :             nulls[i++] = true;
    1603           0 :             nulls[i++] = true;
    1604             :         }
    1605             : 
    1606        1224 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
    1607             :                              values, nulls);
    1608             :     }
    1609             : 
    1610         138 :     return (Datum) 0;
    1611             : }
    1612             : 
    1613             : /*
    1614             :  * pg_event_trigger_table_rewrite_oid
    1615             :  *
    1616             :  * Make the Oid of the table going to be rewritten available to the user
    1617             :  * function run by the Event Trigger.
    1618             :  */
    1619             : Datum
    1620         126 : pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
    1621             : {
    1622             :     /*
    1623             :      * Protect this function from being called out of context
    1624             :      */
    1625         126 :     if (!currentEventTriggerState ||
    1626         120 :         currentEventTriggerState->table_rewrite_oid == InvalidOid)
    1627           6 :         ereport(ERROR,
    1628             :                 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
    1629             :                  errmsg("%s can only be called in a table_rewrite event trigger function",
    1630             :                         "pg_event_trigger_table_rewrite_oid()")));
    1631             : 
    1632         120 :     PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
    1633             : }
    1634             : 
    1635             : /*
    1636             :  * pg_event_trigger_table_rewrite_reason
    1637             :  *
    1638             :  * Make the rewrite reason available to the user.
    1639             :  */
    1640             : Datum
    1641          78 : pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
    1642             : {
    1643             :     /*
    1644             :      * Protect this function from being called out of context
    1645             :      */
    1646          78 :     if (!currentEventTriggerState ||
    1647          78 :         currentEventTriggerState->table_rewrite_reason == 0)
    1648           0 :         ereport(ERROR,
    1649             :                 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
    1650             :                  errmsg("%s can only be called in a table_rewrite event trigger function",
    1651             :                         "pg_event_trigger_table_rewrite_reason()")));
    1652             : 
    1653          78 :     PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
    1654             : }
    1655             : 
    1656             : /*-------------------------------------------------------------------------
    1657             :  * Support for DDL command deparsing
    1658             :  *
    1659             :  * The routines below enable an event trigger function to obtain a list of
    1660             :  * DDL commands as they are executed.  There are three main pieces to this
    1661             :  * feature:
    1662             :  *
    1663             :  * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
    1664             :  * adds a struct CollectedCommand representation of itself to the command list,
    1665             :  * using the routines below.
    1666             :  *
    1667             :  * 2) Some time after that, ddl_command_end fires and the command list is made
    1668             :  * available to the event trigger function via pg_event_trigger_ddl_commands();
    1669             :  * the complete command details are exposed as a column of type pg_ddl_command.
    1670             :  *
    1671             :  * 3) An extension can install a function capable of taking a value of type
    1672             :  * pg_ddl_command and transform it into some external, user-visible and/or
    1673             :  * -modifiable representation.
    1674             :  *-------------------------------------------------------------------------
    1675             :  */
    1676             : 
    1677             : /*
    1678             :  * Inhibit DDL command collection.
    1679             :  */
    1680             : void
    1681         268 : EventTriggerInhibitCommandCollection(void)
    1682             : {
    1683         268 :     if (!currentEventTriggerState)
    1684         260 :         return;
    1685             : 
    1686           8 :     currentEventTriggerState->commandCollectionInhibited = true;
    1687             : }
    1688             : 
    1689             : /*
    1690             :  * Re-establish DDL command collection.
    1691             :  */
    1692             : void
    1693         268 : EventTriggerUndoInhibitCommandCollection(void)
    1694             : {
    1695         268 :     if (!currentEventTriggerState)
    1696         260 :         return;
    1697             : 
    1698           8 :     currentEventTriggerState->commandCollectionInhibited = false;
    1699             : }
    1700             : 
    1701             : /*
    1702             :  * EventTriggerCollectSimpleCommand
    1703             :  *      Save data about a simple DDL command that was just executed
    1704             :  *
    1705             :  * address identifies the object being operated on.  secondaryObject is an
    1706             :  * object address that was related in some way to the executed command; its
    1707             :  * meaning is command-specific.
    1708             :  *
    1709             :  * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
    1710             :  * object being moved, objectId is its OID, and secondaryOid is the OID of the
    1711             :  * old schema.  (The destination schema OID can be obtained by catalog lookup
    1712             :  * of the object.)
    1713             :  */
    1714             : void
    1715      139494 : EventTriggerCollectSimpleCommand(ObjectAddress address,
    1716             :                                  ObjectAddress secondaryObject,
    1717             :                                  Node *parsetree)
    1718             : {
    1719             :     MemoryContext oldcxt;
    1720             :     CollectedCommand *command;
    1721             : 
    1722             :     /* ignore if event trigger context not set, or collection disabled */
    1723      139494 :     if (!currentEventTriggerState ||
    1724        1702 :         currentEventTriggerState->commandCollectionInhibited)
    1725      137792 :         return;
    1726             : 
    1727        1702 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1728             : 
    1729        1702 :     command = palloc(sizeof(CollectedCommand));
    1730             : 
    1731        1702 :     command->type = SCT_Simple;
    1732        1702 :     command->in_extension = creating_extension;
    1733             : 
    1734        1702 :     command->d.simple.address = address;
    1735        1702 :     command->d.simple.secondaryObject = secondaryObject;
    1736        1702 :     command->parsetree = copyObject(parsetree);
    1737             : 
    1738        1702 :     currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
    1739             :                                                     command);
    1740             : 
    1741        1702 :     MemoryContextSwitchTo(oldcxt);
    1742             : }
    1743             : 
    1744             : /*
    1745             :  * EventTriggerAlterTableStart
    1746             :  *      Prepare to receive data on an ALTER TABLE command about to be executed
    1747             :  *
    1748             :  * Note we don't collect the command immediately; instead we keep it in
    1749             :  * currentCommand, and only when we're done processing the subcommands we will
    1750             :  * add it to the command list.
    1751             :  */
    1752             : void
    1753       63526 : EventTriggerAlterTableStart(Node *parsetree)
    1754             : {
    1755             :     MemoryContext oldcxt;
    1756             :     CollectedCommand *command;
    1757             : 
    1758             :     /* ignore if event trigger context not set, or collection disabled */
    1759       63526 :     if (!currentEventTriggerState ||
    1760        1210 :         currentEventTriggerState->commandCollectionInhibited)
    1761       62316 :         return;
    1762             : 
    1763        1210 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1764             : 
    1765        1210 :     command = palloc(sizeof(CollectedCommand));
    1766             : 
    1767        1210 :     command->type = SCT_AlterTable;
    1768        1210 :     command->in_extension = creating_extension;
    1769             : 
    1770        1210 :     command->d.alterTable.classId = RelationRelationId;
    1771        1210 :     command->d.alterTable.objectId = InvalidOid;
    1772        1210 :     command->d.alterTable.subcmds = NIL;
    1773        1210 :     command->parsetree = copyObject(parsetree);
    1774             : 
    1775        1210 :     command->parent = currentEventTriggerState->currentCommand;
    1776        1210 :     currentEventTriggerState->currentCommand = command;
    1777             : 
    1778        1210 :     MemoryContextSwitchTo(oldcxt);
    1779             : }
    1780             : 
    1781             : /*
    1782             :  * Remember the OID of the object being affected by an ALTER TABLE.
    1783             :  *
    1784             :  * This is needed because in some cases we don't know the OID until later.
    1785             :  */
    1786             : void
    1787       32862 : EventTriggerAlterTableRelid(Oid objectId)
    1788             : {
    1789       32862 :     if (!currentEventTriggerState ||
    1790         890 :         currentEventTriggerState->commandCollectionInhibited)
    1791       31972 :         return;
    1792             : 
    1793         890 :     currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
    1794             : }
    1795             : 
    1796             : /*
    1797             :  * EventTriggerCollectAlterTableSubcmd
    1798             :  *      Save data about a single part of an ALTER TABLE.
    1799             :  *
    1800             :  * Several different commands go through this path, but apart from ALTER TABLE
    1801             :  * itself, they are all concerned with AlterTableCmd nodes that are generated
    1802             :  * internally, so that's all that this code needs to handle at the moment.
    1803             :  */
    1804             : void
    1805       40330 : EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
    1806             : {
    1807             :     MemoryContext oldcxt;
    1808             :     CollectedATSubcmd *newsub;
    1809             : 
    1810             :     /* ignore if event trigger context not set, or collection disabled */
    1811       40330 :     if (!currentEventTriggerState ||
    1812        1442 :         currentEventTriggerState->commandCollectionInhibited)
    1813       38888 :         return;
    1814             : 
    1815             :     Assert(IsA(subcmd, AlterTableCmd));
    1816             :     Assert(currentEventTriggerState->currentCommand != NULL);
    1817             :     Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
    1818             : 
    1819        1442 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1820             : 
    1821        1442 :     newsub = palloc(sizeof(CollectedATSubcmd));
    1822        1442 :     newsub->address = address;
    1823        1442 :     newsub->parsetree = copyObject(subcmd);
    1824             : 
    1825        2884 :     currentEventTriggerState->currentCommand->d.alterTable.subcmds =
    1826        1442 :         lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
    1827             : 
    1828        1442 :     MemoryContextSwitchTo(oldcxt);
    1829             : }
    1830             : 
    1831             : /*
    1832             :  * EventTriggerAlterTableEnd
    1833             :  *      Finish up saving an ALTER TABLE command, and add it to command list.
    1834             :  *
    1835             :  * FIXME this API isn't considering the possibility that an xact/subxact is
    1836             :  * aborted partway through.  Probably it's best to add an
    1837             :  * AtEOSubXact_EventTriggers() to fix this.
    1838             :  */
    1839             : void
    1840       59152 : EventTriggerAlterTableEnd(void)
    1841             : {
    1842             :     CollectedCommand *parent;
    1843             : 
    1844             :     /* ignore if event trigger context not set, or collection disabled */
    1845       59152 :     if (!currentEventTriggerState ||
    1846        1180 :         currentEventTriggerState->commandCollectionInhibited)
    1847       57972 :         return;
    1848             : 
    1849        1180 :     parent = currentEventTriggerState->currentCommand->parent;
    1850             : 
    1851             :     /* If no subcommands, don't collect */
    1852        1180 :     if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
    1853             :     {
    1854             :         MemoryContext oldcxt;
    1855             : 
    1856         850 :         oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1857             : 
    1858        1700 :         currentEventTriggerState->commandList =
    1859         850 :             lappend(currentEventTriggerState->commandList,
    1860         850 :                     currentEventTriggerState->currentCommand);
    1861             : 
    1862         850 :         MemoryContextSwitchTo(oldcxt);
    1863             :     }
    1864             :     else
    1865         330 :         pfree(currentEventTriggerState->currentCommand);
    1866             : 
    1867        1180 :     currentEventTriggerState->currentCommand = parent;
    1868             : }
    1869             : 
    1870             : /*
    1871             :  * EventTriggerCollectGrant
    1872             :  *      Save data about a GRANT/REVOKE command being executed
    1873             :  *
    1874             :  * This function creates a copy of the InternalGrant, as the original might
    1875             :  * not have the right lifetime.
    1876             :  */
    1877             : void
    1878       28796 : EventTriggerCollectGrant(InternalGrant *istmt)
    1879             : {
    1880             :     MemoryContext oldcxt;
    1881             :     CollectedCommand *command;
    1882             :     InternalGrant *icopy;
    1883             :     ListCell   *cell;
    1884             : 
    1885             :     /* ignore if event trigger context not set, or collection disabled */
    1886       28796 :     if (!currentEventTriggerState ||
    1887          50 :         currentEventTriggerState->commandCollectionInhibited)
    1888       28746 :         return;
    1889             : 
    1890          50 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1891             : 
    1892             :     /*
    1893             :      * This is tedious, but necessary.
    1894             :      */
    1895          50 :     icopy = palloc(sizeof(InternalGrant));
    1896          50 :     memcpy(icopy, istmt, sizeof(InternalGrant));
    1897          50 :     icopy->objects = list_copy(istmt->objects);
    1898          50 :     icopy->grantees = list_copy(istmt->grantees);
    1899          50 :     icopy->col_privs = NIL;
    1900          50 :     foreach(cell, istmt->col_privs)
    1901           0 :         icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
    1902             : 
    1903             :     /* Now collect it, using the copied InternalGrant */
    1904          50 :     command = palloc(sizeof(CollectedCommand));
    1905          50 :     command->type = SCT_Grant;
    1906          50 :     command->in_extension = creating_extension;
    1907          50 :     command->d.grant.istmt = icopy;
    1908          50 :     command->parsetree = NULL;
    1909             : 
    1910         100 :     currentEventTriggerState->commandList =
    1911          50 :         lappend(currentEventTriggerState->commandList, command);
    1912             : 
    1913          50 :     MemoryContextSwitchTo(oldcxt);
    1914             : }
    1915             : 
    1916             : /*
    1917             :  * EventTriggerCollectAlterOpFam
    1918             :  *      Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
    1919             :  *      executed
    1920             :  */
    1921             : void
    1922         758 : EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
    1923             :                               List *operators, List *procedures)
    1924             : {
    1925             :     MemoryContext oldcxt;
    1926             :     CollectedCommand *command;
    1927             : 
    1928             :     /* ignore if event trigger context not set, or collection disabled */
    1929         758 :     if (!currentEventTriggerState ||
    1930           2 :         currentEventTriggerState->commandCollectionInhibited)
    1931         756 :         return;
    1932             : 
    1933           2 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1934             : 
    1935           2 :     command = palloc(sizeof(CollectedCommand));
    1936           2 :     command->type = SCT_AlterOpFamily;
    1937           2 :     command->in_extension = creating_extension;
    1938           2 :     ObjectAddressSet(command->d.opfam.address,
    1939             :                      OperatorFamilyRelationId, opfamoid);
    1940           2 :     command->d.opfam.operators = operators;
    1941           2 :     command->d.opfam.procedures = procedures;
    1942           2 :     command->parsetree = (Node *) copyObject(stmt);
    1943             : 
    1944           4 :     currentEventTriggerState->commandList =
    1945           2 :         lappend(currentEventTriggerState->commandList, command);
    1946             : 
    1947           2 :     MemoryContextSwitchTo(oldcxt);
    1948             : }
    1949             : 
    1950             : /*
    1951             :  * EventTriggerCollectCreateOpClass
    1952             :  *      Save data about a CREATE OPERATOR CLASS command being executed
    1953             :  */
    1954             : void
    1955         556 : EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
    1956             :                                  List *operators, List *procedures)
    1957             : {
    1958             :     MemoryContext oldcxt;
    1959             :     CollectedCommand *command;
    1960             : 
    1961             :     /* ignore if event trigger context not set, or collection disabled */
    1962         556 :     if (!currentEventTriggerState ||
    1963           8 :         currentEventTriggerState->commandCollectionInhibited)
    1964         548 :         return;
    1965             : 
    1966           8 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1967             : 
    1968           8 :     command = palloc0(sizeof(CollectedCommand));
    1969           8 :     command->type = SCT_CreateOpClass;
    1970           8 :     command->in_extension = creating_extension;
    1971           8 :     ObjectAddressSet(command->d.createopc.address,
    1972             :                      OperatorClassRelationId, opcoid);
    1973           8 :     command->d.createopc.operators = operators;
    1974           8 :     command->d.createopc.procedures = procedures;
    1975           8 :     command->parsetree = (Node *) copyObject(stmt);
    1976             : 
    1977          16 :     currentEventTriggerState->commandList =
    1978           8 :         lappend(currentEventTriggerState->commandList, command);
    1979             : 
    1980           8 :     MemoryContextSwitchTo(oldcxt);
    1981             : }
    1982             : 
    1983             : /*
    1984             :  * EventTriggerCollectAlterTSConfig
    1985             :  *      Save data about an ALTER TEXT SEARCH CONFIGURATION command being
    1986             :  *      executed
    1987             :  */
    1988             : void
    1989        8568 : EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
    1990             :                                  Oid *dictIds, int ndicts)
    1991             : {
    1992             :     MemoryContext oldcxt;
    1993             :     CollectedCommand *command;
    1994             : 
    1995             :     /* ignore if event trigger context not set, or collection disabled */
    1996        8568 :     if (!currentEventTriggerState ||
    1997           2 :         currentEventTriggerState->commandCollectionInhibited)
    1998        8566 :         return;
    1999             : 
    2000           2 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    2001             : 
    2002           2 :     command = palloc0(sizeof(CollectedCommand));
    2003           2 :     command->type = SCT_AlterTSConfig;
    2004           2 :     command->in_extension = creating_extension;
    2005           2 :     ObjectAddressSet(command->d.atscfg.address,
    2006             :                      TSConfigRelationId, cfgId);
    2007           2 :     command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
    2008           2 :     memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
    2009           2 :     command->d.atscfg.ndicts = ndicts;
    2010           2 :     command->parsetree = (Node *) copyObject(stmt);
    2011             : 
    2012           4 :     currentEventTriggerState->commandList =
    2013           2 :         lappend(currentEventTriggerState->commandList, command);
    2014             : 
    2015           2 :     MemoryContextSwitchTo(oldcxt);
    2016             : }
    2017             : 
    2018             : /*
    2019             :  * EventTriggerCollectAlterDefPrivs
    2020             :  *      Save data about an ALTER DEFAULT PRIVILEGES command being
    2021             :  *      executed
    2022             :  */
    2023             : void
    2024         194 : EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
    2025             : {
    2026             :     MemoryContext oldcxt;
    2027             :     CollectedCommand *command;
    2028             : 
    2029             :     /* ignore if event trigger context not set, or collection disabled */
    2030         194 :     if (!currentEventTriggerState ||
    2031           8 :         currentEventTriggerState->commandCollectionInhibited)
    2032         186 :         return;
    2033             : 
    2034           8 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    2035             : 
    2036           8 :     command = palloc0(sizeof(CollectedCommand));
    2037           8 :     command->type = SCT_AlterDefaultPrivileges;
    2038           8 :     command->d.defprivs.objtype = stmt->action->objtype;
    2039           8 :     command->in_extension = creating_extension;
    2040           8 :     command->parsetree = (Node *) copyObject(stmt);
    2041             : 
    2042          16 :     currentEventTriggerState->commandList =
    2043           8 :         lappend(currentEventTriggerState->commandList, command);
    2044           8 :     MemoryContextSwitchTo(oldcxt);
    2045             : }
    2046             : 
    2047             : /*
    2048             :  * In a ddl_command_end event trigger, this function reports the DDL commands
    2049             :  * being run.
    2050             :  */
    2051             : Datum
    2052         658 : pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
    2053             : {
    2054         658 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    2055             :     ListCell   *lc;
    2056             : 
    2057             :     /*
    2058             :      * Protect this function from being called out of context
    2059             :      */
    2060         658 :     if (!currentEventTriggerState)
    2061           0 :         ereport(ERROR,
    2062             :                 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
    2063             :                  errmsg("%s can only be called in an event trigger function",
    2064             :                         "pg_event_trigger_ddl_commands()")));
    2065             : 
    2066             :     /* Build tuplestore to hold the result rows */
    2067         658 :     InitMaterializedSRF(fcinfo, 0);
    2068             : 
    2069        1348 :     foreach(lc, currentEventTriggerState->commandList)
    2070             :     {
    2071         690 :         CollectedCommand *cmd = lfirst(lc);
    2072             :         Datum       values[9];
    2073         690 :         bool        nulls[9] = {0};
    2074             :         ObjectAddress addr;
    2075         690 :         int         i = 0;
    2076             : 
    2077             :         /*
    2078             :          * For IF NOT EXISTS commands that attempt to create an existing
    2079             :          * object, the returned OID is Invalid.  Don't return anything.
    2080             :          *
    2081             :          * One might think that a viable alternative would be to look up the
    2082             :          * Oid of the existing object and run the deparse with that.  But
    2083             :          * since the parse tree might be different from the one that created
    2084             :          * the object in the first place, we might not end up in a consistent
    2085             :          * state anyway.
    2086             :          */
    2087         690 :         if (cmd->type == SCT_Simple &&
    2088         546 :             !OidIsValid(cmd->d.simple.address.objectId))
    2089           6 :             continue;
    2090             : 
    2091         690 :         switch (cmd->type)
    2092             :         {
    2093         662 :             case SCT_Simple:
    2094             :             case SCT_AlterTable:
    2095             :             case SCT_AlterOpFamily:
    2096             :             case SCT_CreateOpClass:
    2097             :             case SCT_AlterTSConfig:
    2098             :                 {
    2099             :                     char       *identity;
    2100             :                     char       *type;
    2101         662 :                     char       *schema = NULL;
    2102             : 
    2103         662 :                     if (cmd->type == SCT_Simple)
    2104         546 :                         addr = cmd->d.simple.address;
    2105         116 :                     else if (cmd->type == SCT_AlterTable)
    2106         104 :                         ObjectAddressSet(addr,
    2107             :                                          cmd->d.alterTable.classId,
    2108             :                                          cmd->d.alterTable.objectId);
    2109          12 :                     else if (cmd->type == SCT_AlterOpFamily)
    2110           2 :                         addr = cmd->d.opfam.address;
    2111          10 :                     else if (cmd->type == SCT_CreateOpClass)
    2112           8 :                         addr = cmd->d.createopc.address;
    2113           2 :                     else if (cmd->type == SCT_AlterTSConfig)
    2114           2 :                         addr = cmd->d.atscfg.address;
    2115             : 
    2116             :                     /*
    2117             :                      * If an object was dropped in the same command we may end
    2118             :                      * up in a situation where we generated a message but can
    2119             :                      * no longer look for the object information, so skip it
    2120             :                      * rather than failing.  This can happen for example with
    2121             :                      * some subcommand combinations of ALTER TABLE.
    2122             :                      */
    2123         662 :                     identity = getObjectIdentity(&addr, true);
    2124         662 :                     if (identity == NULL)
    2125           6 :                         continue;
    2126             : 
    2127             :                     /* The type can never be NULL. */
    2128         656 :                     type = getObjectTypeDescription(&addr, true);
    2129             : 
    2130             :                     /*
    2131             :                      * Obtain schema name, if any ("pg_temp" if a temp
    2132             :                      * object). If the object class is not in the supported
    2133             :                      * list here, we assume it's a schema-less object type,
    2134             :                      * and thus "schema" remains set to NULL.
    2135             :                      */
    2136         656 :                     if (is_objectclass_supported(addr.classId))
    2137             :                     {
    2138             :                         AttrNumber  nspAttnum;
    2139             : 
    2140         656 :                         nspAttnum = get_object_attnum_namespace(addr.classId);
    2141         656 :                         if (nspAttnum != InvalidAttrNumber)
    2142             :                         {
    2143             :                             Relation    catalog;
    2144             :                             HeapTuple   objtup;
    2145             :                             Oid         schema_oid;
    2146             :                             bool        isnull;
    2147             : 
    2148         568 :                             catalog = table_open(addr.classId, AccessShareLock);
    2149         568 :                             objtup = get_catalog_object_by_oid(catalog,
    2150         568 :                                                                get_object_attnum_oid(addr.classId),
    2151             :                                                                addr.objectId);
    2152         568 :                             if (!HeapTupleIsValid(objtup))
    2153           0 :                                 elog(ERROR, "cache lookup failed for object %u/%u",
    2154             :                                      addr.classId, addr.objectId);
    2155             :                             schema_oid =
    2156         568 :                                 DatumGetObjectId(heap_getattr(objtup, nspAttnum,
    2157             :                                                               RelationGetDescr(catalog), &isnull));
    2158         568 :                             if (isnull)
    2159           0 :                                 elog(ERROR,
    2160             :                                      "invalid null namespace in object %u/%u/%d",
    2161             :                                      addr.classId, addr.objectId, addr.objectSubId);
    2162         568 :                             schema = get_namespace_name_or_temp(schema_oid);
    2163             : 
    2164         568 :                             table_close(catalog, AccessShareLock);
    2165             :                         }
    2166             :                     }
    2167             : 
    2168             :                     /* classid */
    2169         656 :                     values[i++] = ObjectIdGetDatum(addr.classId);
    2170             :                     /* objid */
    2171         656 :                     values[i++] = ObjectIdGetDatum(addr.objectId);
    2172             :                     /* objsubid */
    2173         656 :                     values[i++] = Int32GetDatum(addr.objectSubId);
    2174             :                     /* command tag */
    2175         656 :                     values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
    2176             :                     /* object_type */
    2177         656 :                     values[i++] = CStringGetTextDatum(type);
    2178             :                     /* schema */
    2179         656 :                     if (schema == NULL)
    2180          88 :                         nulls[i++] = true;
    2181             :                     else
    2182         568 :                         values[i++] = CStringGetTextDatum(schema);
    2183             :                     /* identity */
    2184         656 :                     values[i++] = CStringGetTextDatum(identity);
    2185             :                     /* in_extension */
    2186         656 :                     values[i++] = BoolGetDatum(cmd->in_extension);
    2187             :                     /* command */
    2188         656 :                     values[i++] = PointerGetDatum(cmd);
    2189             :                 }
    2190         656 :                 break;
    2191             : 
    2192           2 :             case SCT_AlterDefaultPrivileges:
    2193             :                 /* classid */
    2194           2 :                 nulls[i++] = true;
    2195             :                 /* objid */
    2196           2 :                 nulls[i++] = true;
    2197             :                 /* objsubid */
    2198           2 :                 nulls[i++] = true;
    2199             :                 /* command tag */
    2200           2 :                 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
    2201             :                 /* object_type */
    2202           2 :                 values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
    2203             :                 /* schema */
    2204           2 :                 nulls[i++] = true;
    2205             :                 /* identity */
    2206           2 :                 nulls[i++] = true;
    2207             :                 /* in_extension */
    2208           2 :                 values[i++] = BoolGetDatum(cmd->in_extension);
    2209             :                 /* command */
    2210           2 :                 values[i++] = PointerGetDatum(cmd);
    2211           2 :                 break;
    2212             : 
    2213          26 :             case SCT_Grant:
    2214             :                 /* classid */
    2215          26 :                 nulls[i++] = true;
    2216             :                 /* objid */
    2217          26 :                 nulls[i++] = true;
    2218             :                 /* objsubid */
    2219          26 :                 nulls[i++] = true;
    2220             :                 /* command tag */
    2221          26 :                 values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
    2222             :                                                   "GRANT" : "REVOKE");
    2223             :                 /* object_type */
    2224          26 :                 values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
    2225             :                 /* schema */
    2226          26 :                 nulls[i++] = true;
    2227             :                 /* identity */
    2228          26 :                 nulls[i++] = true;
    2229             :                 /* in_extension */
    2230          26 :                 values[i++] = BoolGetDatum(cmd->in_extension);
    2231             :                 /* command */
    2232          26 :                 values[i++] = PointerGetDatum(cmd);
    2233          26 :                 break;
    2234             :         }
    2235             : 
    2236         684 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
    2237             :                              values, nulls);
    2238             :     }
    2239             : 
    2240         658 :     PG_RETURN_VOID();
    2241             : }
    2242             : 
    2243             : /*
    2244             :  * Return the ObjectType as a string, as it would appear in GRANT and
    2245             :  * REVOKE commands.
    2246             :  */
    2247             : static const char *
    2248          26 : stringify_grant_objtype(ObjectType objtype)
    2249             : {
    2250          26 :     switch (objtype)
    2251             :     {
    2252           0 :         case OBJECT_COLUMN:
    2253           0 :             return "COLUMN";
    2254          16 :         case OBJECT_TABLE:
    2255          16 :             return "TABLE";
    2256           0 :         case OBJECT_SEQUENCE:
    2257           0 :             return "SEQUENCE";
    2258           0 :         case OBJECT_DATABASE:
    2259           0 :             return "DATABASE";
    2260           0 :         case OBJECT_DOMAIN:
    2261           0 :             return "DOMAIN";
    2262           0 :         case OBJECT_FDW:
    2263           0 :             return "FOREIGN DATA WRAPPER";
    2264           0 :         case OBJECT_FOREIGN_SERVER:
    2265           0 :             return "FOREIGN SERVER";
    2266          10 :         case OBJECT_FUNCTION:
    2267          10 :             return "FUNCTION";
    2268           0 :         case OBJECT_LANGUAGE:
    2269           0 :             return "LANGUAGE";
    2270           0 :         case OBJECT_LARGEOBJECT:
    2271           0 :             return "LARGE OBJECT";
    2272           0 :         case OBJECT_SCHEMA:
    2273           0 :             return "SCHEMA";
    2274           0 :         case OBJECT_PARAMETER_ACL:
    2275           0 :             return "PARAMETER";
    2276           0 :         case OBJECT_PROCEDURE:
    2277           0 :             return "PROCEDURE";
    2278           0 :         case OBJECT_ROUTINE:
    2279           0 :             return "ROUTINE";
    2280           0 :         case OBJECT_TABLESPACE:
    2281           0 :             return "TABLESPACE";
    2282           0 :         case OBJECT_TYPE:
    2283           0 :             return "TYPE";
    2284             :             /* these currently aren't used */
    2285           0 :         case OBJECT_ACCESS_METHOD:
    2286             :         case OBJECT_AGGREGATE:
    2287             :         case OBJECT_AMOP:
    2288             :         case OBJECT_AMPROC:
    2289             :         case OBJECT_ATTRIBUTE:
    2290             :         case OBJECT_CAST:
    2291             :         case OBJECT_COLLATION:
    2292             :         case OBJECT_CONVERSION:
    2293             :         case OBJECT_DEFAULT:
    2294             :         case OBJECT_DEFACL:
    2295             :         case OBJECT_DOMCONSTRAINT:
    2296             :         case OBJECT_EVENT_TRIGGER:
    2297             :         case OBJECT_EXTENSION:
    2298             :         case OBJECT_FOREIGN_TABLE:
    2299             :         case OBJECT_INDEX:
    2300             :         case OBJECT_MATVIEW:
    2301             :         case OBJECT_OPCLASS:
    2302             :         case OBJECT_OPERATOR:
    2303             :         case OBJECT_OPFAMILY:
    2304             :         case OBJECT_POLICY:
    2305             :         case OBJECT_PUBLICATION:
    2306             :         case OBJECT_PUBLICATION_NAMESPACE:
    2307             :         case OBJECT_PUBLICATION_REL:
    2308             :         case OBJECT_ROLE:
    2309             :         case OBJECT_RULE:
    2310             :         case OBJECT_STATISTIC_EXT:
    2311             :         case OBJECT_SUBSCRIPTION:
    2312             :         case OBJECT_TABCONSTRAINT:
    2313             :         case OBJECT_TRANSFORM:
    2314             :         case OBJECT_TRIGGER:
    2315             :         case OBJECT_TSCONFIGURATION:
    2316             :         case OBJECT_TSDICTIONARY:
    2317             :         case OBJECT_TSPARSER:
    2318             :         case OBJECT_TSTEMPLATE:
    2319             :         case OBJECT_USER_MAPPING:
    2320             :         case OBJECT_VIEW:
    2321           0 :             elog(ERROR, "unsupported object type: %d", (int) objtype);
    2322             :     }
    2323             : 
    2324           0 :     return "???";             /* keep compiler quiet */
    2325             : }
    2326             : 
    2327             : /*
    2328             :  * Return the ObjectType as a string; as above, but use the spelling
    2329             :  * in ALTER DEFAULT PRIVILEGES commands instead.  Generally this is just
    2330             :  * the plural.
    2331             :  */
    2332             : static const char *
    2333           2 : stringify_adefprivs_objtype(ObjectType objtype)
    2334             : {
    2335           2 :     switch (objtype)
    2336             :     {
    2337           0 :         case OBJECT_COLUMN:
    2338           0 :             return "COLUMNS";
    2339           2 :         case OBJECT_TABLE:
    2340           2 :             return "TABLES";
    2341           0 :         case OBJECT_SEQUENCE:
    2342           0 :             return "SEQUENCES";
    2343           0 :         case OBJECT_DATABASE:
    2344           0 :             return "DATABASES";
    2345           0 :         case OBJECT_DOMAIN:
    2346           0 :             return "DOMAINS";
    2347           0 :         case OBJECT_FDW:
    2348           0 :             return "FOREIGN DATA WRAPPERS";
    2349           0 :         case OBJECT_FOREIGN_SERVER:
    2350           0 :             return "FOREIGN SERVERS";
    2351           0 :         case OBJECT_FUNCTION:
    2352           0 :             return "FUNCTIONS";
    2353           0 :         case OBJECT_LANGUAGE:
    2354           0 :             return "LANGUAGES";
    2355           0 :         case OBJECT_LARGEOBJECT:
    2356           0 :             return "LARGE OBJECTS";
    2357           0 :         case OBJECT_SCHEMA:
    2358           0 :             return "SCHEMAS";
    2359           0 :         case OBJECT_PROCEDURE:
    2360           0 :             return "PROCEDURES";
    2361           0 :         case OBJECT_ROUTINE:
    2362           0 :             return "ROUTINES";
    2363           0 :         case OBJECT_TABLESPACE:
    2364           0 :             return "TABLESPACES";
    2365           0 :         case OBJECT_TYPE:
    2366           0 :             return "TYPES";
    2367             :             /* these currently aren't used */
    2368           0 :         case OBJECT_ACCESS_METHOD:
    2369             :         case OBJECT_AGGREGATE:
    2370             :         case OBJECT_AMOP:
    2371             :         case OBJECT_AMPROC:
    2372             :         case OBJECT_ATTRIBUTE:
    2373             :         case OBJECT_CAST:
    2374             :         case OBJECT_COLLATION:
    2375             :         case OBJECT_CONVERSION:
    2376             :         case OBJECT_DEFAULT:
    2377             :         case OBJECT_DEFACL:
    2378             :         case OBJECT_DOMCONSTRAINT:
    2379             :         case OBJECT_EVENT_TRIGGER:
    2380             :         case OBJECT_EXTENSION:
    2381             :         case OBJECT_FOREIGN_TABLE:
    2382             :         case OBJECT_INDEX:
    2383             :         case OBJECT_MATVIEW:
    2384             :         case OBJECT_OPCLASS:
    2385             :         case OBJECT_OPERATOR:
    2386             :         case OBJECT_OPFAMILY:
    2387             :         case OBJECT_PARAMETER_ACL:
    2388             :         case OBJECT_POLICY:
    2389             :         case OBJECT_PUBLICATION:
    2390             :         case OBJECT_PUBLICATION_NAMESPACE:
    2391             :         case OBJECT_PUBLICATION_REL:
    2392             :         case OBJECT_ROLE:
    2393             :         case OBJECT_RULE:
    2394             :         case OBJECT_STATISTIC_EXT:
    2395             :         case OBJECT_SUBSCRIPTION:
    2396             :         case OBJECT_TABCONSTRAINT:
    2397             :         case OBJECT_TRANSFORM:
    2398             :         case OBJECT_TRIGGER:
    2399             :         case OBJECT_TSCONFIGURATION:
    2400             :         case OBJECT_TSDICTIONARY:
    2401             :         case OBJECT_TSPARSER:
    2402             :         case OBJECT_TSTEMPLATE:
    2403             :         case OBJECT_USER_MAPPING:
    2404             :         case OBJECT_VIEW:
    2405           0 :             elog(ERROR, "unsupported object type: %d", (int) objtype);
    2406             :     }
    2407             : 
    2408           0 :     return "???";             /* keep compiler quiet */
    2409             : }

Generated by: LCOV version 1.16