LCOV - code coverage report
Current view: top level - src/backend/commands - event_trigger.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 501 711 70.5 %
Date: 2017-09-29 15:12:54 Functions: 41 46 89.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11