]> granicus.if.org Git - postgresql/blob - src/backend/commands/trigger.c
Mega-commit to make heap_open/heap_openr/heap_close take an
[postgresql] / src / backend / commands / trigger.c
1 /*-------------------------------------------------------------------------
2  *
3  * trigger.c
4  *        PostgreSQL TRIGGERs support code.
5  *
6  *-------------------------------------------------------------------------
7  */
8 #include "postgres.h"
9
10
11 #include "access/genam.h"
12 #include "access/heapam.h"
13 #include "catalog/catalog.h"
14 #include "catalog/catname.h"
15 #include "catalog/indexing.h"
16 #include "catalog/pg_language.h"
17 #include "catalog/pg_proc.h"
18 #include "catalog/pg_trigger.h"
19 #include "commands/trigger.h"
20 #include "executor/executor.h"
21 #include "miscadmin.h"
22 #include "utils/acl.h"
23 #include "utils/builtins.h"
24 #include "utils/inval.h"
25 #include "utils/syscache.h"
26
27 DLLIMPORT TriggerData *CurrentTriggerData = NULL;
28
29 void            RelationBuildTriggers(Relation relation);
30 void            FreeTriggerDesc(Relation relation);
31
32 static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger);
33 static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid,
34                                    TupleTableSlot **newSlot);
35
36 extern GlobalMemory CacheCxt;
37
38 void
39 CreateTrigger(CreateTrigStmt *stmt)
40 {
41         int16           tgtype;
42         int16           tgattr[8] = {0};
43         Datum           values[Natts_pg_trigger];
44         char            nulls[Natts_pg_trigger];
45         Relation        rel;
46         Relation        tgrel;
47         HeapScanDesc tgscan;
48         ScanKeyData key;
49         Relation        pgrel;
50         HeapTuple       tuple;
51         Relation        idescs[Num_pg_trigger_indices];
52         Relation        ridescs[Num_pg_class_indices];
53         MemoryContext oldcxt;
54         Oid                     fargtypes[8];
55         int                     found = 0;
56         int                     i;
57
58         if (!allowSystemTableMods && IsSystemRelationName(stmt->relname))
59                 elog(ERROR, "CreateTrigger: can't create trigger for system relation %s", stmt->relname);
60
61 #ifndef NO_SECURITY
62         if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME))
63                 elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
64 #endif
65
66         rel = heap_openr(stmt->relname, AccessExclusiveLock);
67
68         TRIGGER_CLEAR_TYPE(tgtype);
69         if (stmt->before)
70                 TRIGGER_SETT_BEFORE(tgtype);
71         if (stmt->row)
72                 TRIGGER_SETT_ROW(tgtype);
73         else
74                 elog(ERROR, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
75
76         for (i = 0; i < 3 && stmt->actions[i]; i++)
77         {
78                 switch (stmt->actions[i])
79                 {
80                         case 'i':
81                                 if (TRIGGER_FOR_INSERT(tgtype))
82                                         elog(ERROR, "CreateTrigger: double INSERT event specified");
83                                 TRIGGER_SETT_INSERT(tgtype);
84                                 break;
85                         case 'd':
86                                 if (TRIGGER_FOR_DELETE(tgtype))
87                                         elog(ERROR, "CreateTrigger: double DELETE event specified");
88                                 TRIGGER_SETT_DELETE(tgtype);
89                                 break;
90                         case 'u':
91                                 if (TRIGGER_FOR_UPDATE(tgtype))
92                                         elog(ERROR, "CreateTrigger: double UPDATE event specified");
93                                 TRIGGER_SETT_UPDATE(tgtype);
94                                 break;
95                         default:
96                                 elog(ERROR, "CreateTrigger: unknown event specified");
97                                 break;
98                 }
99         }
100
101         /* Scan pg_trigger */
102         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
103         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
104                                                    F_OIDEQ, RelationGetRelid(rel));
105         tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
106         while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
107         {
108                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
109
110                 if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
111                         elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
112                                  stmt->trigname, stmt->relname);
113                 else
114                         found++;
115         }
116         heap_endscan(tgscan);
117
118         MemSet(fargtypes, 0, 8 * sizeof(Oid));
119         tuple = SearchSysCacheTuple(PRONAME,
120                                                                 PointerGetDatum(stmt->funcname),
121                                                                 Int32GetDatum(0),
122                                                                 PointerGetDatum(fargtypes),
123                                                                 0);
124         if (!HeapTupleIsValid(tuple) ||
125                 ((Form_pg_proc) GETSTRUCT(tuple))->pronargs != 0)
126                 elog(ERROR, "CreateTrigger: function %s() does not exist",
127                          stmt->funcname);
128         if (((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0)
129                 elog(ERROR, "CreateTrigger: function %s() must return OPAQUE",
130                          stmt->funcname);
131         if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId)
132         {
133                 HeapTuple       langTup;
134
135                 langTup = SearchSysCacheTuple(LANOID,
136                         ObjectIdGetDatum(((Form_pg_proc) GETSTRUCT(tuple))->prolang),
137                                                                           0, 0, 0);
138                 if (!HeapTupleIsValid(langTup))
139                         elog(ERROR, "CreateTrigger: cache lookup for PL failed");
140
141                 if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false)
142                         elog(ERROR, "CreateTrigger: only C and PL functions are supported");
143         }
144
145         MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
146
147         values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
148         values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname));
149         values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_data->t_oid);
150         values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
151         if (stmt->args)
152         {
153                 List       *le;
154                 char       *args;
155                 int16           nargs = length(stmt->args);
156                 int                     len = 0;
157
158                 foreach(le, stmt->args)
159                 {
160                         char       *ar = (char *) lfirst(le);
161
162                         len += strlen(ar) + VARHDRSZ;
163                         for (; *ar; ar++)
164                         {
165                                 if (*ar == '\\')
166                                         len++;
167                         }
168                 }
169                 args = (char *) palloc(len + 1);
170                 args[0] = 0;
171                 foreach(le, stmt->args)
172                 {
173                         char       *s = (char *) lfirst(le);
174                         char       *d = args + strlen(args);
175
176                         while (*s)
177                         {
178                                 if (*s == '\\')
179                                         *d++ = '\\';
180                                 *d++ = *s++;
181                         }
182                         *d = 0;
183                         strcat(args, "\\000");
184                 }
185                 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
186                 values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(args));
187         }
188         else
189         {
190                 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
191                 values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(""));
192         }
193         values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
194
195         tuple = heap_formtuple(tgrel->rd_att, values, nulls);
196         heap_insert(tgrel, tuple);
197         CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
198         CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
199         CatalogCloseIndices(Num_pg_trigger_indices, idescs);
200         pfree(tuple);
201         heap_close(tgrel, RowExclusiveLock);
202
203         pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
204         pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
205
206         /* update pg_class */
207         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
208         tuple = SearchSysCacheTupleCopy(RELNAME,
209                                                                         PointerGetDatum(stmt->relname),
210                                                                         0, 0, 0);
211         if (!HeapTupleIsValid(tuple))
212                 elog(ERROR, "CreateTrigger: relation %s not found in pg_class", stmt->relname);
213
214         ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
215         RelationInvalidateHeapTuple(pgrel, tuple);
216         heap_replace(pgrel, &tuple->t_self, tuple, NULL);
217         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
218         CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
219         CatalogCloseIndices(Num_pg_class_indices, ridescs);
220         pfree(tuple);
221         heap_close(pgrel, RowExclusiveLock);
222
223         CommandCounterIncrement();
224         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
225         FreeTriggerDesc(rel);
226         rel->rd_rel->reltriggers = found + 1;
227         RelationBuildTriggers(rel);
228         MemoryContextSwitchTo(oldcxt);
229         /* Keep lock on target rel until end of xact */
230         heap_close(rel, NoLock);
231 }
232
233 void
234 DropTrigger(DropTrigStmt *stmt)
235 {
236         Relation        rel;
237         Relation        tgrel;
238         HeapScanDesc tgscan;
239         ScanKeyData key;
240         Relation        pgrel;
241         HeapTuple       tuple;
242         Relation        ridescs[Num_pg_class_indices];
243         MemoryContext oldcxt;
244         int                     found = 0;
245         int                     tgfound = 0;
246
247 #ifndef NO_SECURITY
248         if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME))
249                 elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
250 #endif
251
252         rel = heap_openr(stmt->relname, AccessExclusiveLock);
253
254         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
255         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
256                                                    F_OIDEQ, RelationGetRelid(rel));
257         tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
258         while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
259         {
260                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
261
262                 if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
263                 {
264                         heap_delete(tgrel, &tuple->t_self, NULL);
265                         tgfound++;
266                 }
267                 else
268                         found++;
269         }
270         if (tgfound == 0)
271                 elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
272                          stmt->trigname, stmt->relname);
273         if (tgfound > 1)
274                 elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
275                          tgfound, stmt->trigname, stmt->relname);
276         heap_endscan(tgscan);
277         heap_close(tgrel, RowExclusiveLock);
278
279         /* update pg_class */
280         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
281         tuple = SearchSysCacheTupleCopy(RELNAME,
282                                                                         PointerGetDatum(stmt->relname),
283                                                                         0, 0, 0);
284         if (!HeapTupleIsValid(tuple))
285                 elog(ERROR, "DropTrigger: relation %s not found in pg_class", stmt->relname);
286
287         ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found;
288         RelationInvalidateHeapTuple(pgrel, tuple);
289         heap_replace(pgrel, &tuple->t_self, tuple, NULL);
290         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
291         CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
292         CatalogCloseIndices(Num_pg_class_indices, ridescs);
293         pfree(tuple);
294         heap_close(pgrel, RowExclusiveLock);
295
296         CommandCounterIncrement();
297         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
298         FreeTriggerDesc(rel);
299         rel->rd_rel->reltriggers = found;
300         if (found > 0)
301                 RelationBuildTriggers(rel);
302         MemoryContextSwitchTo(oldcxt);
303         /* Keep lock on target rel until end of xact */
304         heap_close(rel, NoLock);
305 }
306
307 void
308 RelationRemoveTriggers(Relation rel)
309 {
310         Relation        tgrel;
311         HeapScanDesc tgscan;
312         ScanKeyData key;
313         HeapTuple       tup;
314
315         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
316         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
317                                                    F_OIDEQ, RelationGetRelid(rel));
318
319         tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
320
321         while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
322                 heap_delete(tgrel, &tup->t_self, NULL);
323
324         heap_endscan(tgscan);
325         heap_close(tgrel, RowExclusiveLock);
326 }
327
328 void
329 RelationBuildTriggers(Relation relation)
330 {
331         TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
332         int                     ntrigs = relation->rd_rel->reltriggers;
333         Trigger    *triggers = NULL;
334         Trigger    *build;
335         Relation        tgrel;
336         Form_pg_trigger pg_trigger;
337         Relation        irel;
338         ScanKeyData skey;
339         HeapTupleData tuple;
340         IndexScanDesc sd;
341         RetrieveIndexResult indexRes;
342         Buffer          buffer;
343         struct varlena *val;
344         bool            isnull;
345         int                     found;
346
347         MemSet(trigdesc, 0, sizeof(TriggerDesc));
348
349         ScanKeyEntryInitialize(&skey,
350                                                    (bits16) 0x0,
351                                                    (AttrNumber) 1,
352                                                    (RegProcedure) F_OIDEQ,
353                                                    ObjectIdGetDatum(RelationGetRelid(relation)));
354
355         tgrel = heap_openr(TriggerRelationName, AccessShareLock);
356         irel = index_openr(TriggerRelidIndex);
357         sd = index_beginscan(irel, false, 1, &skey);
358
359         for (found = 0;;)
360         {
361                 indexRes = index_getnext(sd, ForwardScanDirection);
362                 if (!indexRes)
363                         break;
364
365                 tuple.t_self = indexRes->heap_iptr;
366                 heap_fetch(tgrel, SnapshotNow, &tuple, &buffer);
367                 pfree(indexRes);
368                 if (!tuple.t_data)
369                         continue;
370                 if (found == ntrigs)
371                         elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %.*s",
372                                  NAMEDATALEN, relation->rd_rel->relname.data);
373
374                 pg_trigger = (Form_pg_trigger) GETSTRUCT(&tuple);
375
376                 if (triggers == NULL)
377                         triggers = (Trigger *) palloc(sizeof(Trigger));
378                 else
379                         triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger));
380                 build = &(triggers[found]);
381
382                 build->tgname = nameout(&(pg_trigger->tgname));
383                 build->tgfoid = pg_trigger->tgfoid;
384                 build->tgfunc.fn_addr = NULL;
385                 build->tgtype = pg_trigger->tgtype;
386                 build->tgnargs = pg_trigger->tgnargs;
387                 memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16));
388                 val = (struct varlena *) fastgetattr(&tuple,
389                                                                                          Anum_pg_trigger_tgargs,
390                                                                                          tgrel->rd_att, &isnull);
391                 if (isnull)
392                         elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
393                                  NAMEDATALEN, relation->rd_rel->relname.data);
394                 if (build->tgnargs > 0)
395                 {
396                         char       *p;
397                         int                     i;
398
399                         val = (struct varlena *) fastgetattr(&tuple,
400                                                                                                  Anum_pg_trigger_tgargs,
401                                                                                                  tgrel->rd_att, &isnull);
402                         if (isnull)
403                                 elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
404                                          NAMEDATALEN, relation->rd_rel->relname.data);
405                         p = (char *) VARDATA(val);
406                         build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
407                         for (i = 0; i < build->tgnargs; i++)
408                         {
409                                 build->tgargs[i] = (char *) palloc(strlen(p) + 1);
410                                 strcpy(build->tgargs[i], p);
411                                 p += strlen(p) + 1;
412                         }
413                 }
414                 else
415                         build->tgargs = NULL;
416
417                 found++;
418                 ReleaseBuffer(buffer);
419         }
420
421         if (found < ntrigs)
422                 elog(ERROR, "RelationBuildTriggers: %d record not found for rel %.*s",
423                          ntrigs - found,
424                          NAMEDATALEN, relation->rd_rel->relname.data);
425
426         index_endscan(sd);
427         pfree(sd);
428         index_close(irel);
429         heap_close(tgrel, AccessShareLock);
430
431         /* Build trigdesc */
432         trigdesc->triggers = triggers;
433         for (found = 0; found < ntrigs; found++)
434         {
435                 build = &(triggers[found]);
436                 DescribeTrigger(trigdesc, build);
437         }
438
439         relation->trigdesc = trigdesc;
440
441 }
442
443 void
444 FreeTriggerDesc(Relation relation)
445 {
446         TriggerDesc *trigdesc = relation->trigdesc;
447         Trigger  ***t;
448         Trigger    *trigger;
449         int                     i;
450
451         if (trigdesc == NULL)
452                 return;
453
454         t = trigdesc->tg_before_statement;
455         for (i = 0; i < 3; i++)
456                 if (t[i] != NULL)
457                         pfree(t[i]);
458         t = trigdesc->tg_before_row;
459         for (i = 0; i < 3; i++)
460                 if (t[i] != NULL)
461                         pfree(t[i]);
462         t = trigdesc->tg_after_row;
463         for (i = 0; i < 3; i++)
464                 if (t[i] != NULL)
465                         pfree(t[i]);
466         t = trigdesc->tg_after_statement;
467         for (i = 0; i < 3; i++)
468                 if (t[i] != NULL)
469                         pfree(t[i]);
470
471         trigger = trigdesc->triggers;
472         for (i = 0; i < relation->rd_rel->reltriggers; i++)
473         {
474                 pfree(trigger->tgname);
475                 if (trigger->tgnargs > 0)
476                 {
477                         while (--(trigger->tgnargs) >= 0)
478                                 pfree(trigger->tgargs[trigger->tgnargs]);
479                         pfree(trigger->tgargs);
480                 }
481                 trigger++;
482         }
483         pfree(trigdesc->triggers);
484         pfree(trigdesc);
485         relation->trigdesc = NULL;
486         return;
487 }
488
489 static void
490 DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
491 {
492         uint16     *n;
493         Trigger  ***t,
494                          ***tp;
495
496         if (TRIGGER_FOR_ROW(trigger->tgtype))           /* Is ROW/STATEMENT
497                                                                                                  * trigger */
498         {
499                 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
500                 {
501                         n = trigdesc->n_before_row;
502                         t = trigdesc->tg_before_row;
503                 }
504                 else
505                 {
506                         n = trigdesc->n_after_row;
507                         t = trigdesc->tg_after_row;
508                 }
509         }
510         else
511 /* STATEMENT (NI) */
512         {
513                 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
514                 {
515                         n = trigdesc->n_before_statement;
516                         t = trigdesc->tg_before_statement;
517                 }
518                 else
519                 {
520                         n = trigdesc->n_after_statement;
521                         t = trigdesc->tg_after_statement;
522                 }
523         }
524
525         if (TRIGGER_FOR_INSERT(trigger->tgtype))
526         {
527                 tp = &(t[TRIGGER_EVENT_INSERT]);
528                 if (*tp == NULL)
529                         *tp = (Trigger **) palloc(sizeof(Trigger *));
530                 else
531                         *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
532                                                                                 sizeof(Trigger *));
533                 (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger;
534                 (n[TRIGGER_EVENT_INSERT])++;
535         }
536
537         if (TRIGGER_FOR_DELETE(trigger->tgtype))
538         {
539                 tp = &(t[TRIGGER_EVENT_DELETE]);
540                 if (*tp == NULL)
541                         *tp = (Trigger **) palloc(sizeof(Trigger *));
542                 else
543                         *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
544                                                                                 sizeof(Trigger *));
545                 (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger;
546                 (n[TRIGGER_EVENT_DELETE])++;
547         }
548
549         if (TRIGGER_FOR_UPDATE(trigger->tgtype))
550         {
551                 tp = &(t[TRIGGER_EVENT_UPDATE]);
552                 if (*tp == NULL)
553                         *tp = (Trigger **) palloc(sizeof(Trigger *));
554                 else
555                         *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
556                                                                                 sizeof(Trigger *));
557                 (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger;
558                 (n[TRIGGER_EVENT_UPDATE])++;
559         }
560
561 }
562
563 static HeapTuple
564 ExecCallTriggerFunc(Trigger *trigger)
565 {
566
567         if (trigger->tgfunc.fn_addr == NULL)
568                 fmgr_info(trigger->tgfoid, &trigger->tgfunc);
569
570         if (trigger->tgfunc.fn_plhandler != NULL)
571         {
572                 return (HeapTuple) (*(trigger->tgfunc.fn_plhandler))
573                         (&trigger->tgfunc);
574         }
575
576         return (HeapTuple) ((*fmgr_faddr(&trigger->tgfunc)) ());
577 }
578
579 HeapTuple
580 ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
581 {
582         TriggerData *SaveTriggerData;
583         int                     ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
584         Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
585         HeapTuple       newtuple = trigtuple;
586         HeapTuple       oldtuple;
587         int                     i;
588
589         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
590         SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
591         SaveTriggerData->tg_relation = rel;
592         SaveTriggerData->tg_newtuple = NULL;
593         for (i = 0; i < ntrigs; i++)
594         {
595                 CurrentTriggerData = SaveTriggerData;
596                 CurrentTriggerData->tg_trigtuple = oldtuple = newtuple;
597                 CurrentTriggerData->tg_trigger = trigger[i];
598                 newtuple = ExecCallTriggerFunc(trigger[i]);
599                 if (newtuple == NULL)
600                         break;
601                 else if (oldtuple != newtuple && oldtuple != trigtuple)
602                         pfree(oldtuple);
603         }
604         CurrentTriggerData = NULL;
605         pfree(SaveTriggerData);
606         return newtuple;
607 }
608
609 void
610 ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
611 {
612         TriggerData *SaveTriggerData;
613         int                     ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT];
614         Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT];
615         int                     i;
616
617         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
618         SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
619         SaveTriggerData->tg_relation = rel;
620         SaveTriggerData->tg_newtuple = NULL;
621         for (i = 0; i < ntrigs; i++)
622         {
623                 CurrentTriggerData = SaveTriggerData;
624                 CurrentTriggerData->tg_trigtuple = trigtuple;
625                 CurrentTriggerData->tg_trigger = trigger[i];
626                 ExecCallTriggerFunc(trigger[i]);
627         }
628         CurrentTriggerData = NULL;
629         pfree(SaveTriggerData);
630         return;
631 }
632
633 bool
634 ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid)
635 {
636         Relation        rel = estate->es_result_relation_info->ri_RelationDesc;
637         TriggerData *SaveTriggerData;
638         int                     ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
639         Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
640         HeapTuple       trigtuple;
641         HeapTuple       newtuple = NULL;
642         TupleTableSlot *newSlot;
643         int                     i;
644
645         trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
646         if (trigtuple == NULL)
647                 return false;
648
649         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
650         SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
651         SaveTriggerData->tg_relation = rel;
652         SaveTriggerData->tg_newtuple = NULL;
653         for (i = 0; i < ntrigs; i++)
654         {
655                 CurrentTriggerData = SaveTriggerData;
656                 CurrentTriggerData->tg_trigtuple = trigtuple;
657                 CurrentTriggerData->tg_trigger = trigger[i];
658                 newtuple = ExecCallTriggerFunc(trigger[i]);
659                 if (newtuple == NULL)
660                         break;
661                 if (newtuple != trigtuple)
662                         pfree(newtuple);
663         }
664         CurrentTriggerData = NULL;
665         pfree(SaveTriggerData);
666         pfree(trigtuple);
667
668         return (newtuple == NULL) ? false : true;
669 }
670
671 void
672 ExecARDeleteTriggers(EState *estate, ItemPointer tupleid)
673 {
674         Relation        rel = estate->es_result_relation_info->ri_RelationDesc;
675         TriggerData *SaveTriggerData;
676         int                     ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE];
677         Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE];
678         HeapTuple       trigtuple;
679         int                     i;
680
681         trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
682         Assert(trigtuple != NULL);
683
684         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
685         SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW;
686         SaveTriggerData->tg_relation = rel;
687         SaveTriggerData->tg_newtuple = NULL;
688         for (i = 0; i < ntrigs; i++)
689         {
690                 CurrentTriggerData = SaveTriggerData;
691                 CurrentTriggerData->tg_trigtuple = trigtuple;
692                 CurrentTriggerData->tg_trigger = trigger[i];
693                 ExecCallTriggerFunc(trigger[i]);
694         }
695         CurrentTriggerData = NULL;
696         pfree(SaveTriggerData);
697         pfree(trigtuple);
698         return;
699 }
700
701 HeapTuple
702 ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
703 {
704         Relation        rel = estate->es_result_relation_info->ri_RelationDesc;
705         TriggerData *SaveTriggerData;
706         int                     ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
707         Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE];
708         HeapTuple       trigtuple;
709         HeapTuple       oldtuple;
710         HeapTuple       intuple = newtuple;
711         TupleTableSlot *newSlot;
712         int                     i;
713
714         trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
715         if (trigtuple == NULL)
716                 return NULL;
717
718         /*
719          * In READ COMMITTED isolevel it's possible that newtuple was changed
720          * due to concurrent update.
721          */
722         if (newSlot != NULL)
723                 intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
724
725         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
726         SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
727         SaveTriggerData->tg_relation = rel;
728         for (i = 0; i < ntrigs; i++)
729         {
730                 CurrentTriggerData = SaveTriggerData;
731                 CurrentTriggerData->tg_trigtuple = trigtuple;
732                 CurrentTriggerData->tg_newtuple = oldtuple = newtuple;
733                 CurrentTriggerData->tg_trigger = trigger[i];
734                 newtuple = ExecCallTriggerFunc(trigger[i]);
735                 if (newtuple == NULL)
736                         break;
737                 else if (oldtuple != newtuple && oldtuple != intuple)
738                         pfree(oldtuple);
739         }
740         CurrentTriggerData = NULL;
741         pfree(SaveTriggerData);
742         pfree(trigtuple);
743         return newtuple;
744 }
745
746 void
747 ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
748 {
749         Relation        rel = estate->es_result_relation_info->ri_RelationDesc;
750         TriggerData *SaveTriggerData;
751         int                     ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE];
752         Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE];
753         HeapTuple       trigtuple;
754         int                     i;
755
756         trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
757         Assert(trigtuple != NULL);
758
759         SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
760         SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW;
761         SaveTriggerData->tg_relation = rel;
762         for (i = 0; i < ntrigs; i++)
763         {
764                 CurrentTriggerData = SaveTriggerData;
765                 CurrentTriggerData->tg_trigtuple = trigtuple;
766                 CurrentTriggerData->tg_newtuple = newtuple;
767                 CurrentTriggerData->tg_trigger = trigger[i];
768                 ExecCallTriggerFunc(trigger[i]);
769         }
770         CurrentTriggerData = NULL;
771         pfree(SaveTriggerData);
772         pfree(trigtuple);
773         return;
774 }
775
776 extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
777
778 static HeapTuple
779 GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot)
780 {
781         Relation        relation = estate->es_result_relation_info->ri_RelationDesc;
782         HeapTupleData tuple;
783         HeapTuple       result;
784         Buffer          buffer;
785
786         if (newSlot != NULL)
787         {
788                 int                     test;
789
790                 /*
791                  * mark tuple for update
792                  */
793                 *newSlot = NULL;
794                 tuple.t_self = *tid;
795 ltrmark:;
796                 test = heap_mark4update(relation, &tuple, &buffer);
797                 switch (test)
798                 {
799                         case HeapTupleSelfUpdated:
800                                 ReleaseBuffer(buffer);
801                                 return (NULL);
802
803                         case HeapTupleMayBeUpdated:
804                                 break;
805
806                         case HeapTupleUpdated:
807                                 ReleaseBuffer(buffer);
808                                 if (XactIsoLevel == XACT_SERIALIZABLE)
809                                         elog(ERROR, "Can't serialize access due to concurrent update");
810                                 else if (!(ItemPointerEquals(&(tuple.t_self), tid)))
811                                 {
812                                         TupleTableSlot *epqslot = EvalPlanQual(estate,
813                                          estate->es_result_relation_info->ri_RangeTableIndex,
814                                                                                                                 &(tuple.t_self));
815
816                                         if (!(TupIsNull(epqslot)))
817                                         {
818                                                 *tid = tuple.t_self;
819                                                 *newSlot = epqslot;
820                                                 goto ltrmark;
821                                         }
822                                 }
823
824                                 /*
825                                  * if tuple was deleted or PlanQual failed for updated
826                                  * tuple - we have not process this tuple!
827                                  */
828                                 return (NULL);
829
830                         default:
831                                 ReleaseBuffer(buffer);
832                                 elog(ERROR, "Unknown status %u from heap_mark4update", test);
833                                 return (NULL);
834                 }
835         }
836         else
837         {
838                 PageHeader      dp;
839                 ItemId          lp;
840
841                 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
842
843                 if (!BufferIsValid(buffer))
844                         elog(ERROR, "GetTupleForTrigger: failed ReadBuffer");
845
846                 dp = (PageHeader) BufferGetPage(buffer);
847                 lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
848
849                 Assert(ItemIdIsUsed(lp));
850
851                 tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
852                 tuple.t_len = ItemIdGetLength(lp);
853                 tuple.t_self = *tid;
854         }
855
856         result = heap_copytuple(&tuple);
857         ReleaseBuffer(buffer);
858
859         return result;
860 }