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

Generated by: LCOV version 1.14