Coverage Report

Created: 2026-08-14 06:37

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