On Sat, Jun 26, 2004 at 07:56:09PM -0400, Tom Lane wrote:
> BTW, it would help to know what parts of the patch you intend to work on
> over the next couple of days. I'm reviewing and editorializing right
> now with intent to commit soon, so it would be good if we can avoid
> tromping on each others' feet.
Oops, I forgot that I had inadvertently left a kludge in
commands/trigger.c. Please use this patch for this file instead.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
Jude: I wish humans laid eggs
Ringlord: Why would you want humans to lay eggs?
Jude: So I can eat them
Index: include/commands/trigger.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/commands/trigger.h,v
retrieving revision 1.45
diff -c -r1.45 trigger.h
*** include/commands/trigger.h 29 Nov 2003 22:40:59 -0000 1.45
--- include/commands/trigger.h 25 Jun 2004 22:59:40 -0000
***************
*** 151,198 ****
ItemPointer tupleid,
HeapTuple newtuple);
-
- /*
- * Deferred trigger stuff
- */
- typedef struct DeferredTriggerStatusData
- {
- Oid dts_tgoid;
- bool dts_tgisdeferred;
- } DeferredTriggerStatusData;
-
- typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
-
- typedef struct DeferredTriggerEventItem
- {
- Oid dti_tgoid;
- int32 dti_state;
- } DeferredTriggerEventItem;
-
- typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
-
- typedef struct DeferredTriggerEventData
- {
- DeferredTriggerEvent dte_next; /* list link */
- int32 dte_event;
- Oid dte_relid;
- ItemPointerData dte_oldctid;
- ItemPointerData dte_newctid;
- int32 dte_n_items;
- /* dte_item is actually a variable-size array, of length dte_n_items */
- DeferredTriggerEventItem dte_item[1];
- } DeferredTriggerEventData;
-
-
extern void DeferredTriggerInit(void);
extern void DeferredTriggerBeginXact(void);
extern void DeferredTriggerEndQuery(void);
extern void DeferredTriggerEndXact(void);
extern void DeferredTriggerAbortXact(void);
!
extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
-
/*
* in utils/adt/ri_triggers.c
*/
--- 151,165 ----
ItemPointer tupleid,
HeapTuple newtuple);
extern void DeferredTriggerInit(void);
extern void DeferredTriggerBeginXact(void);
extern void DeferredTriggerEndQuery(void);
extern void DeferredTriggerEndXact(void);
extern void DeferredTriggerAbortXact(void);
! extern void DeferredTriggerBeginSubXact(void);
! extern void DeferredTriggerEndSubXact(bool isCommit);
extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
/*
* in utils/adt/ri_triggers.c
*/
Index: backend/commands/trigger.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/commands/trigger.c,v
retrieving revision 1.165
diff -c -r1.165 trigger.c
*** backend/commands/trigger.c 26 May 2004 04:41:12 -0000 1.165
--- backend/commands/trigger.c 26 Jun 2004 18:12:01 -0000
***************
*** 50,58 ****
MemoryContext per_tuple_context);
static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
bool row_trigger, HeapTuple oldtup, HeapTuple
newtup);
- static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
- Relation rel, TriggerDesc *trigdesc, FmgrInfo
*finfo,
- MemoryContext per_tuple_context);
/*
--- 50,55 ----
***************
*** 1639,1685 ****
/* ----------
* Deferred trigger stuff
* ----------
*/
! typedef struct DeferredTriggersData
{
! /* Internal data is held in a per-transaction memory context */
! MemoryContext deftrig_cxt;
! /* ALL DEFERRED or ALL IMMEDIATE */
! bool deftrig_all_isset;
! bool deftrig_all_isdeferred;
! /* Per trigger state */
! List *deftrig_trigstates;
! /* List of pending deferred triggers. Previous comment below */
! DeferredTriggerEvent deftrig_events;
! DeferredTriggerEvent deftrig_events_imm;
! DeferredTriggerEvent deftrig_event_tail;
! } DeferredTriggersData;
! /* ----------
! * deftrig_events, deftrig_event_tail:
! * The list of pending deferred trigger events during the current transaction.
*
! * deftrig_events is the head, deftrig_event_tail is the last entry.
! * Because this can grow pretty large, we don't use separate List nodes,
! * but instead thread the list through the dte_next fields of the member
! * nodes. Saves just a few bytes per entry, but that adds up.
! *
! * deftrig_events_imm holds the tail pointer as of the last
! * deferredTriggerInvokeEvents call; we can use this to avoid rescanning
! * entries unnecessarily. It is NULL if deferredTriggerInvokeEvents
! * hasn't run since the last state change.
*
! * XXX Need to be able to shove this data out to a file if it grows too
! * large...
! * ----------
*/
typedef DeferredTriggersData *DeferredTriggers;
static DeferredTriggers deferredTriggers;
/* ----------
* deferredTriggerCheckState()
*
--- 1636,1763 ----
/* ----------
* Deferred trigger stuff
+ *
+ * The DeferredTriggersData struct holds data about pending deferred
+ * trigger events during the current transaction tree, with the
+ * following fields:
+ *
+ * memcxt is a private memory context that holds most data in this
+ * struct, except the events themselves (those will be saved in
+ * CommitContext) and the state which has its own context.
+ *
+ * state keeps track of the deferred state of each trigger
+ * (including the global state)
+ *
+ * events is the head of the list of events.
+ *
+ * tail_thisxact points to the tail of the list, for the current
+ * transaction (whether main transaction or subtransaction). We always
+ * append to the list using this pointer.
+ *
+ * events_imm points to the last element scanned by the last
+ * deferredTriggerInvokeEvents call. We can use this to avoid rescanning
+ * innecessarily; if it's NULL, the scan should start at the head of the
+ * list. It's name comes from the fact that it's set to the last event fired
+ * by the last call to immediate triggers.
+ *
+ * tail_stack and imm_stack are stacks of pointer, which hold the pointers
+ * to the tail and the "immediate" events as the start of a subtransaction.
+ * We use to revert them when aborting the subtransaction.
+ *
+ * numpushed and numalloc keep control of allocation and storage in the
+ * stacks.
+ *
+ * XXX We need to be able to save this data in a file if it grows too
+ * large.
* ----------
*/
! /* Per-item data */
! typedef struct DeferredTriggerEventItem
{
! Oid dti_tgoid;
! TransactionId dti_done_xid;
! int32 dti_state;
! } DeferredTriggerEventItem;
!
! typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
!
! /* Per-event data */
! typedef struct DeferredTriggerEventData
! {
! DeferredTriggerEvent dte_next; /* list link */
! int32 dte_event;
! Oid dte_relid;
! TransactionId dte_done_xid;
! ItemPointerData dte_oldctid;
! ItemPointerData dte_newctid;
! int32 dte_n_items;
! /* dte_item is actually a variable-size array, of length dte_n_items */
! DeferredTriggerEventItem dte_item[1];
! } DeferredTriggerEventData;
!
! /* Per-trigger status data */
! typedef struct DeferredTriggerStatusData
! {
! Oid dts_tgoid;
! bool dts_tgisdeferred;
! } DeferredTriggerStatusData;
! typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
!
!
! /*
! * List of per-trigger status data.
! * We give it its own memory context so it can be freed easily.
*
! * saved is a boolean to flag whether it has been saved by the
! * current subtransaction.
*
! * all_isset and all_isdeferred are used to keep track
! * of SET CONSTRAINTS ALL {DEFERRED, IMMEDIATE}.
! *
! * trigstates is a list of DeferredTriggerStatus structs, which holds
! * the per-trigger state.
*/
+ typedef struct DeferredTriggerStateData
+ {
+ MemoryContext memcxt;
+ bool saved;
+ bool all_isset;
+ bool all_isdeferred;
+ List *trigstates;
+ } DeferredTriggerStateData;
+
+ typedef DeferredTriggerStateData *DeferredTriggerState;
+
+ /* Per-transaction data */
+ typedef struct DeferredTriggersData
+ {
+ MemoryContext memcxt;
+ DeferredTriggerState state;
+ DeferredTriggerEvent events;
+ DeferredTriggerEvent tail_thisxact;
+ DeferredTriggerEvent events_imm;
+ DeferredTriggerEvent *tail_stack;
+ DeferredTriggerEvent *imm_stack;
+ int numpushed;
+ int numalloc;
+ } DeferredTriggersData;
typedef DeferredTriggersData *DeferredTriggers;
static DeferredTriggers deferredTriggers;
+ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
+ Relation rel, TriggerDesc *trigdesc, FmgrInfo
*finfo,
+ MemoryContext per_tuple_context);
+
+ static DeferredTriggerState
+ DeferredTriggerStateCopy(DeferredTriggerState state);
+
+ static DeferredTriggerState
+ DeferredTriggerStateCreate(void);
+
/* ----------
* deferredTriggerCheckState()
*
***************
*** 1704,1710 ****
/*
* Lookup if we know an individual state for this trigger
*/
! foreach(sl, deferredTriggers->deftrig_trigstates)
{
trigstate = (DeferredTriggerStatus) lfirst(sl);
if (trigstate->dts_tgoid == tgoid)
--- 1782,1788 ----
/*
* Lookup if we know an individual state for this trigger
*/
! foreach(sl, deferredTriggers->state->trigstates)
{
trigstate = (DeferredTriggerStatus) lfirst(sl);
if (trigstate->dts_tgoid == tgoid)
***************
*** 1715,1736 ****
* No individual state known - so if the user issued a SET CONSTRAINT
* ALL ..., we return that instead of the triggers default state.
*/
! if (deferredTriggers->deftrig_all_isset)
! return deferredTriggers->deftrig_all_isdeferred;
/*
* No ALL state known either, remember the default state as the
* current and return that.
*/
! oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
trigstate = (DeferredTriggerStatus)
palloc(sizeof(DeferredTriggerStatusData));
trigstate->dts_tgoid = tgoid;
trigstate->dts_tgisdeferred =
((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
! deferredTriggers->deftrig_trigstates =
! lappend(deferredTriggers->deftrig_trigstates, trigstate);
MemoryContextSwitchTo(oldcxt);
--- 1793,1814 ----
* No individual state known - so if the user issued a SET CONSTRAINT
* ALL ..., we return that instead of the triggers default state.
*/
! if (deferredTriggers->state->all_isset)
! return deferredTriggers->state->all_isdeferred;
/*
* No ALL state known either, remember the default state as the
* current and return that.
*/
! oldcxt = MemoryContextSwitchTo(deferredTriggers->memcxt);
trigstate = (DeferredTriggerStatus)
palloc(sizeof(DeferredTriggerStatusData));
trigstate->dts_tgoid = tgoid;
trigstate->dts_tgisdeferred =
((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
! deferredTriggers->state->trigstates =
! lappend(deferredTriggers->state->trigstates, trigstate);
MemoryContextSwitchTo(oldcxt);
***************
*** 1747,1768 ****
static void
deferredTriggerAddEvent(DeferredTriggerEvent event)
{
! /*
! * Since the event list could grow quite long, we keep track of the
! * list tail and append there, rather than just doing a stupid
! * "lappend". This avoids O(N^2) behavior for large numbers of events.
! */
! event->dte_next = NULL;
! if (deferredTriggers->deftrig_event_tail == NULL)
{
/* first list entry */
! deferredTriggers->deftrig_events = event;
! deferredTriggers->deftrig_event_tail = event;
}
else
{
! deferredTriggers->deftrig_event_tail->dte_next = event;
! deferredTriggers->deftrig_event_tail = event;
}
}
--- 1825,1842 ----
static void
deferredTriggerAddEvent(DeferredTriggerEvent event)
{
! Assert(event->dte_next == NULL);
!
! if (deferredTriggers->tail_thisxact == NULL)
{
/* first list entry */
! deferredTriggers->events = event;
! deferredTriggers->tail_thisxact = event;
}
else
{
! deferredTriggers->tail_thisxact->dte_next = event;
! deferredTriggers->tail_thisxact = event;
}
}
***************
*** 1915,1932 ****
/*
* If immediate_only is true, then the only events that could need
! * firing are those since deftrig_events_imm. (But if
! * deftrig_events_imm is NULL, we must scan the entire list.)
*/
! if (immediate_only && deferredTriggers->deftrig_events_imm != NULL)
{
! prev_event = deferredTriggers->deftrig_events_imm;
event = prev_event->dte_next;
}
else
{
prev_event = NULL;
! event = deferredTriggers->deftrig_events;
}
while (event != NULL)
--- 1989,2006 ----
/*
* If immediate_only is true, then the only events that could need
! * firing are those since events_imm. (But if
! * events_imm is NULL, we must scan the entire list.)
*/
! if (immediate_only && deferredTriggers->events_imm != NULL)
{
! prev_event = deferredTriggers->events_imm;
event = prev_event->dte_next;
}
else
{
prev_event = NULL;
! event = deferredTriggers->events;
}
while (event != NULL)
***************
*** 1936,1945 ****
int i;
/*
! * Check if event is already completely done.
*/
! if (!(event->dte_event & (TRIGGER_DEFERRED_DONE |
!
TRIGGER_DEFERRED_CANCELED)))
{
MemoryContextReset(per_tuple_context);
--- 2010,2022 ----
int i;
/*
! * Skip executing cancelled events, and events done by transactions
! * that are not aborted.
*/
! if (!(event->dte_event & TRIGGER_DEFERRED_CANCELED) ||
! (event->dte_event & TRIGGER_DEFERRED_DONE &&
! TransactionIdIsValid(event->dte_done_xid) &&
! !TransactionIdDidAbort(event->dte_done_xid)))
{
MemoryContextReset(per_tuple_context);
***************
*** 1948,1954 ****
*/
for (i = 0; i < event->dte_n_items; i++)
{
! if (event->dte_item[i].dti_state &
TRIGGER_DEFERRED_DONE)
continue;
/*
--- 2025,2033 ----
*/
for (i = 0; i < event->dte_n_items; i++)
{
! if (event->dte_item[i].dti_state &
TRIGGER_DEFERRED_DONE &&
!
TransactionIdIsValid(event->dte_item[i].dti_done_xid) &&
!
!(TransactionIdDidAbort(event->dte_item[i].dti_done_xid)))
continue;
/*
***************
*** 2003,2008 ****
--- 2082,2088 ----
per_tuple_context);
event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
+ event->dte_item[i].dti_done_xid =
GetCurrentTransactionId();
} /* end loop over items
within event */
}
***************
*** 2022,2044 ****
}
else
{
! /* Done */
! if (immediate_only)
{
/* delink it from list and free it */
if (prev_event)
prev_event->dte_next = next_event;
else
! deferredTriggers->deftrig_events = next_event;
pfree(event);
}
else
{
/*
! * We will clean up later, but just for paranoia's
sake,
! * mark the event done.
*/
event->dte_event |= TRIGGER_DEFERRED_DONE;
}
}
--- 2102,2128 ----
}
else
{
! /*
! * We can drop an item if it's done, but only if we're not
! * inside a subtransaction because it could abort later on.
! * We will want to check the item again if it does.
! */
! if (immediate_only && !IsSubTransaction())
{
/* delink it from list and free it */
if (prev_event)
prev_event->dte_next = next_event;
else
! deferredTriggers->events = next_event;
pfree(event);
}
else
{
/*
! * Mark the event done.
*/
event->dte_event |= TRIGGER_DEFERRED_DONE;
+ event->dte_done_xid = GetCurrentTransactionId();
}
}
***************
*** 2046,2055 ****
}
/* Update list tail pointer in case we just deleted tail event */
! deferredTriggers->deftrig_event_tail = prev_event;
/* Set the immediate event pointer for next time */
! deferredTriggers->deftrig_events_imm = prev_event;
/* Release working resources */
if (rel)
--- 2130,2139 ----
}
/* Update list tail pointer in case we just deleted tail event */
! deferredTriggers->tail_thisxact = prev_event;
/* Set the immediate event pointer for next time */
! deferredTriggers->events_imm = prev_event;
/* Release working resources */
if (rel)
***************
*** 2060,2082 ****
MemoryContextDelete(per_tuple_context);
}
-
- /* ----------
- * DeferredTriggerInit()
- *
- * Initialize the deferred trigger mechanism. This is called during
- * backend startup and is guaranteed to be before the first of all
- * transactions.
- * ----------
- */
- void
- DeferredTriggerInit(void)
- {
- /* Nothing to do */
- ;
- }
-
-
/* ----------
* DeferredTriggerBeginXact()
*
--- 2144,2149 ----
***************
*** 2087,2120 ****
void
DeferredTriggerBeginXact(void)
{
! /*
! * This will be changed to a special context when the nested
! * transactions project moves forward.
! */
! MemoryContext cxt = TopTransactionContext;
! deferredTriggers = (DeferredTriggers) MemoryContextAlloc(TopTransactionContext,
!
sizeof(DeferredTriggersData));
/*
* Create the per transaction memory context
*/
! deferredTriggers->deftrig_cxt = AllocSetContextCreate(cxt,
!
"DeferredTriggerXact",
!
ALLOCSET_DEFAULT_MINSIZE,
!
ALLOCSET_DEFAULT_INITSIZE,
!
ALLOCSET_DEFAULT_MAXSIZE);
/*
* If unspecified, constraints default to IMMEDIATE, per SQL
*/
! deferredTriggers->deftrig_all_isdeferred = false;
! deferredTriggers->deftrig_all_isset = false;
! deferredTriggers->deftrig_trigstates = NIL;
! deferredTriggers->deftrig_events = NULL;
! deferredTriggers->deftrig_events_imm = NULL;
! deferredTriggers->deftrig_event_tail = NULL;
}
--- 2154,2185 ----
void
DeferredTriggerBeginXact(void)
{
! MemoryContext old_cxt = MemoryContextSwitchTo(TopTransactionContext);
! deferredTriggers = (DeferredTriggers) palloc(sizeof(DeferredTriggersData));
/*
* Create the per transaction memory context
*/
! deferredTriggers->memcxt = AllocSetContextCreate(TopTransactionContext,
!
"DeferredTriggerXact",
!
ALLOCSET_DEFAULT_MINSIZE,
!
ALLOCSET_DEFAULT_INITSIZE,
!
ALLOCSET_DEFAULT_MAXSIZE);
/*
* If unspecified, constraints default to IMMEDIATE, per SQL
*/
! deferredTriggers->state = DeferredTriggerStateCreate();
! deferredTriggers->events = NULL;
! deferredTriggers->events_imm = NULL;
! deferredTriggers->tail_thisxact = NULL;
! deferredTriggers->tail_stack = NULL;
! deferredTriggers->imm_stack = NULL;
! deferredTriggers->numalloc = 0;
! deferredTriggers->numpushed = 0;
! MemoryContextSwitchTo(old_cxt);
}
***************
*** 2183,2188 ****
--- 2248,2446 ----
deferredTriggers = NULL;
}
+ /*
+ * DeferredTriggerBeginSubXact()
+ *
+ * Start a subtransaction.
+ */
+ void
+ DeferredTriggerBeginSubXact(void)
+ {
+ /*
+ * Ignore call if the transaction is in aborted state.
+ */
+ if (deferredTriggers == NULL)
+ return;
+
+ /*
+ * Reset the saved flag for the new subtransaction.
+ */
+ deferredTriggers->state->saved = false;
+
+ /*
+ * Allocate more space in the stacks if needed.
+ */
+ if (deferredTriggers->numpushed == deferredTriggers->numalloc)
+ {
+ MemoryContext old_cxt;
+
+ old_cxt = MemoryContextSwitchTo(deferredTriggers->memcxt);
+
+ if (deferredTriggers->numalloc == 0)
+ {
+ #define DEFTRIG_INITALLOC 8
+ deferredTriggers->tail_stack =
+ palloc(DEFTRIG_INITALLOC *
sizeof(DeferredTriggerEvent));
+
+ deferredTriggers->imm_stack =
+ palloc(DEFTRIG_INITALLOC *
sizeof(DeferredTriggerEvent));
+
+ deferredTriggers->numalloc = DEFTRIG_INITALLOC;
+ }
+ else
+ {
+ deferredTriggers->numalloc *= 2;
+
+ deferredTriggers->tail_stack =
+ repalloc(deferredTriggers->tail_stack,
+ deferredTriggers->numalloc *
sizeof(DeferredTriggerEvent));
+ deferredTriggers->imm_stack =
+ repalloc(deferredTriggers->imm_stack,
+ deferredTriggers->numalloc *
sizeof(DeferredTriggerEvent));
+ }
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ /*
+ * Push the current list position into the stack and reset the
+ * pointer.
+ */
+ deferredTriggers->tail_stack[deferredTriggers->numpushed] =
+ deferredTriggers->tail_thisxact;
+ deferredTriggers->imm_stack[deferredTriggers->numpushed] =
+ deferredTriggers->events_imm;
+
+ deferredTriggers->numpushed++;
+ }
+
+ /*
+ * DeferredTriggerEndSubXact()
+ *
+ * The current subtransaction is aborting.
+ */
+ void
+ DeferredTriggerEndSubXact(bool isCommit)
+ {
+ /*
+ * Ignore call if the transaction is in aborted state.
+ */
+ if (deferredTriggers == NULL)
+ return;
+
+ /*
+ * Move back the "top of the stack."
+ */
+ deferredTriggers->numpushed --;
+
+ if (!isCommit)
+ {
+ DeferredTriggerState state;
+ /*
+ * Restore the pointers from the stacks.
+ */
+ deferredTriggers->tail_thisxact =
+ deferredTriggers->tail_stack[deferredTriggers->numpushed];
+ deferredTriggers->events_imm =
+ deferredTriggers->imm_stack[deferredTriggers->numpushed];
+
+ /*
+ * Cleanup the head and the tail of the list.
+ */
+ if (deferredTriggers->tail_thisxact == NULL)
+ deferredTriggers->events = NULL;
+ else
+ deferredTriggers->tail_thisxact->dte_next = NULL;
+
+ /*
+ * We don't need to free the items, since the CommitContext will be
+ * reset shortly.
+ */
+
+ /*
+ * Restore the trigger state. If the saved state is NULL, then
+ * this subxact didn't save it, so it doesn't need restoring.
+ */
+ if ((state = TransactionGetDeftrigState()) != NULL)
+ {
+ MemoryContextDelete(deferredTriggers->state->memcxt);
+ deferredTriggers->state = state;
+ }
+ }
+ }
+
+ static DeferredTriggerState
+ DeferredTriggerStateCreate(void)
+ {
+ DeferredTriggerState state;
+ MemoryContext old_cxt;
+ MemoryContext memcxt;
+
+ /*
+ * Create the DeferredTriggerState memory context
+ */
+ memcxt = AllocSetContextCreate(deferredTriggers->memcxt,
+
"DeferredTriggerState",
+
ALLOCSET_SMALL_MINSIZE,
+
ALLOCSET_SMALL_INITSIZE,
+
ALLOCSET_SMALL_MAXSIZE);
+
+ old_cxt = MemoryContextSwitchTo(memcxt);
+
+ /*
+ * We assume that zeroing will initialize correctly the state values.
+ *
+ * NB: the state struct is allocated in the state->memcxt trigger.
+ * Be sure not to lose the pointer prior to deleting it ...
+ */
+ state = (DeferredTriggerState)
+ palloc0(sizeof(DeferredTriggerStateData));
+
+ state->memcxt = memcxt;
+
+ MemoryContextSwitchTo(old_cxt);
+
+ return state;
+ }
+
+ /*
+ * DeferredTriggerStateCopy
+ */
+ static DeferredTriggerState
+ DeferredTriggerStateCopy(DeferredTriggerState origstate)
+ {
+ DeferredTriggerState state;
+ MemoryContext old_cxt;
+ ListCell *cell;
+
+ state = DeferredTriggerStateCreate();
+
+ old_cxt = MemoryContextSwitchTo(state->memcxt);
+
+ state->saved = false;
+ state->all_isset = origstate->all_isset;
+ state->all_isdeferred = origstate->all_isdeferred;
+
+ /*
+ * XXX We assume there are not a lot of items on this list. If there
+ * were, we probably should be using an array of items and not a list
+ * -- this would not only save space on the list itself, but it'd also
+ * make this copying much faster: the whole struct would be a single
+ * memcpy().
+ */
+ foreach(cell, origstate->trigstates)
+ {
+ DeferredTriggerStatus trigstate = (DeferredTriggerStatus)
+ palloc(sizeof(DeferredTriggerStatusData));
+
+ memcpy(trigstate, lfirst(cell), sizeof(DeferredTriggerStatus));
+ state->trigstates = lappend(state->trigstates, trigstate);
+ }
+
+ MemoryContextSwitchTo(old_cxt);
+
+ return state;
+ }
/* ----------
* DeferredTriggerSetState()
***************
*** 2193,2200 ****
void
DeferredTriggerSetState(ConstraintsSetStmt *stmt)
{
- ListCell *l;
-
/*
* Ignore call if we aren't in a transaction.
*/
--- 2451,2456 ----
***************
*** 2202,2207 ****
--- 2458,2473 ----
return;
/*
+ * Save the current state so it can be restored if the subtransaction
+ * aborts.
+ */
+ if (IsSubTransaction() && !deferredTriggers->state->saved)
+ {
+
TransactionSetDeftrigState(DeferredTriggerStateCopy(deferredTriggers->state));
+ deferredTriggers->state->saved = true;
+ }
+
+ /*
* Handle SET CONSTRAINTS ALL ...
*/
if (stmt->constraints == NIL)
***************
*** 2210,2232 ****
* Drop all per-transaction information about individual trigger
* states.
*/
! list_free_deep(deferredTriggers->deftrig_trigstates);
! deferredTriggers->deftrig_trigstates = NIL;
/*
* Set the per-transaction ALL state to known.
*/
! deferredTriggers->deftrig_all_isset = true;
! deferredTriggers->deftrig_all_isdeferred = stmt->deferred;
}
else
{
Relation tgrel;
MemoryContext oldcxt;
! bool found;
! DeferredTriggerStatus state;
! ListCell *ls;
! List *loid = NIL;
/* ----------
* Handle SET CONSTRAINTS constraint-name [, ...]
--- 2476,2496 ----
* Drop all per-transaction information about individual trigger
* states.
*/
! list_free_deep(deferredTriggers->state->trigstates);
! deferredTriggers->state->trigstates = NIL;
/*
* Set the per-transaction ALL state to known.
*/
! deferredTriggers->state->all_isset = true;
! deferredTriggers->state->all_isdeferred = stmt->deferred;
}
else
{
Relation tgrel;
MemoryContext oldcxt;
! ListCell *l;
! List *oidlist = NIL;
/* ----------
* Handle SET CONSTRAINTS constraint-name [, ...]
***************
*** 2241,2246 ****
--- 2505,2511 ----
ScanKeyData skey;
SysScanDesc tgscan;
HeapTuple htup;
+ bool found;
/*
* Check that only named constraints are set explicitly
***************
*** 2285,2291 ****
cname)));
constr_oid = HeapTupleGetOid(htup);
! loid = lappend_oid(loid, constr_oid);
found = true;
}
--- 2550,2556 ----
cname)));
constr_oid = HeapTupleGetOid(htup);
! oidlist = lappend_oid(oidlist, constr_oid);
found = true;
}
***************
*** 2305,2334 ****
* Inside of a transaction block set the trigger states of
* individual triggers on transaction level.
*/
! oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
! foreach(l, loid)
{
! found = false;
! foreach(ls, deferredTriggers->deftrig_trigstates)
{
! state = (DeferredTriggerStatus) lfirst(ls);
! if (state->dts_tgoid == lfirst_oid(l))
{
! state->dts_tgisdeferred = stmt->deferred;
found = true;
break;
}
}
if (!found)
{
! state = (DeferredTriggerStatus)
! palloc(sizeof(DeferredTriggerStatusData));
! state->dts_tgoid = lfirst_oid(l);
! state->dts_tgisdeferred = stmt->deferred;
! deferredTriggers->deftrig_trigstates =
! lappend(deferredTriggers->deftrig_trigstates,
state);
}
}
--- 2570,2604 ----
* Inside of a transaction block set the trigger states of
* individual triggers on transaction level.
*/
! oldcxt = MemoryContextSwitchTo(deferredTriggers->state->memcxt);
! foreach(l, oidlist)
{
! ListCell *ls;
! bool found = false;
!
! foreach(ls, deferredTriggers->state->trigstates)
{
! DeferredTriggerStatus status = (DeferredTriggerStatus)
lfirst(ls);
! if (status->dts_tgoid == lfirst_oid(l))
{
! status->dts_tgisdeferred = stmt->deferred;
found = true;
break;
}
}
if (!found)
{
! DeferredTriggerStatus status;
!
! status = (DeferredTriggerStatus) palloc
! (sizeof(DeferredTriggerStatusData));
!
! status->dts_tgoid = lfirst_oid(l);
! status->dts_tgisdeferred = stmt->deferred;
! deferredTriggers->state->trigstates =
! lappend(deferredTriggers->state->trigstates,
status);
}
}
***************
*** 2347,2360 ****
* entire list, in case some deferred events are now immediately
* invokable.
*/
! deferredTriggers->deftrig_events_imm = NULL;
}
/* ----------
* DeferredTriggerSaveEvent()
*
! * Called by ExecAR...Triggers() to add the event to the queue.
*
* NOTE: should be called only if we've determined that an event must
* be added to the queue.
--- 2617,2630 ----
* entire list, in case some deferred events are now immediately
* invokable.
*/
! deferredTriggers->events_imm = NULL;
}
/* ----------
* DeferredTriggerSaveEvent()
*
! * Called by ExecA[RS]...Triggers() to add the event to the queue.
*
* NOTE: should be called only if we've determined that an event must
* be added to the queue.
***************
*** 2423,2431 ****
return;
/*
! * Create a new event
*/
! oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
n_enabled_triggers * sizeof(DeferredTriggerEventItem);
--- 2693,2702 ----
return;
/*
! * Create a new event. We use the CommitContext so the event
! * will automatically go away if the subtransaction aborts.
*/
! oldcxt = MemoryContextSwitchTo(CommitContext);
new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
n_enabled_triggers * sizeof(DeferredTriggerEventItem);
***************
*** 2433,2438 ****
--- 2704,2710 ----
new_event = (DeferredTriggerEvent) palloc(new_size);
new_event->dte_next = NULL;
new_event->dte_event = event & TRIGGER_EVENT_OPMASK;
+ new_event->dte_done_xid = InvalidTransactionId;
if (row_trigger)
new_event->dte_event |= TRIGGER_EVENT_ROW;
new_event->dte_relid = rel->rd_id;
***************
*** 2449,2454 ****
--- 2721,2727 ----
ev_item = &(new_event->dte_item[i]);
ev_item->dti_tgoid = trigger->tgoid;
+ ev_item->dti_done_xid = InvalidTransactionId;
ev_item->dti_state =
((trigger->tgdeferrable) ?
TRIGGER_DEFERRED_DEFERRABLE : 0) |
***************
*** 2517,2522 ****
--- 2790,2796 ----
* the trigger at all.
*/
new_event->dte_item[i].dti_state |=
TRIGGER_DEFERRED_DONE;
+ new_event->dte_item[i].dti_done_xid =
GetCurrentTransactionId();
}
}
---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster