diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-03-03 03:30:54 +0000 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-03-03 03:30:54 +0000 |
| commit | 984a6ced3ece409e97b74ce163297b8193eeeb6c (patch) | |
| tree | c6c3ad8b3166d3bc492d7a22b89a1ba13d51d1a1 /src/backend | |
| parent | 2a0ba3f8ddd0d58288fe1d146af4ca57fd82dc45 (diff) | |
| download | postgresql-984a6ced3ece409e97b74ce163297b8193eeeb6c.tar.gz | |
Add CASCADE option to TRUNCATE. Joachim Wieland
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/catalog/heap.c | 62 | ||||
| -rw-r--r-- | src/backend/commands/tablecmds.c | 90 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/nodes/equalfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 5 | ||||
| -rw-r--r-- | src/backend/tcop/utility.c | 8 |
6 files changed, 142 insertions, 29 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index b79b9f0708..ae86c44c38 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.293 2005/11/22 18:17:08 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.294 2006/03/03 03:30:52 tgl Exp $ * * * INTERFACE ROUTINES @@ -2043,7 +2043,7 @@ heap_truncate_check_FKs(List *relations, bool tempTables) if (con->contype != CONSTRAINT_FOREIGN) continue; - /* Not for one of our list of tables */ + /* Not referencing one of our list of tables */ if (!list_member_oid(oids, con->confrelid)) continue; @@ -2066,7 +2066,8 @@ heap_truncate_check_FKs(List *relations, bool tempTables) get_rel_name(con->conrelid), get_rel_name(con->confrelid), NameStr(con->conname)), - errhint("Truncate table \"%s\" at the same time.", + errhint("Truncate table \"%s\" at the same time, " + "or use TRUNCATE ... CASCADE.", get_rel_name(con->conrelid)))); } } @@ -2074,3 +2075,58 @@ heap_truncate_check_FKs(List *relations, bool tempTables) systable_endscan(fkeyScan); heap_close(fkeyRel, AccessShareLock); } + +/* + * heap_truncate_find_FKs + * Find relations having foreign keys referencing any relations that + * are to be truncated + * + * This is almost the same code as heap_truncate_check_FKs, but we don't + * raise an error if we find such relations; instead we return a list of + * their OIDs. Also note that the input is a list of OIDs not a list + * of Relations. The result list does *not* include any rels that are + * already in the input list. + * + * Note: caller should already have exclusive lock on all rels mentioned + * in relationIds. Since adding or dropping an FK requires exclusive lock + * on both rels, this ensures that the answer will be stable. + */ +List * +heap_truncate_find_FKs(List *relationIds) +{ + List *result = NIL; + Relation fkeyRel; + SysScanDesc fkeyScan; + HeapTuple tuple; + + /* + * Must scan pg_constraint. Right now, it is a seqscan because + * there is no available index on confrelid. + */ + fkeyRel = heap_open(ConstraintRelationId, AccessShareLock); + + fkeyScan = systable_beginscan(fkeyRel, InvalidOid, false, + SnapshotNow, 0, NULL); + + while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) + { + Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); + + /* Not a foreign key */ + if (con->contype != CONSTRAINT_FOREIGN) + continue; + + /* Not referencing one of our list of tables */ + if (!list_member_oid(relationIds, con->confrelid)) + continue; + + /* Add referencer unless already in input or result list */ + if (!list_member_oid(relationIds, con->conrelid)) + result = list_append_unique_oid(result, con->conrelid); + } + + systable_endscan(fkeyScan); + heap_close(fkeyRel, AccessShareLock); + + return result; +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 48ebd597c9..4a3c146faa 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.177 2006/01/30 16:18:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.178 2006/03/03 03:30:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -527,26 +527,79 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior) * ExecuteTruncate * Executes a TRUNCATE command. * - * This is a multi-relation truncate. It first opens and grabs exclusive - * locks on all relations involved, checking permissions and otherwise - * verifying that the relation is OK for truncation. When they are all - * open, it checks foreign key references on them, namely that FK references - * are all internal to the group that's being truncated. Finally all - * relations are truncated and reindexed. + * This is a multi-relation truncate. We first open and grab exclusive + * lock on all relations involved, checking permissions and otherwise + * verifying that the relation is OK for truncation. In CASCADE mode, + * relations having FK references to the targeted relations are automatically + * added to the group; in RESTRICT mode, we check that all FK references are + * internal to the group that's being truncated. Finally all the relations + * are truncated and reindexed. */ void -ExecuteTruncate(List *relations) +ExecuteTruncate(TruncateStmt *stmt) { List *rels = NIL; + List *directRelids = NIL; ListCell *cell; + Oid relid; + Relation rel; - foreach(cell, relations) + /* + * Open and exclusive-lock all the explicitly-specified relations + */ + foreach(cell, stmt->relations) { RangeVar *rv = lfirst(cell); - Relation rel; - /* Grab exclusive lock in preparation for truncate */ rel = heap_openrv(rv, AccessExclusiveLock); + rels = lappend(rels, rel); + directRelids = lappend_oid(directRelids, 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. + */ + if (stmt->behavior == DROP_CASCADE) + { + List *relids = list_copy(directRelids); + + for (;;) + { + List *newrelids; + + newrelids = heap_truncate_find_FKs(relids); + if (newrelids == NIL) + break; /* nothing else to add */ + + foreach(cell, newrelids) + { + relid = lfirst_oid(cell); + rel = heap_open(relid, AccessExclusiveLock); + rels = lappend(rels, rel); + relids = lappend_oid(relids, relid); + } + } + } + + /* now check all involved relations */ + foreach(cell, rels) + { + rel = (Relation) lfirst(cell); + relid = RelationGetRelid(rel); + + /* + * 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. + */ + if (stmt->behavior == DROP_CASCADE && + !list_member_oid(directRelids, relid)) + ereport(NOTICE, + (errmsg("truncate cascades to table \"%s\"", + RelationGetRelationName(rel)))); /* Only allow truncate on regular tables */ if (rel->rd_rel->relkind != RELKIND_RELATION) @@ -585,25 +638,30 @@ ExecuteTruncate(List *relations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot truncate temporary tables of other sessions"))); - - /* Save it into the list of rels to truncate */ - rels = lappend(rels, rel); } /* - * Check foreign key references. + * 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) { - Relation rel = lfirst(cell); 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 diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 91f06df039..1e79857d80 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.327 2006/02/19 00:04:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.328 2006/03/03 03:30:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1961,6 +1961,7 @@ _copyTruncateStmt(TruncateStmt *from) TruncateStmt *newnode = makeNode(TruncateStmt); COPY_NODE_FIELD(relations); + COPY_SCALAR_FIELD(behavior); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 9a2a5fd0d5..6c25554804 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.263 2006/02/19 00:04:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.264 2006/03/03 03:30:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -930,6 +930,7 @@ static bool _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) { COMPARE_NODE_FIELD(relations); + COMPARE_SCALAR_FIELD(behavior); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a60d4157b2..92f0ba67d7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.531 2006/02/28 22:37:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.532 2006/03/03 03:30:53 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2938,10 +2938,11 @@ attrs: '.' attr_name *****************************************************************************/ TruncateStmt: - TRUNCATE opt_table qualified_name_list + TRUNCATE opt_table qualified_name_list opt_drop_behavior { TruncateStmt *n = makeNode(TruncateStmt); n->relations = $3; + n->behavior = $4; $$ = (Node *)n; } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index b55b1578b3..9a9d138032 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.252 2006/02/12 19:11:01 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.253 2006/03/03 03:30:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -628,11 +628,7 @@ ProcessUtility(Node *parsetree, break; case T_TruncateStmt: - { - TruncateStmt *stmt = (TruncateStmt *) parsetree; - - ExecuteTruncate(stmt->relations); - } + ExecuteTruncate((TruncateStmt *) parsetree); break; case T_CommentStmt: |
