LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 84.4 % 789 666
Test Date: 2026-03-10 19:16:50 Functions: 95.7 % 46 44
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-2026, 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          100 : CreateEventTrigger(CreateEventTrigStmt *stmt)
     125              : {
     126              :     HeapTuple   tuple;
     127              :     Oid         funcoid;
     128              :     Oid         funcrettype;
     129          100 :     Oid         evtowner = GetUserId();
     130              :     ListCell   *lc;
     131          100 :     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          100 :     if (!superuser())
     139            3 :         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           97 :     if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
     147           53 :         strcmp(stmt->eventname, "ddl_command_end") != 0 &&
     148           33 :         strcmp(stmt->eventname, "sql_drop") != 0 &&
     149           16 :         strcmp(stmt->eventname, "login") != 0 &&
     150           11 :         strcmp(stmt->eventname, "table_rewrite") != 0)
     151            3 :         ereport(ERROR,
     152              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     153              :                  errmsg("unrecognized event name \"%s\"",
     154              :                         stmt->eventname)));
     155              : 
     156              :     /* Validate filter conditions. */
     157          140 :     foreach(lc, stmt->whenclause)
     158              :     {
     159           52 :         DefElem    *def = (DefElem *) lfirst(lc);
     160              : 
     161           52 :         if (strcmp(def->defname, "tag") == 0)
     162              :         {
     163           49 :             if (tags != NULL)
     164            3 :                 error_duplicate_filter_variable(def->defname);
     165           46 :             tags = (List *) def->arg;
     166              :         }
     167              :         else
     168            3 :             ereport(ERROR,
     169              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     170              :                      errmsg("unrecognized filter variable \"%s\"", def->defname)));
     171              :     }
     172              : 
     173              :     /* Validate tag list, if any. */
     174           88 :     if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
     175           50 :          strcmp(stmt->eventname, "ddl_command_end") == 0 ||
     176           30 :          strcmp(stmt->eventname, "sql_drop") == 0)
     177           75 :         && tags != NULL)
     178           43 :         validate_ddl_tags("tag", tags);
     179           45 :     else if (strcmp(stmt->eventname, "table_rewrite") == 0
     180            8 :              && tags != NULL)
     181            0 :         validate_table_rewrite_tags("tag", tags);
     182           45 :     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           70 :     tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
     192           70 :     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           70 :     funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
     200           70 :     funcrettype = get_func_rettype(funcoid);
     201           70 :     if (funcrettype != EVENT_TRIGGEROID)
     202            3 :         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           67 :     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           43 : validate_ddl_tags(const char *filtervar, List *taglist)
     217              : {
     218              :     ListCell   *lc;
     219              : 
     220          101 :     foreach(lc, taglist)
     221              :     {
     222           76 :         const char *tagstr = strVal(lfirst(lc));
     223           76 :         CommandTag  commandTag = GetCommandTagEnum(tagstr);
     224              : 
     225           76 :         if (commandTag == CMDTAG_UNKNOWN)
     226            6 :             ereport(ERROR,
     227              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     228              :                      errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
     229              :                             tagstr, filtervar)));
     230           70 :         if (!command_tag_event_trigger_ok(commandTag))
     231           12 :             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           25 : }
     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            3 : error_duplicate_filter_variable(const char *defname)
     266              : {
     267            3 :     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           67 : 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           67 :     tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
     292              : 
     293              :     /* Build the new pg_trigger tuple. */
     294           67 :     trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
     295              :                                  Anum_pg_event_trigger_oid);
     296           67 :     values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
     297           67 :     memset(nulls, false, sizeof(nulls));
     298           67 :     namestrcpy(&evtnamedata, trigname);
     299           67 :     values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
     300           67 :     namestrcpy(&evteventdata, eventname);
     301           67 :     values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
     302           67 :     values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
     303           67 :     values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
     304           67 :     values[Anum_pg_event_trigger_evtenabled - 1] =
     305           67 :         CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
     306           67 :     if (taglist == NIL)
     307           42 :         nulls[Anum_pg_event_trigger_evttags - 1] = true;
     308              :     else
     309           25 :         values[Anum_pg_event_trigger_evttags - 1] =
     310           25 :             filter_list_to_array(taglist);
     311              : 
     312              :     /* Insert heap tuple. */
     313           67 :     tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
     314           67 :     CatalogTupleInsert(tgrel, tuple);
     315           67 :     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           67 :     if (strcmp(eventname, "login") == 0)
     322            5 :         SetDatabaseHasLoginEventTriggers();
     323              : 
     324              :     /* Depend on owner. */
     325           67 :     recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
     326              : 
     327              :     /* Depend on event trigger function. */
     328           67 :     myself.classId = EventTriggerRelationId;
     329           67 :     myself.objectId = trigoid;
     330           67 :     myself.objectSubId = 0;
     331           67 :     referenced.classId = ProcedureRelationId;
     332           67 :     referenced.objectId = funcoid;
     333           67 :     referenced.objectSubId = 0;
     334           67 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     335              : 
     336              :     /* Depend on extension, if any. */
     337           67 :     recordDependencyOnCurrentExtension(&myself, false);
     338              : 
     339              :     /* Post creation hook for new event trigger */
     340           67 :     InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
     341              : 
     342              :     /* Close pg_event_trigger. */
     343           67 :     table_close(tgrel, RowExclusiveLock);
     344              : 
     345           67 :     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           25 : filter_list_to_array(List *filterlist)
     361              : {
     362              :     ListCell   *lc;
     363              :     Datum      *data;
     364           25 :     int         i = 0,
     365           25 :                 l = list_length(filterlist);
     366              : 
     367           25 :     data = palloc_array(Datum, l);
     368              : 
     369           80 :     foreach(lc, filterlist)
     370              :     {
     371           55 :         const char *value = strVal(lfirst(lc));
     372              :         char       *result,
     373              :                    *p;
     374              : 
     375           55 :         result = pstrdup(value);
     376          663 :         for (p = result; *p; p++)
     377          608 :             *p = pg_ascii_toupper((unsigned char) *p);
     378           55 :         data[i++] = PointerGetDatum(cstring_to_text(result));
     379           55 :         pfree(result);
     380              :     }
     381              : 
     382           25 :     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           10 : SetDatabaseHasLoginEventTriggers(void)
     391              : {
     392              :     /* Set dathasloginevt flag in pg_database */
     393              :     Form_pg_database db;
     394           10 :     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           10 :     LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
     406              : 
     407           10 :     tuple = SearchSysCacheLockedCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
     408           10 :     if (!HeapTupleIsValid(tuple))
     409            0 :         elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
     410           10 :     otid = tuple->t_self;
     411           10 :     db = (Form_pg_database) GETSTRUCT(tuple);
     412           10 :     if (!db->dathasloginevt)
     413              :     {
     414            5 :         db->dathasloginevt = true;
     415            5 :         CatalogTupleUpdate(pg_db, &otid, tuple);
     416            5 :         CommandCounterIncrement();
     417              :     }
     418           10 :     UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
     419           10 :     table_close(pg_db, RowExclusiveLock);
     420           10 :     heap_freetuple(tuple);
     421           10 : }
     422              : 
     423              : /*
     424              :  * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
     425              :  */
     426              : Oid
     427           24 : AlterEventTrigger(AlterEventTrigStmt *stmt)
     428              : {
     429              :     Relation    tgrel;
     430              :     HeapTuple   tup;
     431              :     Oid         trigoid;
     432              :     Form_pg_event_trigger evtForm;
     433           24 :     char        tgenabled = stmt->tgenabled;
     434              : 
     435           24 :     tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
     436              : 
     437           24 :     tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
     438              :                               CStringGetDatum(stmt->trigname));
     439           24 :     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           24 :     evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
     446           24 :     trigoid = evtForm->oid;
     447              : 
     448           24 :     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           24 :     evtForm->evtenabled = tgenabled;
     454              : 
     455           24 :     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           24 :     if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
     462              :         tgenabled != TRIGGER_DISABLED)
     463            5 :         SetDatabaseHasLoginEventTriggers();
     464              : 
     465           24 :     InvokeObjectPostAlterHook(EventTriggerRelationId,
     466              :                               trigoid, 0);
     467              : 
     468              :     /* clean up */
     469           24 :     heap_freetuple(tup);
     470           24 :     table_close(tgrel, RowExclusiveLock);
     471              : 
     472           24 :     return trigoid;
     473              : }
     474              : 
     475              : /*
     476              :  * Change event trigger's owner -- by name
     477              :  */
     478              : ObjectAddress
     479            7 : 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            7 :     rel = table_open(EventTriggerRelationId, RowExclusiveLock);
     488              : 
     489            7 :     tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
     490              : 
     491            7 :     if (!HeapTupleIsValid(tup))
     492            0 :         ereport(ERROR,
     493              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     494              :                  errmsg("event trigger \"%s\" does not exist", name)));
     495              : 
     496            7 :     evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
     497            7 :     evtOid = evtForm->oid;
     498              : 
     499            7 :     AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
     500              : 
     501            4 :     ObjectAddressSet(address, EventTriggerRelationId, evtOid);
     502              : 
     503            4 :     heap_freetuple(tup);
     504              : 
     505            4 :     table_close(rel, RowExclusiveLock);
     506              : 
     507            4 :     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            7 : AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
     540              : {
     541              :     Form_pg_event_trigger form;
     542              : 
     543            7 :     form = (Form_pg_event_trigger) GETSTRUCT(tup);
     544              : 
     545            7 :     if (form->evtowner == newOwnerId)
     546            1 :         return;
     547              : 
     548            6 :     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            6 :     if (!superuser_arg(newOwnerId))
     554            3 :         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            3 :     form->evtowner = newOwnerId;
     561            3 :     CatalogTupleUpdate(rel, &tup->t_self, tup);
     562              : 
     563              :     /* Update owner dependency reference */
     564            3 :     changeDependencyOnOwner(EventTriggerRelationId,
     565              :                             form->oid,
     566              :                             newOwnerId);
     567              : 
     568            3 :     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           86 : get_event_trigger_oid(const char *trigname, bool missing_ok)
     580              : {
     581              :     Oid         oid;
     582              : 
     583           86 :     oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
     584              :                           CStringGetDatum(trigname));
     585           86 :     if (!OidIsValid(oid) && !missing_ok)
     586            6 :         ereport(ERROR,
     587              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     588              :                  errmsg("event trigger \"%s\" does not exist", trigname)));
     589           80 :     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         1259 : 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         1259 :     if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
     605              :     {
     606           27 :         if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
     607           21 :             return false;
     608              :     }
     609              :     else
     610              :     {
     611         1232 :         if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
     612            0 :             return false;
     613              :     }
     614              : 
     615              :     /* Filter by tags, if any were specified. */
     616         1238 :     if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
     617          237 :         return false;
     618              : 
     619              :     /* if we reach that point, we're not filtering out this item */
     620         1001 :     return true;
     621              : }
     622              : 
     623              : static CommandTag
     624         1109 : EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
     625              : {
     626         1109 :     if (event == EVT_Login)
     627          132 :         return CMDTAG_LOGIN;
     628              :     else
     629          977 :         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        83104 : 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        83104 :     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        83104 :     cachelist = EventCacheLookup(event);
     685        83104 :     if (cachelist == NIL)
     686        81995 :         return NIL;
     687              : 
     688              :     /* Get the command tag. */
     689         1109 :     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         2368 :     foreach(lc, cachelist)
     699              :     {
     700         1259 :         EventTriggerCacheItem *item = lfirst(lc);
     701              : 
     702         1259 :         if (unfiltered || filter_event_trigger(tag, item))
     703              :         {
     704              :             /* We must plan to fire this trigger. */
     705         1001 :             runlist = lappend_oid(runlist, item->fnoid);
     706              :         }
     707              :     }
     708              : 
     709              :     /* Don't spend any more time on this if no functions to run */
     710         1109 :     if (runlist == NIL)
     711          210 :         return NIL;
     712              : 
     713          899 :     trigdata->type = T_EventTriggerData;
     714          899 :     trigdata->event = eventstr;
     715          899 :     trigdata->parsetree = parsetree;
     716          899 :     trigdata->tag = tag;
     717              : 
     718          899 :     return runlist;
     719              : }
     720              : 
     721              : /*
     722              :  * Fire ddl_command_start triggers.
     723              :  */
     724              : void
     725       112808 : 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       112808 :     if (!IsUnderPostmaster || !event_triggers)
     750       112600 :         return;
     751              : 
     752        81249 :     runlist = EventTriggerCommonSetup(parsetree,
     753              :                                       EVT_DDLCommandStart,
     754              :                                       "ddl_command_start",
     755              :                                       &trigdata, false);
     756        81249 :     if (runlist == NIL)
     757        81041 :         return;
     758              : 
     759              :     /* Run the triggers. */
     760          208 :     EventTriggerInvoke(runlist, &trigdata);
     761              : 
     762              :     /* Cleanup. */
     763          208 :     list_free(runlist);
     764              : 
     765              :     /*
     766              :      * Make sure anything the event triggers did will be visible to the main
     767              :      * command.
     768              :      */
     769          208 :     CommandCounterIncrement();
     770              : }
     771              : 
     772              : /*
     773              :  * Fire ddl_command_end triggers.
     774              :  */
     775              : void
     776       106323 : 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       106323 :     if (!IsUnderPostmaster || !event_triggers)
     786       105869 :         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        74764 :     if (!currentEventTriggerState)
     799        73393 :         return;
     800              : 
     801         1371 :     runlist = EventTriggerCommonSetup(parsetree,
     802              :                                       EVT_DDLCommandEnd, "ddl_command_end",
     803              :                                       &trigdata, false);
     804         1371 :     if (runlist == NIL)
     805          917 :         return;
     806              : 
     807              :     /*
     808              :      * Make sure anything the main command did will be visible to the event
     809              :      * triggers.
     810              :      */
     811          454 :     CommandCounterIncrement();
     812              : 
     813              :     /* Run the triggers. */
     814          454 :     EventTriggerInvoke(runlist, &trigdata);
     815              : 
     816              :     /* Cleanup. */
     817          454 :     list_free(runlist);
     818              : }
     819              : 
     820              : /*
     821              :  * Fire sql_drop triggers.
     822              :  */
     823              : void
     824       106332 : 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       106332 :     if (!IsUnderPostmaster || !event_triggers)
     834       106269 :         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        74773 :     if (!currentEventTriggerState ||
     844         1380 :         slist_is_empty(&currentEventTriggerState->SQLDropList))
     845        74502 :         return;
     846              : 
     847          271 :     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          271 :     if (runlist == NIL)
     858          208 :         return;
     859              : 
     860              :     /*
     861              :      * Make sure anything the main command did will be visible to the event
     862              :      * triggers.
     863              :      */
     864           63 :     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           63 :     currentEventTriggerState->in_sql_drop = true;
     874              : 
     875              :     /* Run the triggers. */
     876           63 :     PG_TRY();
     877              :     {
     878           63 :         EventTriggerInvoke(runlist, &trigdata);
     879              :     }
     880            9 :     PG_FINALLY();
     881              :     {
     882           63 :         currentEventTriggerState->in_sql_drop = false;
     883              :     }
     884           63 :     PG_END_TRY();
     885              : 
     886              :     /* Cleanup. */
     887           54 :     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        13754 : 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        13754 :     if (!IsUnderPostmaster || !event_triggers ||
     908        13686 :         !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
     909        13619 :         return;
     910              : 
     911          135 :     StartTransactionCommand();
     912          135 :     runlist = EventTriggerCommonSetup(NULL,
     913              :                                       EVT_Login, "login",
     914              :                                       &trigdata, false);
     915              : 
     916          135 :     if (runlist != NIL)
     917              :     {
     918              :         /*
     919              :          * Event trigger execution may require an active snapshot.
     920              :          */
     921          132 :         PushActiveSnapshot(GetTransactionSnapshot());
     922              : 
     923              :         /* Run the triggers. */
     924          132 :         EventTriggerInvoke(runlist, &trigdata);
     925              : 
     926              :         /* Cleanup. */
     927          132 :         list_free(runlist);
     928              : 
     929          132 :         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            3 :     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            3 :         runlist = EventTriggerCommonSetup(NULL,
     949              :                                           EVT_Login, "login",
     950              :                                           &trigdata, true);
     951              : 
     952            3 :         if (runlist == NIL)
     953              :         {
     954            3 :             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            3 :             ScanKeyInit(&key[0],
     962              :                         Anum_pg_database_oid,
     963              :                         BTEqualStrategyNumber, F_OIDEQ,
     964              :                         ObjectIdGetDatum(MyDatabaseId));
     965              : 
     966            3 :             systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
     967              :                                           NULL, 1, key, &tuple, &state);
     968              : 
     969            3 :             if (!HeapTupleIsValid(tuple))
     970            0 :                 elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
     971              : 
     972            3 :             db = (Form_pg_database) GETSTRUCT(tuple);
     973            3 :             if (db->dathasloginevt)
     974              :             {
     975            3 :                 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            3 :                 systable_inplace_update_finish(state, tuple);
     984              :             }
     985              :             else
     986            0 :                 systable_inplace_update_cancel(state);
     987            3 :             table_close(pg_db, RowExclusiveLock);
     988            3 :             heap_freetuple(tuple);
     989              :         }
     990              :         else
     991              :         {
     992            0 :             list_free(runlist);
     993              :         }
     994              :     }
     995          135 :     CommitTransactionCommand();
     996              : }
     997              : 
     998              : 
     999              : /*
    1000              :  * Fire table_rewrite triggers.
    1001              :  */
    1002              : void
    1003          552 : 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          552 :     if (!IsUnderPostmaster || !event_triggers)
    1013          510 :         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          552 :     if (!currentEventTriggerState)
    1023          477 :         return;
    1024              : 
    1025           75 :     runlist = EventTriggerCommonSetup(parsetree,
    1026              :                                       EVT_TableRewrite,
    1027              :                                       "table_rewrite",
    1028              :                                       &trigdata, false);
    1029           75 :     if (runlist == NIL)
    1030           33 :         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           42 :     currentEventTriggerState->table_rewrite_oid = tableOid;
    1040           42 :     currentEventTriggerState->table_rewrite_reason = reason;
    1041              : 
    1042              :     /* Run the triggers. */
    1043           42 :     PG_TRY();
    1044              :     {
    1045           42 :         EventTriggerInvoke(runlist, &trigdata);
    1046              :     }
    1047            3 :     PG_FINALLY();
    1048              :     {
    1049           42 :         currentEventTriggerState->table_rewrite_oid = InvalidOid;
    1050           42 :         currentEventTriggerState->table_rewrite_reason = 0;
    1051              :     }
    1052           42 :     PG_END_TRY();
    1053              : 
    1054              :     /* Cleanup. */
    1055           39 :     list_free(runlist);
    1056              : 
    1057              :     /*
    1058              :      * Make sure anything the event triggers did will be visible to the main
    1059              :      * command.
    1060              :      */
    1061           39 :     CommandCounterIncrement();
    1062              : }
    1063              : 
    1064              : /*
    1065              :  * Invoke each event trigger in a list of event triggers.
    1066              :  */
    1067              : static void
    1068          899 : EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
    1069              : {
    1070              :     MemoryContext context;
    1071              :     MemoryContext oldcontext;
    1072              :     ListCell   *lc;
    1073          899 :     bool        first = true;
    1074              : 
    1075              :     /* Guard against stack overflow due to recursive event trigger */
    1076          899 :     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          899 :     context = AllocSetContextCreate(CurrentMemoryContext,
    1083              :                                     "event trigger context",
    1084              :                                     ALLOCSET_DEFAULT_SIZES);
    1085          899 :     oldcontext = MemoryContextSwitchTo(context);
    1086              : 
    1087              :     /* Call each event trigger. */
    1088         1885 :     foreach(lc, fn_oid_list)
    1089              :     {
    1090          998 :         LOCAL_FCINFO(fcinfo, 0);
    1091          998 :         Oid         fnoid = lfirst_oid(lc);
    1092              :         FmgrInfo    flinfo;
    1093              :         PgStat_FunctionCallUsage fcusage;
    1094              : 
    1095          998 :         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          998 :         if (first)
    1104          899 :             first = false;
    1105              :         else
    1106           99 :             CommandCounterIncrement();
    1107              : 
    1108              :         /* Look up the function */
    1109          998 :         fmgr_info(fnoid, &flinfo);
    1110              : 
    1111              :         /* Call the function, passing no arguments but setting a context. */
    1112          998 :         InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
    1113              :                                  InvalidOid, (Node *) trigdata, NULL);
    1114          998 :         pgstat_init_function_usage(fcinfo, &fcusage);
    1115          998 :         FunctionCallInvoke(fcinfo);
    1116          986 :         pgstat_end_function_usage(&fcusage, true);
    1117              : 
    1118              :         /* Reclaim memory. */
    1119          986 :         MemoryContextReset(context);
    1120              :     }
    1121              : 
    1122              :     /* Restore old memory context and delete the temporary one. */
    1123          887 :     MemoryContextSwitchTo(oldcontext);
    1124          887 :     MemoryContextDelete(context);
    1125          887 : }
    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        41205 : EventTriggerSupportsObjectType(ObjectType obtype)
    1134              : {
    1135        41205 :     switch (obtype)
    1136              :     {
    1137          699 :         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          699 :             return false;
    1143           78 :         case OBJECT_EVENT_TRIGGER:
    1144              :             /* no support for event triggers on event triggers */
    1145           78 :             return false;
    1146        40428 :         default:
    1147        40428 :             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         1954 : EventTriggerSupportsObject(const ObjectAddress *object)
    1158              : {
    1159         1954 :     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           58 :         case EventTriggerRelationId:
    1169              :             /* no support for event triggers on event triggers */
    1170           58 :             return false;
    1171         1896 :         default:
    1172         1896 :             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       112809 : 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       112809 :     if (!trackDroppedObjectsNeeded())
    1194       111347 :         return false;
    1195              : 
    1196         1462 :     cxt = AllocSetContextCreate(TopMemoryContext,
    1197              :                                 "event trigger state",
    1198              :                                 ALLOCSET_DEFAULT_SIZES);
    1199         1462 :     state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
    1200         1462 :     state->cxt = cxt;
    1201         1462 :     slist_init(&(state->SQLDropList));
    1202         1462 :     state->in_sql_drop = false;
    1203         1462 :     state->table_rewrite_oid = InvalidOid;
    1204              : 
    1205         2924 :     state->commandCollectionInhibited = currentEventTriggerState ?
    1206         1462 :         currentEventTriggerState->commandCollectionInhibited : false;
    1207         1462 :     state->currentCommand = NULL;
    1208         1462 :     state->commandList = NIL;
    1209         1462 :     state->previous = currentEventTriggerState;
    1210         1462 :     currentEventTriggerState = state;
    1211              : 
    1212         1462 :     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         1462 : EventTriggerEndCompleteQuery(void)
    1228              : {
    1229              :     EventTriggerQueryState *prevstate;
    1230              : 
    1231         1462 :     prevstate = currentEventTriggerState->previous;
    1232              : 
    1233              :     /* this avoids the need for retail pfree of SQLDropList items: */
    1234         1462 :     MemoryContextDelete(currentEventTriggerState->cxt);
    1235              : 
    1236         1462 :     currentEventTriggerState = prevstate;
    1237         1462 : }
    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       130148 : trackDroppedObjectsNeeded(void)
    1246              : {
    1247              :     /*
    1248              :      * true if any sql_drop, table_rewrite, ddl_command_end event trigger
    1249              :      * exists
    1250              :      */
    1251       258786 :     return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
    1252       258786 :         (EventCacheLookup(EVT_TableRewrite) != NIL) ||
    1253       128550 :         (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         2024 : EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
    1278              : {
    1279              :     SQLDropObject *obj;
    1280              :     MemoryContext oldcxt;
    1281              : 
    1282         2024 :     if (!currentEventTriggerState)
    1283          128 :         return;
    1284              : 
    1285              :     Assert(EventTriggerSupportsObject(object));
    1286              : 
    1287         1896 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1288              : 
    1289         1896 :     obj = palloc0_object(SQLDropObject);
    1290         1896 :     obj->address = *object;
    1291         1896 :     obj->original = original;
    1292         1896 :     obj->normal = normal;
    1293              : 
    1294         1896 :     if (object->classId == NamespaceRelationId)
    1295              :     {
    1296              :         /* Special handling is needed for temp namespaces */
    1297           33 :         if (isTempNamespace(object->objectId))
    1298            0 :             obj->istemp = true;
    1299           33 :         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           33 :         obj->objname = get_namespace_name(object->objectId);
    1307              :     }
    1308         1863 :     else if (object->classId == AttrDefaultRelationId)
    1309              :     {
    1310              :         /* We treat a column default as temp if its table is temp */
    1311              :         ObjectAddress colobject;
    1312              : 
    1313          200 :         colobject = GetAttrDefaultColumnAddress(object->objectId);
    1314          200 :         if (OidIsValid(colobject.objectId))
    1315              :         {
    1316          200 :             if (!obtain_object_name_namespace(&colobject, obj))
    1317              :             {
    1318            0 :                 pfree(obj);
    1319            0 :                 MemoryContextSwitchTo(oldcxt);
    1320            0 :                 return;
    1321              :             }
    1322              :         }
    1323              :     }
    1324         1663 :     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           54 :         pg_trigger_rel = table_open(TriggerRelationId, AccessShareLock);
    1336           54 :         ScanKeyInit(&skey[0],
    1337              :                     Anum_pg_trigger_oid,
    1338              :                     BTEqualStrategyNumber, F_OIDEQ,
    1339           54 :                     ObjectIdGetDatum(object->objectId));
    1340           54 :         sscan = systable_beginscan(pg_trigger_rel, TriggerOidIndexId, true,
    1341              :                                    NULL, 1, skey);
    1342           54 :         tuple = systable_getnext(sscan);
    1343           54 :         if (HeapTupleIsValid(tuple))
    1344           54 :             relid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid;
    1345              :         else
    1346            0 :             relid = InvalidOid; /* shouldn't happen */
    1347           54 :         systable_endscan(sscan);
    1348           54 :         table_close(pg_trigger_rel, AccessShareLock);
    1349              :         /* Do nothing if we didn't find the trigger */
    1350           54 :         if (OidIsValid(relid))
    1351              :         {
    1352              :             ObjectAddress relobject;
    1353              : 
    1354           54 :             relobject.classId = RelationRelationId;
    1355           54 :             relobject.objectId = relid;
    1356              :             /* Arbitrarily set objectSubId nonzero so as not to fill objname */
    1357           54 :             relobject.objectSubId = 1;
    1358           54 :             if (!obtain_object_name_namespace(&relobject, obj))
    1359              :             {
    1360            0 :                 pfree(obj);
    1361            0 :                 MemoryContextSwitchTo(oldcxt);
    1362            0 :                 return;
    1363              :             }
    1364              :         }
    1365              :     }
    1366         1609 :     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           15 :         pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
    1378           15 :         ScanKeyInit(&skey[0],
    1379              :                     Anum_pg_policy_oid,
    1380              :                     BTEqualStrategyNumber, F_OIDEQ,
    1381           15 :                     ObjectIdGetDatum(object->objectId));
    1382           15 :         sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
    1383              :                                    NULL, 1, skey);
    1384           15 :         tuple = systable_getnext(sscan);
    1385           15 :         if (HeapTupleIsValid(tuple))
    1386           15 :             relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
    1387              :         else
    1388            0 :             relid = InvalidOid; /* shouldn't happen */
    1389           15 :         systable_endscan(sscan);
    1390           15 :         table_close(pg_policy_rel, AccessShareLock);
    1391              :         /* Do nothing if we didn't find the policy */
    1392           15 :         if (OidIsValid(relid))
    1393              :         {
    1394              :             ObjectAddress relobject;
    1395              : 
    1396           15 :             relobject.classId = RelationRelationId;
    1397           15 :             relobject.objectId = relid;
    1398              :             /* Arbitrarily set objectSubId nonzero so as not to fill objname */
    1399           15 :             relobject.objectSubId = 1;
    1400           15 :             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         1594 :         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         1896 :     obj->objidentity =
    1422         1896 :         getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
    1423              :                                false);
    1424              : 
    1425              :     /* object type */
    1426         1896 :     obj->objecttype = getObjectTypeDescription(&obj->address, false);
    1427              : 
    1428         1896 :     slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
    1429              : 
    1430         1896 :     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         1863 : 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         1863 :     if (is_objectclass_supported(object->classId))
    1452              :     {
    1453              :         Relation    catalog;
    1454              :         HeapTuple   tuple;
    1455              : 
    1456         1863 :         catalog = table_open(object->classId, AccessShareLock);
    1457         1863 :         tuple = get_catalog_object_by_oid(catalog,
    1458         1863 :                                           get_object_attnum_oid(object->classId),
    1459         1863 :                                           object->objectId);
    1460              : 
    1461         1863 :         if (tuple)
    1462              :         {
    1463              :             AttrNumber  attnum;
    1464              :             Datum       datum;
    1465              :             bool        isnull;
    1466              : 
    1467         1863 :             attnum = get_object_attnum_namespace(object->classId);
    1468         1863 :             if (attnum != InvalidAttrNumber)
    1469              :             {
    1470         1835 :                 datum = heap_getattr(tuple, attnum,
    1471              :                                      RelationGetDescr(catalog), &isnull);
    1472         1835 :                 if (!isnull)
    1473              :                 {
    1474              :                     Oid         namespaceId;
    1475              : 
    1476         1835 :                     namespaceId = DatumGetObjectId(datum);
    1477              :                     /* temp objects are only reported if they are my own */
    1478         1835 :                     if (isTempNamespace(namespaceId))
    1479              :                     {
    1480           36 :                         obj->schemaname = "pg_temp";
    1481           36 :                         obj->istemp = true;
    1482              :                     }
    1483         1799 :                     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         1799 :                         obj->schemaname = get_namespace_name(namespaceId);
    1492         1799 :                         obj->istemp = false;
    1493              :                     }
    1494              :                 }
    1495              :             }
    1496              : 
    1497         1863 :             if (get_object_namensp_unique(object->classId) &&
    1498         1536 :                 object->objectSubId == 0)
    1499              :             {
    1500         1255 :                 attnum = get_object_attnum_name(object->classId);
    1501         1255 :                 if (attnum != InvalidAttrNumber)
    1502              :                 {
    1503         1255 :                     datum = heap_getattr(tuple, attnum,
    1504              :                                          RelationGetDescr(catalog), &isnull);
    1505         1255 :                     if (!isnull)
    1506         1255 :                         obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
    1507              :                 }
    1508              :             }
    1509              :         }
    1510              : 
    1511         1863 :         table_close(catalog, AccessShareLock);
    1512              :     }
    1513              : 
    1514         1863 :     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           72 : pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
    1525              : {
    1526           72 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    1527              :     slist_iter  iter;
    1528              : 
    1529              :     /*
    1530              :      * Protect this function from being called out of context
    1531              :      */
    1532           72 :     if (!currentEventTriggerState ||
    1533           72 :         !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           72 :     InitMaterializedSRF(fcinfo, 0);
    1541              : 
    1542          696 :     slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
    1543              :     {
    1544              :         SQLDropObject *obj;
    1545          624 :         int         i = 0;
    1546          624 :         Datum       values[12] = {0};
    1547          624 :         bool        nulls[12] = {0};
    1548              : 
    1549          624 :         obj = slist_container(SQLDropObject, next, iter.cur);
    1550              : 
    1551              :         /* classid */
    1552          624 :         values[i++] = ObjectIdGetDatum(obj->address.classId);
    1553              : 
    1554              :         /* objid */
    1555          624 :         values[i++] = ObjectIdGetDatum(obj->address.objectId);
    1556              : 
    1557              :         /* objsubid */
    1558          624 :         values[i++] = Int32GetDatum(obj->address.objectSubId);
    1559              : 
    1560              :         /* original */
    1561          624 :         values[i++] = BoolGetDatum(obj->original);
    1562              : 
    1563              :         /* normal */
    1564          624 :         values[i++] = BoolGetDatum(obj->normal);
    1565              : 
    1566              :         /* is_temporary */
    1567          624 :         values[i++] = BoolGetDatum(obj->istemp);
    1568              : 
    1569              :         /* object_type */
    1570          624 :         values[i++] = CStringGetTextDatum(obj->objecttype);
    1571              : 
    1572              :         /* schema_name */
    1573          624 :         if (obj->schemaname)
    1574          585 :             values[i++] = CStringGetTextDatum(obj->schemaname);
    1575              :         else
    1576           39 :             nulls[i++] = true;
    1577              : 
    1578              :         /* object_name */
    1579          624 :         if (obj->objname)
    1580          471 :             values[i++] = CStringGetTextDatum(obj->objname);
    1581              :         else
    1582          153 :             nulls[i++] = true;
    1583              : 
    1584              :         /* object_identity */
    1585          624 :         if (obj->objidentity)
    1586          624 :             values[i++] = CStringGetTextDatum(obj->objidentity);
    1587              :         else
    1588            0 :             nulls[i++] = true;
    1589              : 
    1590              :         /* address_names and address_args */
    1591          624 :         if (obj->addrnames)
    1592              :         {
    1593          624 :             values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
    1594              : 
    1595          624 :             if (obj->addrargs)
    1596           30 :                 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
    1597              :             else
    1598          594 :                 values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
    1599              :         }
    1600              :         else
    1601              :         {
    1602            0 :             nulls[i++] = true;
    1603            0 :             nulls[i++] = true;
    1604              :         }
    1605              : 
    1606          624 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
    1607              :                              values, nulls);
    1608              :     }
    1609              : 
    1610           72 :     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           63 : pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
    1621              : {
    1622              :     /*
    1623              :      * Protect this function from being called out of context
    1624              :      */
    1625           63 :     if (!currentEventTriggerState ||
    1626           60 :         currentEventTriggerState->table_rewrite_oid == InvalidOid)
    1627            3 :         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           60 :     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           39 : pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
    1642              : {
    1643              :     /*
    1644              :      * Protect this function from being called out of context
    1645              :      */
    1646           39 :     if (!currentEventTriggerState ||
    1647           39 :         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           39 :     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          134 : EventTriggerInhibitCommandCollection(void)
    1682              : {
    1683          134 :     if (!currentEventTriggerState)
    1684          130 :         return;
    1685              : 
    1686            4 :     currentEventTriggerState->commandCollectionInhibited = true;
    1687              : }
    1688              : 
    1689              : /*
    1690              :  * Re-establish DDL command collection.
    1691              :  */
    1692              : void
    1693          134 : EventTriggerUndoInhibitCommandCollection(void)
    1694              : {
    1695          134 :     if (!currentEventTriggerState)
    1696          130 :         return;
    1697              : 
    1698            4 :     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        70495 : 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        70495 :     if (!currentEventTriggerState ||
    1724          860 :         currentEventTriggerState->commandCollectionInhibited)
    1725        69635 :         return;
    1726              : 
    1727          860 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1728              : 
    1729          860 :     command = palloc_object(CollectedCommand);
    1730              : 
    1731          860 :     command->type = SCT_Simple;
    1732          860 :     command->in_extension = creating_extension;
    1733              : 
    1734          860 :     command->d.simple.address = address;
    1735          860 :     command->d.simple.secondaryObject = secondaryObject;
    1736          860 :     command->parsetree = copyObject(parsetree);
    1737              : 
    1738          860 :     currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
    1739              :                                                     command);
    1740              : 
    1741          860 :     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        33086 : EventTriggerAlterTableStart(Node *parsetree)
    1754              : {
    1755              :     MemoryContext oldcxt;
    1756              :     CollectedCommand *command;
    1757              : 
    1758              :     /* ignore if event trigger context not set, or collection disabled */
    1759        33086 :     if (!currentEventTriggerState ||
    1760          615 :         currentEventTriggerState->commandCollectionInhibited)
    1761        32471 :         return;
    1762              : 
    1763          615 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1764              : 
    1765          615 :     command = palloc_object(CollectedCommand);
    1766              : 
    1767          615 :     command->type = SCT_AlterTable;
    1768          615 :     command->in_extension = creating_extension;
    1769              : 
    1770          615 :     command->d.alterTable.classId = RelationRelationId;
    1771          615 :     command->d.alterTable.objectId = InvalidOid;
    1772          615 :     command->d.alterTable.subcmds = NIL;
    1773          615 :     command->parsetree = copyObject(parsetree);
    1774              : 
    1775          615 :     command->parent = currentEventTriggerState->currentCommand;
    1776          615 :     currentEventTriggerState->currentCommand = command;
    1777              : 
    1778          615 :     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        17239 : EventTriggerAlterTableRelid(Oid objectId)
    1788              : {
    1789        17239 :     if (!currentEventTriggerState ||
    1790          456 :         currentEventTriggerState->commandCollectionInhibited)
    1791        16783 :         return;
    1792              : 
    1793          456 :     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        20932 : 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        20932 :     if (!currentEventTriggerState ||
    1812          732 :         currentEventTriggerState->commandCollectionInhibited)
    1813        20200 :         return;
    1814              : 
    1815              :     Assert(IsA(subcmd, AlterTableCmd));
    1816              :     Assert(currentEventTriggerState->currentCommand != NULL);
    1817              :     Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
    1818              : 
    1819          732 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1820              : 
    1821          732 :     newsub = palloc_object(CollectedATSubcmd);
    1822          732 :     newsub->address = address;
    1823          732 :     newsub->parsetree = copyObject(subcmd);
    1824              : 
    1825         1464 :     currentEventTriggerState->currentCommand->d.alterTable.subcmds =
    1826          732 :         lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
    1827              : 
    1828          732 :     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        30713 : EventTriggerAlterTableEnd(void)
    1841              : {
    1842              :     CollectedCommand *parent;
    1843              : 
    1844              :     /* ignore if event trigger context not set, or collection disabled */
    1845        30713 :     if (!currentEventTriggerState ||
    1846          600 :         currentEventTriggerState->commandCollectionInhibited)
    1847        30113 :         return;
    1848              : 
    1849          600 :     parent = currentEventTriggerState->currentCommand->parent;
    1850              : 
    1851              :     /* If no subcommands, don't collect */
    1852          600 :     if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
    1853              :     {
    1854              :         MemoryContext oldcxt;
    1855              : 
    1856          432 :         oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1857              : 
    1858          864 :         currentEventTriggerState->commandList =
    1859          432 :             lappend(currentEventTriggerState->commandList,
    1860          432 :                     currentEventTriggerState->currentCommand);
    1861              : 
    1862          432 :         MemoryContextSwitchTo(oldcxt);
    1863              :     }
    1864              :     else
    1865          168 :         pfree(currentEventTriggerState->currentCommand);
    1866              : 
    1867          600 :     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        10633 : 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        10633 :     if (!currentEventTriggerState ||
    1887           25 :         currentEventTriggerState->commandCollectionInhibited)
    1888        10608 :         return;
    1889              : 
    1890           25 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1891              : 
    1892              :     /*
    1893              :      * This is tedious, but necessary.
    1894              :      */
    1895           25 :     icopy = palloc_object(InternalGrant);
    1896           25 :     memcpy(icopy, istmt, sizeof(InternalGrant));
    1897           25 :     icopy->objects = list_copy(istmt->objects);
    1898           25 :     icopy->grantees = list_copy(istmt->grantees);
    1899           25 :     icopy->col_privs = NIL;
    1900           25 :     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           25 :     command = palloc_object(CollectedCommand);
    1905           25 :     command->type = SCT_Grant;
    1906           25 :     command->in_extension = creating_extension;
    1907           25 :     command->d.grant.istmt = icopy;
    1908           25 :     command->parsetree = NULL;
    1909              : 
    1910           50 :     currentEventTriggerState->commandList =
    1911           25 :         lappend(currentEventTriggerState->commandList, command);
    1912              : 
    1913           25 :     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          148 : 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          148 :     if (!currentEventTriggerState ||
    1930            1 :         currentEventTriggerState->commandCollectionInhibited)
    1931          147 :         return;
    1932              : 
    1933            1 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1934              : 
    1935            1 :     command = palloc_object(CollectedCommand);
    1936            1 :     command->type = SCT_AlterOpFamily;
    1937            1 :     command->in_extension = creating_extension;
    1938            1 :     ObjectAddressSet(command->d.opfam.address,
    1939              :                      OperatorFamilyRelationId, opfamoid);
    1940            1 :     command->d.opfam.operators = operators;
    1941            1 :     command->d.opfam.procedures = procedures;
    1942            1 :     command->parsetree = (Node *) copyObject(stmt);
    1943              : 
    1944            2 :     currentEventTriggerState->commandList =
    1945            1 :         lappend(currentEventTriggerState->commandList, command);
    1946              : 
    1947            1 :     MemoryContextSwitchTo(oldcxt);
    1948              : }
    1949              : 
    1950              : /*
    1951              :  * EventTriggerCollectCreateOpClass
    1952              :  *      Save data about a CREATE OPERATOR CLASS command being executed
    1953              :  */
    1954              : void
    1955          279 : 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          279 :     if (!currentEventTriggerState ||
    1963            4 :         currentEventTriggerState->commandCollectionInhibited)
    1964          275 :         return;
    1965              : 
    1966            4 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    1967              : 
    1968            4 :     command = palloc0_object(CollectedCommand);
    1969            4 :     command->type = SCT_CreateOpClass;
    1970            4 :     command->in_extension = creating_extension;
    1971            4 :     ObjectAddressSet(command->d.createopc.address,
    1972              :                      OperatorClassRelationId, opcoid);
    1973            4 :     command->d.createopc.operators = operators;
    1974            4 :     command->d.createopc.procedures = procedures;
    1975            4 :     command->parsetree = (Node *) copyObject(stmt);
    1976              : 
    1977            8 :     currentEventTriggerState->commandList =
    1978            4 :         lappend(currentEventTriggerState->commandList, command);
    1979              : 
    1980            4 :     MemoryContextSwitchTo(oldcxt);
    1981              : }
    1982              : 
    1983              : /*
    1984              :  * EventTriggerCollectAlterTSConfig
    1985              :  *      Save data about an ALTER TEXT SEARCH CONFIGURATION command being
    1986              :  *      executed
    1987              :  */
    1988              : void
    1989         4666 : 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         4666 :     if (!currentEventTriggerState ||
    1997            2 :         currentEventTriggerState->commandCollectionInhibited)
    1998         4664 :         return;
    1999              : 
    2000            2 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    2001              : 
    2002            2 :     command = palloc0_object(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 :     if (ndicts > 0)
    2008              :     {
    2009            1 :         command->d.atscfg.dictIds = palloc_array(Oid, ndicts);
    2010            1 :         memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
    2011              :     }
    2012            2 :     command->d.atscfg.ndicts = ndicts;
    2013            2 :     command->parsetree = (Node *) copyObject(stmt);
    2014              : 
    2015            4 :     currentEventTriggerState->commandList =
    2016            2 :         lappend(currentEventTriggerState->commandList, command);
    2017              : 
    2018            2 :     MemoryContextSwitchTo(oldcxt);
    2019              : }
    2020              : 
    2021              : /*
    2022              :  * EventTriggerCollectAlterDefPrivs
    2023              :  *      Save data about an ALTER DEFAULT PRIVILEGES command being
    2024              :  *      executed
    2025              :  */
    2026              : void
    2027           97 : EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
    2028              : {
    2029              :     MemoryContext oldcxt;
    2030              :     CollectedCommand *command;
    2031              : 
    2032              :     /* ignore if event trigger context not set, or collection disabled */
    2033           97 :     if (!currentEventTriggerState ||
    2034            4 :         currentEventTriggerState->commandCollectionInhibited)
    2035           93 :         return;
    2036              : 
    2037            4 :     oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
    2038              : 
    2039            4 :     command = palloc0_object(CollectedCommand);
    2040            4 :     command->type = SCT_AlterDefaultPrivileges;
    2041            4 :     command->d.defprivs.objtype = stmt->action->objtype;
    2042            4 :     command->in_extension = creating_extension;
    2043            4 :     command->parsetree = (Node *) copyObject(stmt);
    2044              : 
    2045            8 :     currentEventTriggerState->commandList =
    2046            4 :         lappend(currentEventTriggerState->commandList, command);
    2047            4 :     MemoryContextSwitchTo(oldcxt);
    2048              : }
    2049              : 
    2050              : /*
    2051              :  * In a ddl_command_end event trigger, this function reports the DDL commands
    2052              :  * being run.
    2053              :  */
    2054              : Datum
    2055          342 : pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
    2056              : {
    2057          342 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    2058              :     ListCell   *lc;
    2059              : 
    2060              :     /*
    2061              :      * Protect this function from being called out of context
    2062              :      */
    2063          342 :     if (!currentEventTriggerState)
    2064            0 :         ereport(ERROR,
    2065              :                 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
    2066              :                  errmsg("%s can only be called in an event trigger function",
    2067              :                         "pg_event_trigger_ddl_commands()")));
    2068              : 
    2069              :     /* Build tuplestore to hold the result rows */
    2070          342 :     InitMaterializedSRF(fcinfo, 0);
    2071              : 
    2072          704 :     foreach(lc, currentEventTriggerState->commandList)
    2073              :     {
    2074          362 :         CollectedCommand *cmd = lfirst(lc);
    2075              :         Datum       values[9];
    2076          362 :         bool        nulls[9] = {0};
    2077              :         ObjectAddress addr;
    2078          362 :         int         i = 0;
    2079              : 
    2080              :         /*
    2081              :          * For IF NOT EXISTS commands that attempt to create an existing
    2082              :          * object, the returned OID is Invalid.  Don't return anything.
    2083              :          *
    2084              :          * One might think that a viable alternative would be to look up the
    2085              :          * Oid of the existing object and run the deparse with that.  But
    2086              :          * since the parse tree might be different from the one that created
    2087              :          * the object in the first place, we might not end up in a consistent
    2088              :          * state anyway.
    2089              :          */
    2090          362 :         if (cmd->type == SCT_Simple &&
    2091          282 :             !OidIsValid(cmd->d.simple.address.objectId))
    2092            3 :             continue;
    2093              : 
    2094          362 :         switch (cmd->type)
    2095              :         {
    2096          348 :             case SCT_Simple:
    2097              :             case SCT_AlterTable:
    2098              :             case SCT_AlterOpFamily:
    2099              :             case SCT_CreateOpClass:
    2100              :             case SCT_AlterTSConfig:
    2101              :                 {
    2102              :                     char       *identity;
    2103              :                     char       *type;
    2104          348 :                     char       *schema = NULL;
    2105              : 
    2106          348 :                     if (cmd->type == SCT_Simple)
    2107          282 :                         addr = cmd->d.simple.address;
    2108           66 :                     else if (cmd->type == SCT_AlterTable)
    2109           59 :                         ObjectAddressSet(addr,
    2110              :                                          cmd->d.alterTable.classId,
    2111              :                                          cmd->d.alterTable.objectId);
    2112            7 :                     else if (cmd->type == SCT_AlterOpFamily)
    2113            1 :                         addr = cmd->d.opfam.address;
    2114            6 :                     else if (cmd->type == SCT_CreateOpClass)
    2115            4 :                         addr = cmd->d.createopc.address;
    2116            2 :                     else if (cmd->type == SCT_AlterTSConfig)
    2117            2 :                         addr = cmd->d.atscfg.address;
    2118              : 
    2119              :                     /*
    2120              :                      * If an object was dropped in the same command we may end
    2121              :                      * up in a situation where we generated a message but can
    2122              :                      * no longer look for the object information, so skip it
    2123              :                      * rather than failing.  This can happen for example with
    2124              :                      * some subcommand combinations of ALTER TABLE.
    2125              :                      */
    2126          348 :                     identity = getObjectIdentity(&addr, true);
    2127          348 :                     if (identity == NULL)
    2128            3 :                         continue;
    2129              : 
    2130              :                     /* The type can never be NULL. */
    2131          345 :                     type = getObjectTypeDescription(&addr, true);
    2132              : 
    2133              :                     /*
    2134              :                      * Obtain schema name, if any ("pg_temp" if a temp
    2135              :                      * object). If the object class is not in the supported
    2136              :                      * list here, we assume it's a schema-less object type,
    2137              :                      * and thus "schema" remains set to NULL.
    2138              :                      */
    2139          345 :                     if (is_objectclass_supported(addr.classId))
    2140              :                     {
    2141              :                         AttrNumber  nspAttnum;
    2142              : 
    2143          345 :                         nspAttnum = get_object_attnum_namespace(addr.classId);
    2144          345 :                         if (nspAttnum != InvalidAttrNumber)
    2145              :                         {
    2146              :                             Relation    catalog;
    2147              :                             HeapTuple   objtup;
    2148              :                             Oid         schema_oid;
    2149              :                             bool        isnull;
    2150              : 
    2151          301 :                             catalog = table_open(addr.classId, AccessShareLock);
    2152          301 :                             objtup = get_catalog_object_by_oid(catalog,
    2153          301 :                                                                get_object_attnum_oid(addr.classId),
    2154              :                                                                addr.objectId);
    2155          301 :                             if (!HeapTupleIsValid(objtup))
    2156            0 :                                 elog(ERROR, "cache lookup failed for object %u/%u",
    2157              :                                      addr.classId, addr.objectId);
    2158              :                             schema_oid =
    2159          301 :                                 DatumGetObjectId(heap_getattr(objtup, nspAttnum,
    2160              :                                                               RelationGetDescr(catalog), &isnull));
    2161          301 :                             if (isnull)
    2162            0 :                                 elog(ERROR,
    2163              :                                      "invalid null namespace in object %u/%u/%d",
    2164              :                                      addr.classId, addr.objectId, addr.objectSubId);
    2165          301 :                             schema = get_namespace_name_or_temp(schema_oid);
    2166              : 
    2167          301 :                             table_close(catalog, AccessShareLock);
    2168              :                         }
    2169              :                     }
    2170              : 
    2171              :                     /* classid */
    2172          345 :                     values[i++] = ObjectIdGetDatum(addr.classId);
    2173              :                     /* objid */
    2174          345 :                     values[i++] = ObjectIdGetDatum(addr.objectId);
    2175              :                     /* objsubid */
    2176          345 :                     values[i++] = Int32GetDatum(addr.objectSubId);
    2177              :                     /* command tag */
    2178          345 :                     values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
    2179              :                     /* object_type */
    2180          345 :                     values[i++] = CStringGetTextDatum(type);
    2181              :                     /* schema */
    2182          345 :                     if (schema == NULL)
    2183           44 :                         nulls[i++] = true;
    2184              :                     else
    2185          301 :                         values[i++] = CStringGetTextDatum(schema);
    2186              :                     /* identity */
    2187          345 :                     values[i++] = CStringGetTextDatum(identity);
    2188              :                     /* in_extension */
    2189          345 :                     values[i++] = BoolGetDatum(cmd->in_extension);
    2190              :                     /* command */
    2191          345 :                     values[i++] = PointerGetDatum(cmd);
    2192              :                 }
    2193          345 :                 break;
    2194              : 
    2195            1 :             case SCT_AlterDefaultPrivileges:
    2196              :                 /* classid */
    2197            1 :                 nulls[i++] = true;
    2198              :                 /* objid */
    2199            1 :                 nulls[i++] = true;
    2200              :                 /* objsubid */
    2201            1 :                 nulls[i++] = true;
    2202              :                 /* command tag */
    2203            1 :                 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
    2204              :                 /* object_type */
    2205            1 :                 values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
    2206              :                 /* schema */
    2207            1 :                 nulls[i++] = true;
    2208              :                 /* identity */
    2209            1 :                 nulls[i++] = true;
    2210              :                 /* in_extension */
    2211            1 :                 values[i++] = BoolGetDatum(cmd->in_extension);
    2212              :                 /* command */
    2213            1 :                 values[i++] = PointerGetDatum(cmd);
    2214            1 :                 break;
    2215              : 
    2216           13 :             case SCT_Grant:
    2217              :                 /* classid */
    2218           13 :                 nulls[i++] = true;
    2219              :                 /* objid */
    2220           13 :                 nulls[i++] = true;
    2221              :                 /* objsubid */
    2222           13 :                 nulls[i++] = true;
    2223              :                 /* command tag */
    2224           13 :                 values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
    2225              :                                                   "GRANT" : "REVOKE");
    2226              :                 /* object_type */
    2227           13 :                 values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
    2228              :                 /* schema */
    2229           13 :                 nulls[i++] = true;
    2230              :                 /* identity */
    2231           13 :                 nulls[i++] = true;
    2232              :                 /* in_extension */
    2233           13 :                 values[i++] = BoolGetDatum(cmd->in_extension);
    2234              :                 /* command */
    2235           13 :                 values[i++] = PointerGetDatum(cmd);
    2236           13 :                 break;
    2237              :         }
    2238              : 
    2239          359 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
    2240              :                              values, nulls);
    2241              :     }
    2242              : 
    2243          342 :     PG_RETURN_VOID();
    2244              : }
    2245              : 
    2246              : /*
    2247              :  * Return the ObjectType as a string, as it would appear in GRANT and
    2248              :  * REVOKE commands.
    2249              :  */
    2250              : static const char *
    2251           13 : stringify_grant_objtype(ObjectType objtype)
    2252              : {
    2253           13 :     switch (objtype)
    2254              :     {
    2255            0 :         case OBJECT_COLUMN:
    2256            0 :             return "COLUMN";
    2257            8 :         case OBJECT_TABLE:
    2258            8 :             return "TABLE";
    2259            0 :         case OBJECT_SEQUENCE:
    2260            0 :             return "SEQUENCE";
    2261            0 :         case OBJECT_DATABASE:
    2262            0 :             return "DATABASE";
    2263            0 :         case OBJECT_DOMAIN:
    2264            0 :             return "DOMAIN";
    2265            0 :         case OBJECT_FDW:
    2266            0 :             return "FOREIGN DATA WRAPPER";
    2267            0 :         case OBJECT_FOREIGN_SERVER:
    2268            0 :             return "FOREIGN SERVER";
    2269            5 :         case OBJECT_FUNCTION:
    2270            5 :             return "FUNCTION";
    2271            0 :         case OBJECT_LANGUAGE:
    2272            0 :             return "LANGUAGE";
    2273            0 :         case OBJECT_LARGEOBJECT:
    2274            0 :             return "LARGE OBJECT";
    2275            0 :         case OBJECT_SCHEMA:
    2276            0 :             return "SCHEMA";
    2277            0 :         case OBJECT_PARAMETER_ACL:
    2278            0 :             return "PARAMETER";
    2279            0 :         case OBJECT_PROCEDURE:
    2280            0 :             return "PROCEDURE";
    2281            0 :         case OBJECT_ROUTINE:
    2282            0 :             return "ROUTINE";
    2283            0 :         case OBJECT_TABLESPACE:
    2284            0 :             return "TABLESPACE";
    2285            0 :         case OBJECT_TYPE:
    2286            0 :             return "TYPE";
    2287              :             /* these currently aren't used */
    2288            0 :         case OBJECT_ACCESS_METHOD:
    2289              :         case OBJECT_AGGREGATE:
    2290              :         case OBJECT_AMOP:
    2291              :         case OBJECT_AMPROC:
    2292              :         case OBJECT_ATTRIBUTE:
    2293              :         case OBJECT_CAST:
    2294              :         case OBJECT_COLLATION:
    2295              :         case OBJECT_CONVERSION:
    2296              :         case OBJECT_DEFAULT:
    2297              :         case OBJECT_DEFACL:
    2298              :         case OBJECT_DOMCONSTRAINT:
    2299              :         case OBJECT_EVENT_TRIGGER:
    2300              :         case OBJECT_EXTENSION:
    2301              :         case OBJECT_FOREIGN_TABLE:
    2302              :         case OBJECT_INDEX:
    2303              :         case OBJECT_MATVIEW:
    2304              :         case OBJECT_OPCLASS:
    2305              :         case OBJECT_OPERATOR:
    2306              :         case OBJECT_OPFAMILY:
    2307              :         case OBJECT_POLICY:
    2308              :         case OBJECT_PUBLICATION:
    2309              :         case OBJECT_PUBLICATION_NAMESPACE:
    2310              :         case OBJECT_PUBLICATION_REL:
    2311              :         case OBJECT_ROLE:
    2312              :         case OBJECT_RULE:
    2313              :         case OBJECT_STATISTIC_EXT:
    2314              :         case OBJECT_SUBSCRIPTION:
    2315              :         case OBJECT_TABCONSTRAINT:
    2316              :         case OBJECT_TRANSFORM:
    2317              :         case OBJECT_TRIGGER:
    2318              :         case OBJECT_TSCONFIGURATION:
    2319              :         case OBJECT_TSDICTIONARY:
    2320              :         case OBJECT_TSPARSER:
    2321              :         case OBJECT_TSTEMPLATE:
    2322              :         case OBJECT_USER_MAPPING:
    2323              :         case OBJECT_VIEW:
    2324            0 :             elog(ERROR, "unsupported object type: %d", (int) objtype);
    2325              :     }
    2326              : 
    2327            0 :     return "???";             /* keep compiler quiet */
    2328              : }
    2329              : 
    2330              : /*
    2331              :  * Return the ObjectType as a string; as above, but use the spelling
    2332              :  * in ALTER DEFAULT PRIVILEGES commands instead.  Generally this is just
    2333              :  * the plural.
    2334              :  */
    2335              : static const char *
    2336            1 : stringify_adefprivs_objtype(ObjectType objtype)
    2337              : {
    2338            1 :     switch (objtype)
    2339              :     {
    2340            0 :         case OBJECT_COLUMN:
    2341            0 :             return "COLUMNS";
    2342            1 :         case OBJECT_TABLE:
    2343            1 :             return "TABLES";
    2344            0 :         case OBJECT_SEQUENCE:
    2345            0 :             return "SEQUENCES";
    2346            0 :         case OBJECT_DATABASE:
    2347            0 :             return "DATABASES";
    2348            0 :         case OBJECT_DOMAIN:
    2349            0 :             return "DOMAINS";
    2350            0 :         case OBJECT_FDW:
    2351            0 :             return "FOREIGN DATA WRAPPERS";
    2352            0 :         case OBJECT_FOREIGN_SERVER:
    2353            0 :             return "FOREIGN SERVERS";
    2354            0 :         case OBJECT_FUNCTION:
    2355            0 :             return "FUNCTIONS";
    2356            0 :         case OBJECT_LANGUAGE:
    2357            0 :             return "LANGUAGES";
    2358            0 :         case OBJECT_LARGEOBJECT:
    2359            0 :             return "LARGE OBJECTS";
    2360            0 :         case OBJECT_SCHEMA:
    2361            0 :             return "SCHEMAS";
    2362            0 :         case OBJECT_PROCEDURE:
    2363            0 :             return "PROCEDURES";
    2364            0 :         case OBJECT_ROUTINE:
    2365            0 :             return "ROUTINES";
    2366            0 :         case OBJECT_TABLESPACE:
    2367            0 :             return "TABLESPACES";
    2368            0 :         case OBJECT_TYPE:
    2369            0 :             return "TYPES";
    2370              :             /* these currently aren't used */
    2371            0 :         case OBJECT_ACCESS_METHOD:
    2372              :         case OBJECT_AGGREGATE:
    2373              :         case OBJECT_AMOP:
    2374              :         case OBJECT_AMPROC:
    2375              :         case OBJECT_ATTRIBUTE:
    2376              :         case OBJECT_CAST:
    2377              :         case OBJECT_COLLATION:
    2378              :         case OBJECT_CONVERSION:
    2379              :         case OBJECT_DEFAULT:
    2380              :         case OBJECT_DEFACL:
    2381              :         case OBJECT_DOMCONSTRAINT:
    2382              :         case OBJECT_EVENT_TRIGGER:
    2383              :         case OBJECT_EXTENSION:
    2384              :         case OBJECT_FOREIGN_TABLE:
    2385              :         case OBJECT_INDEX:
    2386              :         case OBJECT_MATVIEW:
    2387              :         case OBJECT_OPCLASS:
    2388              :         case OBJECT_OPERATOR:
    2389              :         case OBJECT_OPFAMILY:
    2390              :         case OBJECT_PARAMETER_ACL:
    2391              :         case OBJECT_POLICY:
    2392              :         case OBJECT_PUBLICATION:
    2393              :         case OBJECT_PUBLICATION_NAMESPACE:
    2394              :         case OBJECT_PUBLICATION_REL:
    2395              :         case OBJECT_ROLE:
    2396              :         case OBJECT_RULE:
    2397              :         case OBJECT_STATISTIC_EXT:
    2398              :         case OBJECT_SUBSCRIPTION:
    2399              :         case OBJECT_TABCONSTRAINT:
    2400              :         case OBJECT_TRANSFORM:
    2401              :         case OBJECT_TRIGGER:
    2402              :         case OBJECT_TSCONFIGURATION:
    2403              :         case OBJECT_TSDICTIONARY:
    2404              :         case OBJECT_TSPARSER:
    2405              :         case OBJECT_TSTEMPLATE:
    2406              :         case OBJECT_USER_MAPPING:
    2407              :         case OBJECT_VIEW:
    2408            0 :             elog(ERROR, "unsupported object type: %d", (int) objtype);
    2409              :     }
    2410              : 
    2411            0 :     return "???";             /* keep compiler quiet */
    2412              : }
        

Generated by: LCOV version 2.0-1