LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 84.4 % 790 667
Test Date: 2026-07-03 19:57:34 Functions: 95.7 % 46 44
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 67.3 % 471 317

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

Generated by: LCOV version 2.0-1