Disallow TRUNCATE when there are any pending after-trigger events for
the target relation(s). There might be some cases where we could discard the pending event instead, but for the moment a conservative approach seems sufficient. Per report from Markus Schiltknecht and subsequent discussion.
This commit is contained in:
parent
395c8166aa
commit
f8bbfad075
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.201 2006/08/25 04:06:48 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.202 2006/09/04 21:15:55 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -611,6 +611,13 @@ ExecuteTruncate(TruncateStmt *stmt)
|
|||||||
heap_truncate_check_FKs(rels, false);
|
heap_truncate_check_FKs(rels, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also check for pending AFTER trigger events on the target relations.
|
||||||
|
* We can't just leave those be, since they will try to fetch tuples
|
||||||
|
* that the TRUNCATE removes.
|
||||||
|
*/
|
||||||
|
AfterTriggerCheckTruncate(relids);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OK, truncate each table.
|
* OK, truncate each table.
|
||||||
*/
|
*/
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.206 2006/08/03 16:04:41 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.207 2006/09/04 21:15:56 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -3117,6 +3117,74 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------
|
||||||
|
* AfterTriggerCheckTruncate()
|
||||||
|
* Test deferred-trigger status to see if a TRUNCATE is OK.
|
||||||
|
*
|
||||||
|
* The argument is a list of OIDs of relations due to be truncated.
|
||||||
|
* We raise error if there are any pending after-trigger events for them.
|
||||||
|
*
|
||||||
|
* In some scenarios it'd be reasonable to remove pending events (more
|
||||||
|
* specifically, mark them DONE by the current subxact) but without a lot
|
||||||
|
* of knowledge of the trigger semantics we can't do this in general.
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AfterTriggerCheckTruncate(List *relids)
|
||||||
|
{
|
||||||
|
AfterTriggerEvent event;
|
||||||
|
int depth;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore call if we aren't in a transaction. (Shouldn't happen?)
|
||||||
|
*/
|
||||||
|
if (afterTriggers == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Scan queued events */
|
||||||
|
for (event = afterTriggers->events.head;
|
||||||
|
event != NULL;
|
||||||
|
event = event->ate_next)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We can ignore completed events. (Even if a DONE flag is rolled
|
||||||
|
* back by subxact abort, it's OK because the effects of the
|
||||||
|
* TRUNCATE must get rolled back too.)
|
||||||
|
*/
|
||||||
|
if (event->ate_event & AFTER_TRIGGER_DONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (list_member_oid(relids, event->ate_relid))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot truncate table \"%s\" because it has pending trigger events",
|
||||||
|
get_rel_name(event->ate_relid))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also scan events queued by incomplete queries. This could only
|
||||||
|
* matter if a TRUNCATE is executed by a function or trigger within
|
||||||
|
* an updating query on the same relation, which is pretty perverse,
|
||||||
|
* but let's check.
|
||||||
|
*/
|
||||||
|
for (depth = 0; depth <= afterTriggers->query_depth; depth++)
|
||||||
|
{
|
||||||
|
for (event = afterTriggers->query_stack[depth].head;
|
||||||
|
event != NULL;
|
||||||
|
event = event->ate_next)
|
||||||
|
{
|
||||||
|
if (event->ate_event & AFTER_TRIGGER_DONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (list_member_oid(relids, event->ate_relid))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot truncate table \"%s\" because it has pending trigger events",
|
||||||
|
get_rel_name(event->ate_relid))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* AfterTriggerSaveEvent()
|
* AfterTriggerSaveEvent()
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.58 2006/06/16 20:23:45 adunstan Exp $
|
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.59 2006/09/04 21:15:56 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -164,8 +164,8 @@ extern void AfterTriggerFireDeferred(void);
|
|||||||
extern void AfterTriggerEndXact(bool isCommit);
|
extern void AfterTriggerEndXact(bool isCommit);
|
||||||
extern void AfterTriggerBeginSubXact(void);
|
extern void AfterTriggerBeginSubXact(void);
|
||||||
extern void AfterTriggerEndSubXact(bool isCommit);
|
extern void AfterTriggerEndSubXact(bool isCommit);
|
||||||
|
|
||||||
extern void AfterTriggerSetState(ConstraintsSetStmt *stmt);
|
extern void AfterTriggerSetState(ConstraintsSetStmt *stmt);
|
||||||
|
extern void AfterTriggerCheckTruncate(List *relids);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user