Dept. of second thoughts: rejigger the TRUNCATE ... CASCADE patch so that
relations are still checked for permissions etc as soon as they are opened. The original form of the patch could hold exclusive lock for a long time on relations that the user doesn't even have permissions to access, let alone truncate.
This commit is contained in:
parent
a6add72ac3
commit
4e086f7cb5
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.178 2006/03/03 03:30:52 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.179 2006/03/03 18:25:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -156,6 +156,7 @@ typedef struct NewColumnValue
|
||||
} NewColumnValue;
|
||||
|
||||
|
||||
static void truncate_check_rel(Relation rel);
|
||||
static List *MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
List **supOids, List **supconstr, int *supOidCount);
|
||||
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
|
||||
@ -539,33 +540,33 @@ void
|
||||
ExecuteTruncate(TruncateStmt *stmt)
|
||||
{
|
||||
List *rels = NIL;
|
||||
List *directRelids = NIL;
|
||||
List *relids = NIL;
|
||||
ListCell *cell;
|
||||
Oid relid;
|
||||
Relation rel;
|
||||
|
||||
/*
|
||||
* Open and exclusive-lock all the explicitly-specified relations
|
||||
* Open, exclusive-lock, and check all the explicitly-specified relations
|
||||
*/
|
||||
foreach(cell, stmt->relations)
|
||||
{
|
||||
RangeVar *rv = lfirst(cell);
|
||||
Relation rel;
|
||||
|
||||
rel = heap_openrv(rv, AccessExclusiveLock);
|
||||
truncate_check_rel(rel);
|
||||
rels = lappend(rels, rel);
|
||||
directRelids = lappend_oid(directRelids, RelationGetRelid(rel));
|
||||
relids = lappend_oid(relids, RelationGetRelid(rel));
|
||||
}
|
||||
|
||||
/*
|
||||
* In CASCADE mode, suck in all referencing relations as well. This
|
||||
* requires multiple iterations to find indirectly-dependent relations.
|
||||
* At each phase, we need to exclusive-lock new rels before looking
|
||||
* for their dependencies, else we might miss something.
|
||||
* for their dependencies, else we might miss something. Also, we
|
||||
* check each rel as soon as we open it, to avoid a faux pas such as
|
||||
* holding lock for a long time on a rel we have no permissions for.
|
||||
*/
|
||||
if (stmt->behavior == DROP_CASCADE)
|
||||
{
|
||||
List *relids = list_copy(directRelids);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
List *newrelids;
|
||||
@ -576,31 +577,76 @@ ExecuteTruncate(TruncateStmt *stmt)
|
||||
|
||||
foreach(cell, newrelids)
|
||||
{
|
||||
relid = lfirst_oid(cell);
|
||||
Oid relid = lfirst_oid(cell);
|
||||
Relation rel;
|
||||
|
||||
rel = heap_open(relid, AccessExclusiveLock);
|
||||
ereport(NOTICE,
|
||||
(errmsg("truncate cascades to table \"%s\"",
|
||||
RelationGetRelationName(rel))));
|
||||
truncate_check_rel(rel);
|
||||
rels = lappend(rels, rel);
|
||||
relids = lappend_oid(relids, relid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now check all involved relations */
|
||||
foreach(cell, rels)
|
||||
{
|
||||
rel = (Relation) lfirst(cell);
|
||||
relid = RelationGetRelid(rel);
|
||||
/*
|
||||
* Check foreign key references. In CASCADE mode, this should be
|
||||
* unnecessary since we just pulled in all the references; but as
|
||||
* a cross-check, do it anyway if in an Assert-enabled build.
|
||||
*/
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
heap_truncate_check_FKs(rels, false);
|
||||
#else
|
||||
if (stmt->behavior == DROP_RESTRICT)
|
||||
heap_truncate_check_FKs(rels, false);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this table was added to the command by CASCADE, report it.
|
||||
* We don't do this earlier because if we error out on one of the
|
||||
* tables, it'd be confusing to list subsequently-added tables.
|
||||
* OK, truncate each table.
|
||||
*/
|
||||
if (stmt->behavior == DROP_CASCADE &&
|
||||
!list_member_oid(directRelids, relid))
|
||||
ereport(NOTICE,
|
||||
(errmsg("truncate cascades to table \"%s\"",
|
||||
RelationGetRelationName(rel))));
|
||||
foreach(cell, rels)
|
||||
{
|
||||
Relation rel = (Relation) lfirst(cell);
|
||||
Oid heap_relid;
|
||||
Oid toast_relid;
|
||||
|
||||
/*
|
||||
* Create a new empty storage file for the relation, and assign it as
|
||||
* the relfilenode value. The old storage file is scheduled for
|
||||
* deletion at commit.
|
||||
*/
|
||||
setNewRelfilenode(rel);
|
||||
|
||||
heap_relid = RelationGetRelid(rel);
|
||||
toast_relid = rel->rd_rel->reltoastrelid;
|
||||
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
/*
|
||||
* The same for the toast table, if any.
|
||||
*/
|
||||
if (OidIsValid(toast_relid))
|
||||
{
|
||||
rel = relation_open(toast_relid, AccessExclusiveLock);
|
||||
setNewRelfilenode(rel);
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconstruct the indexes to match, and we're done.
|
||||
*/
|
||||
reindex_relation(heap_relid, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
|
||||
*/
|
||||
static void
|
||||
truncate_check_rel(Relation rel)
|
||||
{
|
||||
/* Only allow truncate on regular tables */
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
ereport(ERROR,
|
||||
@ -638,57 +684,6 @@ ExecuteTruncate(TruncateStmt *stmt)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot truncate temporary tables of other sessions")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check foreign key references. In CASCADE mode, this should be
|
||||
* unnecessary since we just pulled in all the references; but as
|
||||
* a cross-check, do it anyway if in an Assert-enabled build.
|
||||
*/
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
heap_truncate_check_FKs(rels, false);
|
||||
#else
|
||||
if (stmt->behavior == DROP_RESTRICT)
|
||||
heap_truncate_check_FKs(rels, false);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OK, truncate each table.
|
||||
*/
|
||||
foreach(cell, rels)
|
||||
{
|
||||
Oid heap_relid;
|
||||
Oid toast_relid;
|
||||
|
||||
rel = (Relation) lfirst(cell);
|
||||
|
||||
/*
|
||||
* Create a new empty storage file for the relation, and assign it as
|
||||
* the relfilenode value. The old storage file is scheduled for
|
||||
* deletion at commit.
|
||||
*/
|
||||
setNewRelfilenode(rel);
|
||||
|
||||
heap_relid = RelationGetRelid(rel);
|
||||
toast_relid = rel->rd_rel->reltoastrelid;
|
||||
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
/*
|
||||
* The same for the toast table, if any.
|
||||
*/
|
||||
if (OidIsValid(toast_relid))
|
||||
{
|
||||
rel = relation_open(toast_relid, AccessExclusiveLock);
|
||||
setNewRelfilenode(rel);
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconstruct the indexes to match, and we're done.
|
||||
*/
|
||||
reindex_relation(heap_relid, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------
|
||||
|
Loading…
x
Reference in New Issue
Block a user