7
7
* Portions Copyright (c) 1994, Regents of the University of California
8
8
*
9
9
* IDENTIFICATION
10
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.209 2006/10/04 00:29:51 momjian Exp $
10
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.210 2006/11/23 01:14:59 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
@@ -1801,8 +1801,8 @@ ltrmark:;
1801
1801
* during the current transaction tree. (BEFORE triggers are fired
1802
1802
* immediately so we don't need any persistent state about them.) The struct
1803
1803
* and most of its subsidiary data are kept in TopTransactionContext; however
1804
- * the individual event records are kept in CurTransactionContext, so that
1805
- * they will easily go away during subtransaction abort.
1804
+ * the individual event records are kept in separate contexts, to make them
1805
+ * easy to delete during subtransaction abort.
1806
1806
*
1807
1807
* Because the list of pending events can grow large, we go to some effort
1808
1808
* to minimize memory consumption. We do not use the generic List mechanism
@@ -1889,7 +1889,10 @@ typedef struct AfterTriggerEventList
1889
1889
* events is the current list of deferred events. This is global across
1890
1890
* all subtransactions of the current transaction. In a subtransaction
1891
1891
* abort, we know that the events added by the subtransaction are at the
1892
- * end of the list, so it is relatively easy to discard them.
1892
+ * end of the list, so it is relatively easy to discard them. The event
1893
+ * structs themselves are stored in event_cxt if generated by the top-level
1894
+ * transaction, else in per-subtransaction contexts identified by the
1895
+ * entries in cxt_stack.
1893
1896
*
1894
1897
* query_depth is the current depth of nested AfterTriggerBeginQuery calls
1895
1898
* (-1 when the stack is empty).
@@ -1931,6 +1934,8 @@ typedef struct AfterTriggersData
1931
1934
int query_depth ; /* current query list index */
1932
1935
AfterTriggerEventList * query_stack ; /* events pending from each query */
1933
1936
int maxquerydepth ; /* allocated len of above array */
1937
+ MemoryContext event_cxt ; /* top transaction's event context, if any */
1938
+ MemoryContext * cxt_stack ; /* per-subtransaction event contexts */
1934
1939
1935
1940
/* these fields are just for resetting at subtrans abort: */
1936
1941
@@ -2464,7 +2469,11 @@ AfterTriggerBeginXact(void)
2464
2469
8 * sizeof (AfterTriggerEventList ));
2465
2470
afterTriggers -> maxquerydepth = 8 ;
2466
2471
2472
+ /* Context for events is created only when needed */
2473
+ afterTriggers -> event_cxt = NULL ;
2474
+
2467
2475
/* Subtransaction stack is empty until/unless needed */
2476
+ afterTriggers -> cxt_stack = NULL ;
2468
2477
afterTriggers -> state_stack = NULL ;
2469
2478
afterTriggers -> events_stack = NULL ;
2470
2479
afterTriggers -> depth_stack = NULL ;
@@ -2626,8 +2635,18 @@ AfterTriggerEndXact(bool isCommit)
2626
2635
* Forget everything we know about AFTER triggers.
2627
2636
*
2628
2637
* Since all the info is in TopTransactionContext or children thereof, we
2629
- * need do nothing special to reclaim memory.
2638
+ * don't really need to do anything to reclaim memory. However, the
2639
+ * pending-events list could be large, and so it's useful to discard
2640
+ * it as soon as possible --- especially if we are aborting because we
2641
+ * ran out of memory for the list!
2642
+ *
2643
+ * (Note: any event_cxts of child subtransactions could also be
2644
+ * deleted here, but we have no convenient way to find them, so we
2645
+ * leave it to TopTransactionContext reset to clean them up.)
2630
2646
*/
2647
+ if (afterTriggers && afterTriggers -> event_cxt )
2648
+ MemoryContextDelete (afterTriggers -> event_cxt );
2649
+
2631
2650
afterTriggers = NULL ;
2632
2651
}
2633
2652
@@ -2649,7 +2668,10 @@ AfterTriggerBeginSubXact(void)
2649
2668
return ;
2650
2669
2651
2670
/*
2652
- * Allocate more space in the stacks if needed.
2671
+ * Allocate more space in the stacks if needed. (Note: because the
2672
+ * minimum nest level of a subtransaction is 2, we waste the first
2673
+ * couple entries of each array; not worth the notational effort to
2674
+ * avoid it.)
2653
2675
*/
2654
2676
while (my_level >= afterTriggers -> maxtransdepth )
2655
2677
{
@@ -2660,6 +2682,8 @@ AfterTriggerBeginSubXact(void)
2660
2682
old_cxt = MemoryContextSwitchTo (TopTransactionContext );
2661
2683
2662
2684
#define DEFTRIG_INITALLOC 8
2685
+ afterTriggers -> cxt_stack = (MemoryContext * )
2686
+ palloc (DEFTRIG_INITALLOC * sizeof (MemoryContext ));
2663
2687
afterTriggers -> state_stack = (SetConstraintState * )
2664
2688
palloc (DEFTRIG_INITALLOC * sizeof (SetConstraintState ));
2665
2689
afterTriggers -> events_stack = (AfterTriggerEventList * )
@@ -2677,6 +2701,9 @@ AfterTriggerBeginSubXact(void)
2677
2701
/* repalloc will keep the stacks in the same context */
2678
2702
int new_alloc = afterTriggers -> maxtransdepth * 2 ;
2679
2703
2704
+ afterTriggers -> cxt_stack = (MemoryContext * )
2705
+ repalloc (afterTriggers -> cxt_stack ,
2706
+ new_alloc * sizeof (MemoryContext ));
2680
2707
afterTriggers -> state_stack = (SetConstraintState * )
2681
2708
repalloc (afterTriggers -> state_stack ,
2682
2709
new_alloc * sizeof (SetConstraintState ));
@@ -2695,8 +2722,10 @@ AfterTriggerBeginSubXact(void)
2695
2722
2696
2723
/*
2697
2724
* Push the current information into the stack. The SET CONSTRAINTS state
2698
- * is not saved until/unless changed.
2725
+ * is not saved until/unless changed. Likewise, we don't make a
2726
+ * per-subtransaction event context until needed.
2699
2727
*/
2728
+ afterTriggers -> cxt_stack [my_level ] = NULL ;
2700
2729
afterTriggers -> state_stack [my_level ] = NULL ;
2701
2730
afterTriggers -> events_stack [my_level ] = afterTriggers -> events ;
2702
2731
afterTriggers -> depth_stack [my_level ] = afterTriggers -> query_depth ;
@@ -2742,7 +2771,23 @@ AfterTriggerEndSubXact(bool isCommit)
2742
2771
else
2743
2772
{
2744
2773
/*
2745
- * Aborting --- restore the pointers from the stacks.
2774
+ * Aborting. We don't really need to release the subxact's event_cxt,
2775
+ * since it will go away anyway when CurTransactionContext gets reset,
2776
+ * but doing so early in subxact abort helps free space we might need.
2777
+ *
2778
+ * (Note: any event_cxts of child subtransactions could also be
2779
+ * deleted here, but we have no convenient way to find them, so we
2780
+ * leave it to CurTransactionContext reset to clean them up.)
2781
+ */
2782
+ if (afterTriggers -> cxt_stack [my_level ])
2783
+ {
2784
+ MemoryContextDelete (afterTriggers -> cxt_stack [my_level ]);
2785
+ /* avoid double delete if repeated aborts */
2786
+ afterTriggers -> cxt_stack [my_level ] = NULL ;
2787
+ }
2788
+
2789
+ /*
2790
+ * Restore the pointers from the stacks.
2746
2791
*/
2747
2792
afterTriggers -> events = afterTriggers -> events_stack [my_level ];
2748
2793
afterTriggers -> query_depth = afterTriggers -> depth_stack [my_level ];
@@ -2753,11 +2798,6 @@ AfterTriggerEndSubXact(bool isCommit)
2753
2798
if (afterTriggers -> events .tail != NULL )
2754
2799
afterTriggers -> events .tail -> ate_next = NULL ;
2755
2800
2756
- /*
2757
- * We don't need to free the subtransaction's items, since the
2758
- * CurTransactionContext will be reset shortly.
2759
- */
2760
-
2761
2801
/*
2762
2802
* Restore the trigger state. If the saved state is NULL, then this
2763
2803
* subxact didn't save it, so it doesn't need restoring.
@@ -3204,6 +3244,8 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
3204
3244
{
3205
3245
Relation rel = relinfo -> ri_RelationDesc ;
3206
3246
TriggerDesc * trigdesc = relinfo -> ri_TrigDesc ;
3247
+ int my_level = GetCurrentTransactionNestLevel ();
3248
+ MemoryContext * cxtptr ;
3207
3249
AfterTriggerEvent new_event ;
3208
3250
int i ;
3209
3251
int ntriggers ;
@@ -3294,12 +3336,29 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
3294
3336
}
3295
3337
3296
3338
/*
3297
- * Create a new event. We use the CurTransactionContext so the event
3298
- * will automatically go away if the subtransaction aborts.
3339
+ * If we don't yet have an event context for the current (sub)xact,
3340
+ * create one. Make it a child of CurTransactionContext to ensure it
3341
+ * will go away if the subtransaction aborts.
3342
+ */
3343
+ if (my_level > 1 ) /* subtransaction? */
3344
+ {
3345
+ Assert (my_level < afterTriggers -> maxtransdepth );
3346
+ cxtptr = & afterTriggers -> cxt_stack [my_level ];
3347
+ }
3348
+ else
3349
+ cxtptr = & afterTriggers -> event_cxt ;
3350
+ if (* cxtptr == NULL )
3351
+ * cxtptr = AllocSetContextCreate (CurTransactionContext ,
3352
+ "AfterTriggerEvents" ,
3353
+ ALLOCSET_DEFAULT_MINSIZE ,
3354
+ ALLOCSET_DEFAULT_INITSIZE ,
3355
+ ALLOCSET_DEFAULT_MAXSIZE );
3356
+
3357
+ /*
3358
+ * Create a new event.
3299
3359
*/
3300
3360
new_event = (AfterTriggerEvent )
3301
- MemoryContextAlloc (CurTransactionContext ,
3302
- sizeof (AfterTriggerEventData ));
3361
+ MemoryContextAlloc (* cxtptr , sizeof (AfterTriggerEventData ));
3303
3362
new_event -> ate_next = NULL ;
3304
3363
new_event -> ate_event =
3305
3364
(event & TRIGGER_EVENT_OPMASK ) |
0 commit comments