LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 83.4 % 962 802
Test Date: 2026-01-26 10:56:24 Functions: 91.3 % 46 42
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 58.5 % 591 346

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

Generated by: LCOV version 2.3.2-1