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

Generated by: LCOV version 1.14