From 80559fa9e9657ecfdb92a210971b8d0aa6e82e39 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 30 Oct 2004 20:53:06 +0000 Subject: [PATCH] I found a corner case in which it is possible for RI_FKey_check's call of HeapTupleSatisfiesItself() to trigger a hint-bit update on the tuple: if the row was updated or deleted by a subtransaction of my own transaction that was later rolled back. This cannot occur in pre-8.0 of course, so the hint-bit patch applied a couple weeks ago is OK for existing releases. But for 8.0 it seems we had better fix things so that RI_FKey_check can pass the correct buffer number to HeapTupleSatisfiesItself. Accordingly, add fields to the TriggerData struct to carry the buffer ID(s) for the old and new tuple(s). There are other possible solutions but this one seems cleanest; it will allow other AFTER-trigger functions to safely do tqual.c calls if they want to. Put new fields at end of struct so that there is no API breakage. --- doc/src/sgml/trigger.sgml | 25 ++++++++++++++++- src/backend/commands/tablecmds.c | 4 ++- src/backend/commands/trigger.c | 42 +++++++++++++++++++++++------ src/backend/utils/adt/ri_triggers.c | 22 +++++++-------- src/include/commands/trigger.h | 4 ++- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index 56dfec80e6..942eb0c128 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -1,5 +1,5 @@ @@ -256,6 +256,8 @@ typedef struct TriggerData HeapTuple tg_trigtuple; HeapTuple tg_newtuple; Trigger *tg_trigger; + Buffer tg_trigtuplebuf; + Buffer tg_newtuplebuf; } TriggerData; @@ -427,6 +429,27 @@ typedef struct Trigger + + + tg_trigtuplebuf + + + The buffer containing tg_trigtuple, or InvalidBuffer if there + is no such tuple or it is not stored in a disk buffer. + + + + + + tg_newtuplebuf + + + The buffer containing tg_newtuple, or InvalidBuffer if there + is no such tuple or it is not stored in a disk buffer. + + + + diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3616275858..4593327d35 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.137 2004/10/22 17:20:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.138 2004/10/30 20:52:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4256,6 +4256,8 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint, trigdata.tg_trigtuple = tuple; trigdata.tg_newtuple = NULL; trigdata.tg_trigger = &trig; + trigdata.tg_trigtuplebuf = scan->rs_cbuf; + trigdata.tg_newtuplebuf = InvalidBuffer; fcinfo.context = (Node *) &trigdata; diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 7b1bdddf0b..a477fc8469 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.173 2004/10/21 21:33:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.174 2004/10/30 20:52:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1192,8 +1192,10 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo) LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; - LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuple = NULL; + LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; @@ -1246,6 +1248,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; @@ -1253,6 +1256,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, relinfo->ri_TrigFunctions + tgindx[i], @@ -1305,8 +1309,10 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo) LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; - LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuple = NULL; + LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; @@ -1365,6 +1371,7 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; @@ -1372,6 +1379,7 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = trigtuple; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, relinfo->ri_TrigFunctions + tgindx[i], @@ -1434,8 +1442,10 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; - LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuple = NULL; + LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; @@ -1509,6 +1519,8 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, relinfo->ri_TrigFunctions + tgindx[i], @@ -1896,8 +1908,8 @@ AfterTriggerExecute(AfterTriggerEvent event, HeapTupleData oldtuple; HeapTupleData newtuple; HeapTuple rettuple; - Buffer oldbuffer; - Buffer newbuffer; + Buffer oldbuffer = InvalidBuffer; + Buffer newbuffer = InvalidBuffer; int tgindx; /* @@ -1942,16 +1954,22 @@ AfterTriggerExecute(AfterTriggerEvent event, case TRIGGER_EVENT_INSERT: LocTriggerData.tg_trigtuple = &newtuple; LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = newbuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; break; case TRIGGER_EVENT_UPDATE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = &newtuple; + LocTriggerData.tg_trigtuplebuf = oldbuffer; + LocTriggerData.tg_newtuplebuf = newbuffer; break; case TRIGGER_EVENT_DELETE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = oldbuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; break; } @@ -1970,9 +1988,9 @@ AfterTriggerExecute(AfterTriggerEvent event, /* * Release buffers */ - if (ItemPointerIsValid(&(event->ate_oldctid))) + if (oldbuffer != InvalidBuffer) ReleaseBuffer(oldbuffer); - if (ItemPointerIsValid(&(event->ate_newctid))) + if (newbuffer != InvalidBuffer) ReleaseBuffer(newbuffer); } @@ -2916,6 +2934,14 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, LocTriggerData.tg_trigtuple = oldtup; LocTriggerData.tg_newtuple = newtup; LocTriggerData.tg_trigger = trigger; + /* + * We do not currently know which buffers the passed tuples + * are in, but it does not matter because RI_FKey_keyequal_upd + * does not care. We could expand the API of this function + * if it becomes necessary to set these fields accurately. + */ + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; if (RI_FKey_keyequal_upd(&LocTriggerData)) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index b195e24339..a727aee1c0 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -17,7 +17,7 @@ * * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.74 2004/10/15 22:40:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.75 2004/10/30 20:52:59 tgl Exp $ * * ---------- */ @@ -186,6 +186,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) Relation pk_rel; HeapTuple new_row; HeapTuple old_row; + Buffer new_row_buf; RI_QueryKey qkey; void *qplan; int i; @@ -213,35 +214,30 @@ RI_FKey_check(PG_FUNCTION_ARGS) { old_row = trigdata->tg_trigtuple; new_row = trigdata->tg_newtuple; + new_row_buf = trigdata->tg_newtuplebuf; } else { old_row = NULL; new_row = trigdata->tg_trigtuple; + new_row_buf = trigdata->tg_trigtuplebuf; } /* * We should not even consider checking the row if it is no longer * valid since it was either deleted (doesn't matter) or updated (in * which case it'll be checked with its final values). - * - * We do not know what buffer the new_row is in, but it doesn't matter - * since it's not possible for a hint-bit update to occur here (the - * new_row could only contain our own XID, and we haven't yet committed - * or aborted...) */ - if (new_row) + Assert(new_row_buf != InvalidBuffer); + if (!HeapTupleSatisfiesItself(new_row->t_data, new_row_buf)) { - if (!HeapTupleSatisfiesItself(new_row->t_data, InvalidBuffer)) - { - heap_close(pk_rel, RowShareLock); - return PointerGetDatum(NULL); - } + heap_close(pk_rel, RowShareLock); + return PointerGetDatum(NULL); } /* ---------- * SQL3 11.9 - * Gereral rules 2) a): + * General rules 2) a): * If Rf and Rt are empty (no columns to compare given) * constraint is true if 0 < (SELECT COUNT(*) FROM T) * diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 7d5569018a..a271a2a3ea 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.49 2004/09/10 18:40:06 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.50 2004/10/30 20:53:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,8 @@ typedef struct TriggerData HeapTuple tg_trigtuple; HeapTuple tg_newtuple; Trigger *tg_trigger; + Buffer tg_trigtuplebuf; + Buffer tg_newtuplebuf; } TriggerData; /* TriggerEvent bit flags */