diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml index 8bd220f77a7fb7df20b2aa8cd6807849efce6a5c..03ee825ad52409c0a54d24a0c4dbdef701b052c2 100644 --- a/doc/src/sgml/ref/truncate.sgml +++ b/doc/src/sgml/ref/truncate.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.17 2004/03/23 13:21:41 neilc Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.18 2005/01/27 03:17:08 tgl Exp $ PostgreSQL documentation --> @@ -11,7 +11,7 @@ PostgreSQL documentation <refnamediv> <refname>TRUNCATE</refname> - <refpurpose>empty a table</refpurpose> + <refpurpose>empty a table or set of tables</refpurpose> </refnamediv> <indexterm zone="sql-truncate"> @@ -20,7 +20,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> -TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> +TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] </synopsis> </refsynopsisdiv> @@ -28,10 +28,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> <title>Description</title> <para> - <command>TRUNCATE</command> quickly removes all rows from a - table. It has the same effect as an unqualified - <command>DELETE</command> but since it does not actually scan the - table it is faster. This is most useful on large tables. + <command>TRUNCATE</command> quickly removes all rows from a set of + tables. It has the same effect as an unqualified + <command>DELETE</command> on each table, but since it does not actually + scan the tables it is faster. This is most useful on large tables. </para> </refsect1> @@ -43,7 +43,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> <term><replaceable class="PARAMETER">name</replaceable></term> <listitem> <para> - The name (optionally schema-qualified) of the table to be truncated. + The name (optionally schema-qualified) of a table to be truncated. </para> </listitem> </varlistentry> @@ -54,14 +54,15 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> <title>Notes</title> <para> - <command>TRUNCATE</> cannot be used if there are foreign-key references - to the table from other tables. Checking validity in such cases would - require table scans, and the whole point is not to do one. + <command>TRUNCATE</> cannot be used on a table that has foreign-key + references from other tables, unless all such tables are also truncated + in the same command. Checking validity in such cases would require table + scans, and the whole point is not to do one. </para> <para> <command>TRUNCATE</> will not run any user-defined <literal>ON - DELETE</literal> triggers that might exist for the table. + DELETE</literal> triggers that might exist for the tables. </para> </refsect1> @@ -69,10 +70,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> <title>Examples</title> <para> - Truncate the table <literal>bigtable</literal>: + Truncate the tables <literal>bigtable</literal> and <literal>fattable</literal>: <programlisting> -TRUNCATE TABLE bigtable; +TRUNCATE TABLE bigtable, fattable; </programlisting> </para> </refsect1> diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 99dc1cf2086b7806b0935cdf03f29ebe6b146ed5..2bf73a5984e276630aadc85d1f4bbce52efa3d1d 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.279 2005/01/10 20:02:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.280 2005/01/27 03:17:17 tgl Exp $ * * * INTERFACE ROUTINES @@ -1985,99 +1985,149 @@ RelationTruncateIndexes(Oid heapId) /* * heap_truncate * - * This routine deletes all data within the specified relation. + * This routine deletes all data within all the specified relations. * * This is not transaction-safe! There is another, transaction-safe - * implementation in commands/cluster.c. We now use this only for + * implementation in commands/tablecmds.c. We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. */ void -heap_truncate(Oid rid) +heap_truncate(List *relids) { - Relation rel; - Oid toastrelid; + List *relations = NIL; + ListCell *cell; + + /* Open relations for processing, and grab exclusive access on each */ + foreach(cell, relids) + { + Oid rid = lfirst_oid(cell); + Relation rel; + Oid toastrelid; - /* Open relation for processing, and grab exclusive access on it. */ - rel = heap_open(rid, AccessExclusiveLock); + rel = heap_open(rid, AccessExclusiveLock); + relations = lappend(relations, rel); + + /* If there is a toast table, add it to the list too */ + toastrelid = rel->rd_rel->reltoastrelid; + if (OidIsValid(toastrelid)) + { + rel = heap_open(toastrelid, AccessExclusiveLock); + relations = lappend(relations, rel); + } + } /* Don't allow truncate on tables that are referenced by foreign keys */ - heap_truncate_check_FKs(rel); + heap_truncate_check_FKs(relations, true); - /* - * Release any buffers associated with this relation. If they're - * dirty, they're just dropped without bothering to flush to disk. - */ - DropRelationBuffers(rel); + /* OK to do it */ + foreach(cell, relations) + { + Relation rel = lfirst(cell); - /* Now truncate the actual data */ - RelationTruncate(rel, 0); + /* + * Release any buffers associated with this relation. If they're + * dirty, they're just dropped without bothering to flush to disk. + */ + DropRelationBuffers(rel); - /* If this relation has indexes, truncate the indexes too */ - RelationTruncateIndexes(rid); + /* Now truncate the actual data */ + RelationTruncate(rel, 0); - /* If it has a toast table, recursively truncate that too */ - toastrelid = rel->rd_rel->reltoastrelid; - if (OidIsValid(toastrelid)) - heap_truncate(toastrelid); + /* If this relation has indexes, truncate the indexes too */ + RelationTruncateIndexes(RelationGetRelid(rel)); - /* - * Close the relation, but keep exclusive lock on it until commit. - */ - heap_close(rel, NoLock); + /* + * Close the relation, but keep exclusive lock on it until commit. + */ + heap_close(rel, NoLock); + } } /* * heap_truncate_check_FKs - * Check for foreign keys referencing a relation that's to be truncated + * Check for foreign keys referencing a list of relations that + * are to be truncated * * We disallow such FKs (except self-referential ones) since the whole point * of TRUNCATE is to not scan the individual rows to be thrown away. * * This is split out so it can be shared by both implementations of truncate. - * Caller should already hold a suitable lock on the relation. + * Caller should already hold a suitable lock on the relations. + * + * tempTables is only used to select an appropriate error message. */ void -heap_truncate_check_FKs(Relation rel) +heap_truncate_check_FKs(List *relations, bool tempTables) { - Oid relid = RelationGetRelid(rel); - ScanKeyData key; + List *oids = NIL; + ListCell *cell; Relation fkeyRel; SysScanDesc fkeyScan; HeapTuple tuple; /* - * Fast path: if the relation has no triggers, it surely has no FKs - * either. + * Build a list of OIDs of the interesting relations. + * + * If a relation has no triggers, then it can neither have FKs nor be + * referenced by a FK from another table, so we can ignore it. */ - if (rel->rd_rel->reltriggers == 0) + foreach(cell, relations) + { + Relation rel = lfirst(cell); + + if (rel->rd_rel->reltriggers != 0) + oids = lappend_oid(oids, RelationGetRelid(rel)); + } + + /* + * Fast path: if no relation has triggers, none has FKs either. + */ + if (oids == NIL) return; /* - * Otherwise, must scan pg_constraint. Right now, this is a seqscan + * Otherwise, must scan pg_constraint. Right now, it is a seqscan * because there is no available index on confrelid. */ fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); - ScanKeyInit(&key, - Anum_pg_constraint_confrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - fkeyScan = systable_beginscan(fkeyRel, NULL, false, - SnapshotNow, 1, &key); + SnapshotNow, 0, NULL); while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); - if (con->contype == CONSTRAINT_FOREIGN && con->conrelid != relid) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot truncate a table referenced in a foreign key constraint"), - errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", - get_rel_name(con->conrelid), - RelationGetRelationName(rel), - NameStr(con->conname)))); + /* Not a foreign key */ + if (con->contype != CONSTRAINT_FOREIGN) + continue; + + /* Not for one of our list of tables */ + if (! list_member_oid(oids, con->confrelid)) + continue; + + /* The referencer should be in our list too */ + if (! list_member_oid(oids, con->conrelid)) + { + if (tempTables) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unsupported ON COMMIT and foreign key combination"), + errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\", but they do not have the same ON COMMIT setting.", + get_rel_name(con->conrelid), + get_rel_name(con->confrelid), + NameStr(con->conname)))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot truncate a table referenced in a foreign key constraint"), + errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", + get_rel_name(con->conrelid), + get_rel_name(con->confrelid), + NameStr(con->conname)), + errhint("Truncate table \"%s\" at the same time.", + get_rel_name(con->conrelid)))); + } } systable_endscan(fkeyScan); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 89dd8dffae015371e5841448428b9107cfc22338..6abf4ab65e151fef541915f0c4f0c6afdeedeec5 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.143 2005/01/24 23:21:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.144 2005/01/27 03:17:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -525,88 +525,113 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior) } /* - * TruncateRelation - * Removes all the rows from a relation. + * 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. */ void -TruncateRelation(const RangeVar *relation) +ExecuteTruncate(List *relations) { - Relation rel; - Oid heap_relid; - Oid toast_relid; + List *rels = NIL; + ListCell *cell; - /* Grab exclusive lock in preparation for truncate */ - rel = heap_openrv(relation, AccessExclusiveLock); + foreach(cell, relations) + { + RangeVar *rv = lfirst(cell); + Relation rel; - /* Only allow truncate on regular tables */ - if (rel->rd_rel->relkind != RELKIND_RELATION) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); + /* Grab exclusive lock in preparation for truncate */ + rel = heap_openrv(rv, AccessExclusiveLock); - /* Permissions checks */ - if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); + /* Only allow truncate on regular tables */ + if (rel->rd_rel->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); - if (!allowSystemTableMods && IsSystemRelation(rel)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied: \"%s\" is a system catalog", - RelationGetRelationName(rel)))); + /* Permissions checks */ + if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); - /* - * We can never allow truncation of shared or nailed-in-cache - * relations, because we can't support changing their relfilenode - * values. - */ - if (rel->rd_rel->relisshared || rel->rd_isnailed) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot truncate system relation \"%s\"", - RelationGetRelationName(rel)))); + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(rel)))); - /* - * Don't allow truncate on temp tables of other backends ... their - * local buffer manager is not going to cope. - */ - if (isOtherTempNamespace(RelationGetNamespace(rel))) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot truncate temporary tables of other sessions"))); + /* + * We can never allow truncation of shared or nailed-in-cache + * relations, because we can't support changing their relfilenode + * values. + */ + if (rel->rd_rel->relisshared || rel->rd_isnailed) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot truncate system relation \"%s\"", + RelationGetRelationName(rel)))); - /* - * Don't allow truncate on tables which are referenced by foreign keys - */ - heap_truncate_check_FKs(rel); + /* + * Don't allow truncate on temp tables of other backends ... their + * local buffer manager is not going to cope. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + 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); + } /* - * Okay, here we go: 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. + * Check foreign key references. */ - setNewRelfilenode(rel); - - heap_relid = RelationGetRelid(rel); - toast_relid = rel->rd_rel->reltoastrelid; - - heap_close(rel, NoLock); + heap_truncate_check_FKs(rels, false); /* - * The same for the toast table, if any. + * OK, truncate each table. */ - if (OidIsValid(toast_relid)) + foreach(cell, rels) { - rel = relation_open(toast_relid, AccessExclusiveLock); + Relation rel = 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); - } - /* - * Reconstruct the indexes to match, and we're done. - */ - reindex_relation(heap_relid, true); + /* + * 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); + } } /*---------- @@ -6013,6 +6038,7 @@ void PreCommit_on_commit_actions(void) { ListCell *l; + List *oids_to_truncate = NIL; foreach(l, on_commits) { @@ -6029,8 +6055,7 @@ PreCommit_on_commit_actions(void) /* Do nothing (there shouldn't be such entries, actually) */ break; case ONCOMMIT_DELETE_ROWS: - heap_truncate(oc->relid); - CommandCounterIncrement(); /* XXX needed? */ + oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); break; case ONCOMMIT_DROP: { @@ -6051,6 +6076,11 @@ PreCommit_on_commit_actions(void) } } } + if (oids_to_truncate != NIL) + { + heap_truncate(oids_to_truncate); + CommandCounterIncrement(); /* XXX needed? */ + } } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index db69cb0a27845a2e72ca5efa08583982c72e53d3..f138402b3c2573a36bb199c324393f488ca3660a 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.295 2004/12/31 21:59:55 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.296 2005/01/27 03:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1815,7 +1815,7 @@ _copyTruncateStmt(TruncateStmt *from) { TruncateStmt *newnode = makeNode(TruncateStmt); - COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(relations); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index d2caee60e1c3337c5c336ea45c55443c9308fdf6..8430eddf69c96418111371a5c35ab0b63a945d0f 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.234 2004/12/31 21:59:55 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.235 2005/01/27 03:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -891,7 +891,7 @@ _equalDropStmt(DropStmt *a, DropStmt *b) static bool _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) { - COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(relations); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 766fe912d5523c0fbf47a990d05bd99f4583d082..757a17e891bfc19cf175fdea22c3a22d06a7493e 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.481 2004/12/31 22:00:27 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.482 2005/01/27 03:17:59 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2681,15 +2681,15 @@ attrs: '.' attr_name /***************************************************************************** * * QUERY: - * truncate table relname + * truncate table relname1, relname2, ... * *****************************************************************************/ TruncateStmt: - TRUNCATE opt_table qualified_name + TRUNCATE opt_table qualified_name_list { TruncateStmt *n = makeNode(TruncateStmt); - n->relation = $3; + n->relations = $3; $$ = (Node *)n; } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 81d557c54ed07e4dce8dbac59ac40b23a921b7eb..006f904f72e0648124f972e425dfb0ca2f7278e6 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.232 2005/01/24 17:46:16 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.233 2005/01/27 03:18:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -575,7 +575,7 @@ ProcessUtility(Node *parsetree, { TruncateStmt *stmt = (TruncateStmt *) parsetree; - TruncateRelation(stmt->relation); + ExecuteTruncate(stmt->relations); } break; diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index bc31c8d777891f58ff7fc21d769dbbc3e9fc6bfc..7a7352c69382b0e3d784b35ad87c81e5da04f66d 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.72 2004/12/31 22:03:24 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.73 2005/01/27 03:18:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,9 +56,9 @@ extern Oid heap_create_with_catalog(const char *relname, extern void heap_drop_with_catalog(Oid relid); -extern void heap_truncate(Oid rid); +extern void heap_truncate(List *relids); -extern void heap_truncate_check_FKs(Relation rel); +extern void heap_truncate_check_FKs(List *relations, bool tempTables); extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 75da81f00006f597d09278b518dca102572e9ffb..5ec8c9a57d1596dc5033729efb17b3cf83acb637 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.21 2004/12/31 22:03:28 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.22 2005/01/27 03:18:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,7 @@ extern void AlterTableInternal(Oid relid, List *cmds, bool recurse); extern void AlterTableCreateToastTable(Oid relOid, bool silent); -extern void TruncateRelation(const RangeVar *relation); +extern void ExecuteTruncate(List *relations); extern void renameatt(Oid myrelid, const char *oldattname, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 14df21516d7780066627df68b6e28c4d054c8454..f5c38619786346f2345793c49520e5ccdd9a5b9e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.271 2004/12/31 22:03:34 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.272 2005/01/27 03:18:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1283,7 +1283,7 @@ typedef struct DropPropertyStmt typedef struct TruncateStmt { NodeTag type; - RangeVar *relation; /* relation to be truncated */ + List *relations; /* relations (RangeVars) to be truncated */ } TruncateStmt; /* ---------------------- diff --git a/src/test/regress/expected/temp.out b/src/test/regress/expected/temp.out index 650f5f704edf2ca7ed8167c78d1719ee1e0abb87..897ae751bd9f6560db1c29e026bbf269c798999a 100644 --- a/src/test/regress/expected/temp.out +++ b/src/test/regress/expected/temp.out @@ -82,3 +82,30 @@ ERROR: relation "temptest" does not exist -- ON COMMIT is only allowed for TEMP CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; ERROR: ON COMMIT can only be used on temporary tables +-- Test foreign keys +BEGIN; +CREATE TEMP TABLE temptest1(col int PRIMARY KEY); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "temptest1_pkey" for table "temptest1" +CREATE TEMP TABLE temptest2(col int REFERENCES temptest1) + ON COMMIT DELETE ROWS; +INSERT INTO temptest1 VALUES (1); +INSERT INTO temptest2 VALUES (1); +COMMIT; +SELECT * FROM temptest1; + col +----- + 1 +(1 row) + +SELECT * FROM temptest2; + col +----- +(0 rows) + +BEGIN; +CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "temptest3_pkey" for table "temptest3" +CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); +COMMIT; +ERROR: unsupported ON COMMIT and foreign key combination +DETAIL: Table "temptest4" references "temptest3" via foreign key constraint "temptest4_col_fkey", but they do not have the same ON COMMIT setting. diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out index a3a00502512d38096ff2ab817def91b87a26fa47..c03deef1e8eea9d99e8020a27ab0743073f60761 100644 --- a/src/test/regress/expected/truncate.out +++ b/src/test/regress/expected/truncate.out @@ -30,23 +30,84 @@ SELECT * FROM truncate_a; ------ (0 rows) --- Test foreign constraint check -CREATE TABLE truncate_b(col1 integer references truncate_a); +-- Test foreign-key checks +CREATE TABLE trunc_b (a int REFERENCES truncate_a); +CREATE TABLE trunc_c (a serial PRIMARY KEY); +NOTICE: CREATE TABLE will create implicit sequence "trunc_c_a_seq" for serial column "trunc_c.a" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "trunc_c_pkey" for table "trunc_c" +CREATE TABLE trunc_d (a int REFERENCES trunc_c); +CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c); +TRUNCATE TABLE truncate_a; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey". +HINT: Truncate table "trunc_b" at the same time. +TRUNCATE TABLE truncate_a,trunc_b; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_e" references "truncate_a" via foreign key constraint "trunc_e_a_fkey". +HINT: Truncate table "trunc_e" at the same time. +TRUNCATE TABLE truncate_a,trunc_b,trunc_e; -- ok +TRUNCATE TABLE truncate_a,trunc_e; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey". +HINT: Truncate table "trunc_b" at the same time. +TRUNCATE TABLE trunc_c; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_d" references "trunc_c" via foreign key constraint "trunc_d_a_fkey". +HINT: Truncate table "trunc_d" at the same time. +TRUNCATE TABLE trunc_c,trunc_d; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_e" references "trunc_c" via foreign key constraint "trunc_e_b_fkey". +HINT: Truncate table "trunc_e" at the same time. +TRUNCATE TABLE trunc_c,trunc_d,trunc_e; -- ok +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; -- fail +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey". +HINT: Truncate table "trunc_b" at the same time. +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; -- ok +-- circular references +ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; +-- Add some data to verify that truncating actually works ... +INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); -SELECT * FROM truncate_a; - col1 ------- - 1 -(1 row) - -TRUNCATE truncate_a; +INSERT INTO trunc_b VALUES (1); +INSERT INTO trunc_d VALUES (1); +INSERT INTO trunc_e VALUES (1,1); +TRUNCATE TABLE trunc_c; ERROR: cannot truncate a table referenced in a foreign key constraint -DETAIL: Table "truncate_b" references "truncate_a" via foreign key constraint "truncate_b_col1_fkey". -SELECT * FROM truncate_a; +DETAIL: Table "trunc_d" references "trunc_c" via foreign key constraint "trunc_d_a_fkey". +HINT: Truncate table "trunc_d" at the same time. +TRUNCATE TABLE trunc_c,trunc_d; +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_e" references "trunc_c" via foreign key constraint "trunc_e_b_fkey". +HINT: Truncate table "trunc_e" at the same time. +TRUNCATE TABLE trunc_c,trunc_d,trunc_e; +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "truncate_a" references "trunc_c" via foreign key constraint "truncate_a_col1_fkey". +HINT: Truncate table "truncate_a" at the same time. +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; +ERROR: cannot truncate a table referenced in a foreign key constraint +DETAIL: Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey". +HINT: Truncate table "trunc_b" at the same time. +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; +-- Verify that truncating did actually work +SELECT * FROM truncate_a + UNION ALL + SELECT * FROM trunc_c + UNION ALL + SELECT * FROM trunc_b + UNION ALL + SELECT * FROM trunc_d; col1 ------ - 1 -(1 row) +(0 rows) + +SELECT * FROM trunc_e; + a | b +---+--- +(0 rows) -DROP TABLE truncate_b; -DROP TABLE truncate_a; +DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; +NOTICE: drop cascades to constraint trunc_e_a_fkey on table trunc_e +NOTICE: drop cascades to constraint trunc_b_a_fkey on table trunc_b +NOTICE: drop cascades to constraint trunc_e_b_fkey on table trunc_e +NOTICE: drop cascades to constraint trunc_d_a_fkey on table trunc_d diff --git a/src/test/regress/sql/temp.sql b/src/test/regress/sql/temp.sql index 397d00bfd94e3682e8896eee2abe605832498a45..972d511ab7635bf659bdabee4b8884831db1f9dd 100644 --- a/src/test/regress/sql/temp.sql +++ b/src/test/regress/sql/temp.sql @@ -83,3 +83,19 @@ SELECT * FROM temptest; -- ON COMMIT is only allowed for TEMP CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; + +-- Test foreign keys +BEGIN; +CREATE TEMP TABLE temptest1(col int PRIMARY KEY); +CREATE TEMP TABLE temptest2(col int REFERENCES temptest1) + ON COMMIT DELETE ROWS; +INSERT INTO temptest1 VALUES (1); +INSERT INTO temptest2 VALUES (1); +COMMIT; +SELECT * FROM temptest1; +SELECT * FROM temptest2; + +BEGIN; +CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; +CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); +COMMIT; diff --git a/src/test/regress/sql/truncate.sql b/src/test/regress/sql/truncate.sql index 79e229feac6136cb2acc774ce7e3a62b4ec45052..09a08e5652a82f5e68605747adf7f349bbd8daaf 100644 --- a/src/test/regress/sql/truncate.sql +++ b/src/test/regress/sql/truncate.sql @@ -14,12 +14,45 @@ TRUNCATE truncate_a; COMMIT; SELECT * FROM truncate_a; --- Test foreign constraint check -CREATE TABLE truncate_b(col1 integer references truncate_a); +-- Test foreign-key checks +CREATE TABLE trunc_b (a int REFERENCES truncate_a); +CREATE TABLE trunc_c (a serial PRIMARY KEY); +CREATE TABLE trunc_d (a int REFERENCES trunc_c); +CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c); + +TRUNCATE TABLE truncate_a; -- fail +TRUNCATE TABLE truncate_a,trunc_b; -- fail +TRUNCATE TABLE truncate_a,trunc_b,trunc_e; -- ok +TRUNCATE TABLE truncate_a,trunc_e; -- fail +TRUNCATE TABLE trunc_c; -- fail +TRUNCATE TABLE trunc_c,trunc_d; -- fail +TRUNCATE TABLE trunc_c,trunc_d,trunc_e; -- ok +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; -- fail +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; -- ok + +-- circular references +ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; + +-- Add some data to verify that truncating actually works ... +INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); -SELECT * FROM truncate_a; -TRUNCATE truncate_a; -SELECT * FROM truncate_a; +INSERT INTO trunc_b VALUES (1); +INSERT INTO trunc_d VALUES (1); +INSERT INTO trunc_e VALUES (1,1); +TRUNCATE TABLE trunc_c; +TRUNCATE TABLE trunc_c,trunc_d; +TRUNCATE TABLE trunc_c,trunc_d,trunc_e; +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; +TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; + +-- Verify that truncating did actually work +SELECT * FROM truncate_a + UNION ALL + SELECT * FROM trunc_c + UNION ALL + SELECT * FROM trunc_b + UNION ALL + SELECT * FROM trunc_d; +SELECT * FROM trunc_e; -DROP TABLE truncate_b; -DROP TABLE truncate_a; +DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;