LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19beta1 Lines: 84.4 % 790 667
Test Date: 2026-06-12 21:16:47 Functions: 95.7 % 46 44
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1