Index: doc/src/sgml/ref/truncate.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/truncate.sgml,v retrieving revision 1.17 diff -c -r1.17 truncate.sgml *** doc/src/sgml/ref/truncate.sgml 23 Mar 2004 13:21:41 -0000 1.17 --- doc/src/sgml/ref/truncate.sgml 6 Nov 2004 01:20:19 -0000 *************** *** 11,17 **** TRUNCATE ! empty a table --- 11,17 ---- TRUNCATE ! empty a set of tables *************** *** 20,26 **** ! TRUNCATE [ TABLE ] name --- 20,26 ---- ! TRUNCATE [ TABLE ] name [, ...] *************** *** 28,37 **** Description ! TRUNCATE quickly removes all rows from a ! table. It has the same effect as an unqualified ! DELETE but since it does not actually scan the ! table it is faster. This is most useful on large tables. --- 28,37 ---- Description ! TRUNCATE quickly removes all rows from a set of ! tables. It has the same effect as an unqualified ! DELETE on each of them, but since it does not actually ! scan the tables it is faster. This is most useful on large tables. *************** *** 55,67 **** 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. TRUNCATE will not run any user-defined ON ! DELETE triggers that might exist for the table. --- 55,68 ---- TRUNCATE cannot be used if there are foreign-key references ! to the table 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. TRUNCATE will not run any user-defined ON ! DELETE triggers that might exist for the tables. *************** *** 69,78 **** Examples ! Truncate the table bigtable: ! TRUNCATE TABLE bigtable; --- 70,79 ---- Examples ! Truncate the tables bigtable and fattable: ! TRUNCATE TABLE bigtable, fattable; Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.276 diff -c -r1.276 heap.c *** src/backend/catalog/heap.c 31 Aug 2004 17:10:36 -0000 1.276 --- src/backend/catalog/heap.c 6 Nov 2004 03:23:44 -0000 *************** *** 2002,2102 **** /* * heap_truncate * ! * This routine deletes all data within the specified relation. * * This is not transaction-safe! There is another, transaction-safe * implementation in commands/cluster.c. We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. */ void ! heap_truncate(Oid rid) { ! Relation rel; ! Oid toastrelid; ! ! /* Open relation for processing, and grab exclusive access on it. */ ! rel = heap_open(rid, AccessExclusiveLock); ! ! /* Don't allow truncate on tables that are referenced by foreign keys */ ! heap_truncate_check_FKs(rel); /* ! * Release any buffers associated with this relation. If they're ! * dirty, they're just dropped without bothering to flush to disk. */ ! DropRelationBuffers(rel); ! /* Now truncate the actual data */ ! RelationTruncate(rel, 0); ! /* If this relation has indexes, truncate the indexes too */ ! RelationTruncateIndexes(rid); ! /* If it has a toast table, recursively truncate that too */ ! toastrelid = rel->rd_rel->reltoastrelid; ! if (OidIsValid(toastrelid)) ! heap_truncate(toastrelid); ! /* ! * 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 * * 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. */ void ! heap_truncate_check_FKs(Relation rel) { ! Oid relid = RelationGetRelid(rel); ! ScanKeyData key; Relation fkeyRel; - SysScanDesc fkeyScan; - HeapTuple tuple; /* ! * Fast path: if the relation has no triggers, it surely has no FKs ! * either. */ ! if (rel->rd_rel->reltriggers == 0) return; /* ! * Otherwise, must scan pg_constraint. Right now, this 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); ! ! 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)))); } - systable_endscan(fkeyScan); heap_close(fkeyRel, AccessShareLock); } --- 2002,2163 ---- /* * heap_truncate * ! * 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 * ON COMMIT truncation of temporary tables, where it doesn't matter. */ void ! heap_truncate(List *relids) { ! List *relations = NIL; ! List *toasttables = NIL; ! ListCell *cell; /* ! * Fast path when there's nothing to truncate */ ! if (list_length(relids) == 0) ! return; ! ! foreach (cell, relids) ! { ! Oid rid = lfirst_oid(cell); ! Relation rel; ! /* Open relation for processing, and grab exclusive access on it. */ ! rel = heap_open(rid, AccessExclusiveLock); ! relations = lappend(relations, rel); ! } ! /* Don't allow truncate on tables that are referenced by foreign keys */ ! heap_truncate_check_FKs(relations); ! ! foreach (cell, relations) ! { ! Relation rel = lfirst(cell); ! Oid toastrelid; ! /* ! * Release any buffers associated with this relation. If they're ! * dirty, they're just dropped without bothering to flush to disk. ! */ ! DropRelationBuffers(rel); ! ! /* Now truncate the actual data */ ! RelationTruncate(rel, 0); ! ! /* If this relation has indexes, truncate the indexes too */ ! RelationTruncateIndexes(RelationGetRelid(rel)); ! ! /* ! * If it has a toast table, schedule it for later truncation. ! * Note that we cannot just append it to the list being processed, ! * because it's not open nor locked. ! */ ! toastrelid = rel->rd_rel->reltoastrelid; ! if (OidIsValid(toastrelid)) ! toasttables = lappend_oid(toasttables, toastrelid); ! ! /* ! * Close the relation, but keep exclusive lock on it until commit. ! */ ! heap_close(rel, NoLock); ! } ! ! /* now truncate TOAST tables */ ! if (list_length(toasttables) > 0) ! heap_truncate(toasttables); } /* * heap_truncate_check_FKs ! * Check for foreign keys referencing a list of relations that ! * have 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 relations. */ void ! heap_truncate_check_FKs(List *relations) { ! List *rels = NIL; ! List *oids = NIL; ! ListCell *cell; Relation fkeyRel; /* ! * Get the list of involved relations and their Oids. */ ! foreach(cell, relations) ! { ! Relation rel = lfirst(cell); ! ! /* ! * If a relation has no triggers, then it can't neither ! * have FKs nor be referenced by a FK from another table, ! * so skip it. ! */ ! if (rel->rd_rel->reltriggers == 0) ! continue; ! ! rels = lappend(rels, rel); ! oids = lappend_oid(oids, RelationGetRelid(rel)); ! } ! ! /* ! * Fast path: if no relation has triggers, none has FKs either. ! */ ! if (list_length(rels) == 0) return; /* ! * Otherwise, must scan pg_constraint. Right now, these are seqscans * because there is no available index on confrelid. + * + * XXX -- is there a way to do it in one sweep? */ fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); ! foreach (cell, rels) { ! Relation rel = lfirst(cell); ! Oid relid = RelationGetRelid(rel); ! SysScanDesc fkeyScan; ! HeapTuple tuple; ! ScanKeyData key; ! ! ScanKeyInit(&key, ! Anum_pg_constraint_confrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(relid)); ! ! fkeyScan = systable_beginscan(fkeyRel, NULL, false, ! SnapshotNow, 1, &key); ! ! while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) ! { ! Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); ! ! if (con->contype == CONSTRAINT_FOREIGN && ! ! list_member_oid(oids, con->conrelid)) ! 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)), ! errhint("Truncate table \"%s\" at the same time", ! get_rel_name(con->conrelid)))); ! } ! systable_endscan(fkeyScan); } heap_close(fkeyRel, AccessShareLock); } Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.138 diff -c -r1.138 tablecmds.c *** src/backend/commands/tablecmds.c 30 Oct 2004 20:52:56 -0000 1.138 --- src/backend/commands/tablecmds.c 6 Nov 2004 03:32:24 -0000 *************** *** 525,612 **** } /* ! * TruncateRelation ! * Removes all the rows from a relation. */ void ! TruncateRelation(const RangeVar *relation) { ! Relation rel; ! Oid heap_relid; ! Oid toast_relid; ! /* Grab exclusive lock in preparation for truncate */ ! rel = heap_openrv(relation, AccessExclusiveLock); ! ! /* 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)))); ! /* Permissions checks */ ! if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(rel)); ! if (!allowSystemTableMods && IsSystemRelation(rel)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied: \"%s\" is a system catalog", ! 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)))); ! /* ! * 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"))); ! /* ! * Don't allow truncate on tables which are referenced by foreign keys ! */ ! heap_truncate_check_FKs(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. ! */ ! 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); } /*---------- --- 525,634 ---- } /* ! * 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 ! ExecuteTruncate(List *relations) { ! List *rels = NIL; ! ListCell *cell; ! foreach (cell, relations) ! { ! RangeVar *rv = lfirst(cell); ! Relation rel; ! /* Grab exclusive lock in preparation for truncate */ ! rel = heap_openrv(rv, AccessExclusiveLock); ! /* 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)))); ! /* Permissions checks */ ! if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(rel)); ! if (!allowSystemTableMods && IsSystemRelation(rel)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied: \"%s\" is a system catalog", ! 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)))); ! /* ! * 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); ! } /* ! * Check foreign key references. */ ! heap_truncate_check_FKs(rels); ! ! foreach (cell, rels) { ! 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); ! /* ! * 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); ! } } /*---------- *************** *** 5996,6001 **** --- 6018,6024 ---- PreCommit_on_commit_actions(void) { ListCell *l; + List *oids_to_truncate = NIL; foreach(l, on_commits) { *************** *** 6012,6019 **** /* Do nothing (there shouldn't be such entries, actually) */ break; case ONCOMMIT_DELETE_ROWS: ! heap_truncate(oc->relid); ! CommandCounterIncrement(); /* XXX needed? */ break; case ONCOMMIT_DROP: { --- 6035,6041 ---- /* Do nothing (there shouldn't be such entries, actually) */ break; case ONCOMMIT_DELETE_ROWS: ! oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); break; case ONCOMMIT_DROP: { *************** *** 6034,6039 **** --- 6056,6064 ---- } } } + if (list_length(oids_to_truncate) > 0) + heap_truncate(oids_to_truncate); + CommandCounterIncrement(); /* XXX needed? */ } /* Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.292 diff -c -r1.292 copyfuncs.c *** src/backend/nodes/copyfuncs.c 29 Aug 2004 05:06:43 -0000 1.292 --- src/backend/nodes/copyfuncs.c 5 Nov 2004 23:04:07 -0000 *************** *** 1800,1806 **** { TruncateStmt *newnode = makeNode(TruncateStmt); ! COPY_NODE_FIELD(relation); return newnode; } --- 1800,1806 ---- { TruncateStmt *newnode = makeNode(TruncateStmt); ! COPY_NODE_FIELD(relations); return newnode; } Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.231 diff -c -r1.231 equalfuncs.c *** src/backend/nodes/equalfuncs.c 29 Aug 2004 05:06:43 -0000 1.231 --- src/backend/nodes/equalfuncs.c 5 Nov 2004 23:04:22 -0000 *************** *** 873,879 **** static bool _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) { ! COMPARE_NODE_FIELD(relation); return true; } --- 873,879 ---- static bool _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) { ! COMPARE_NODE_FIELD(relations); return true; } Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.478 diff -c -r2.478 gram.y *** src/backend/parser/gram.y 1 Oct 2004 16:39:59 -0000 2.478 --- src/backend/parser/gram.y 5 Nov 2004 22:52:07 -0000 *************** *** 2686,2700 **** /***************************************************************************** * * QUERY: ! * truncate table relname * *****************************************************************************/ TruncateStmt: ! TRUNCATE opt_table qualified_name { TruncateStmt *n = makeNode(TruncateStmt); ! n->relation = $3; $$ = (Node *)n; } ; --- 2686,2700 ---- /***************************************************************************** * * QUERY: ! * truncate table relname1, relname2, ... * *****************************************************************************/ TruncateStmt: ! TRUNCATE opt_table qualified_name_list { TruncateStmt *n = makeNode(TruncateStmt); ! n->relations = $3; $$ = (Node *)n; } ; Index: src/backend/tcop/utility.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.230 diff -c -r1.230 utility.c *** src/backend/tcop/utility.c 13 Sep 2004 20:07:06 -0000 1.230 --- src/backend/tcop/utility.c 5 Nov 2004 22:52:07 -0000 *************** *** 575,581 **** { TruncateStmt *stmt = (TruncateStmt *) parsetree; ! TruncateRelation(stmt->relation); } break; --- 575,581 ---- { TruncateStmt *stmt = (TruncateStmt *) parsetree; ! ExecuteTruncate(stmt->relations); } break; Index: src/include/catalog/heap.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/heap.h,v retrieving revision 1.71 diff -c -r1.71 heap.h *** src/include/catalog/heap.h 31 Aug 2004 17:10:36 -0000 1.71 --- src/include/catalog/heap.h 6 Nov 2004 02:15:06 -0000 *************** *** 56,64 **** extern void heap_drop_with_catalog(Oid relid); ! extern void heap_truncate(Oid rid); ! extern void heap_truncate_check_FKs(Relation rel); extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, --- 56,64 ---- extern void heap_drop_with_catalog(Oid relid); ! extern void heap_truncate(List *relids); ! extern void heap_truncate_check_FKs(List *relations); extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, Index: src/include/commands/tablecmds.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/tablecmds.h,v retrieving revision 1.20 diff -c -r1.20 tablecmds.h *** src/include/commands/tablecmds.h 16 Sep 2004 16:58:39 -0000 1.20 --- src/include/commands/tablecmds.h 5 Nov 2004 23:22:07 -0000 *************** *** 27,33 **** extern void AlterTableCreateToastTable(Oid relOid, bool silent); ! extern void TruncateRelation(const RangeVar *relation); extern void renameatt(Oid myrelid, const char *oldattname, --- 27,33 ---- extern void AlterTableCreateToastTable(Oid relOid, bool silent); ! extern void ExecuteTruncate(List *relations); extern void renameatt(Oid myrelid, const char *oldattname, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.269 diff -c -r1.269 parsenodes.h *** src/include/nodes/parsenodes.h 29 Aug 2004 05:06:57 -0000 1.269 --- src/include/nodes/parsenodes.h 5 Nov 2004 22:52:07 -0000 *************** *** 1284,1290 **** typedef struct TruncateStmt { NodeTag type; ! RangeVar *relation; /* relation to be truncated */ } TruncateStmt; /* ---------------------- --- 1284,1290 ---- typedef struct TruncateStmt { NodeTag type; ! List *relations; /* relations (RangeVars) to be truncated */ } TruncateStmt; /* ---------------------- Index: src/test/regress/expected/temp.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/temp.out,v retrieving revision 1.9 diff -c -r1.9 temp.out *** src/test/regress/expected/temp.out 25 Sep 2003 06:58:06 -0000 1.9 --- src/test/regress/expected/temp.out 6 Nov 2004 04:32:09 -0000 *************** *** 82,84 **** --- 82,103 ---- -- 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) ON COMMIT DELETE ROWS; + 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 + ----- + (0 rows) + + SELECT * FROM temptest2; + col + ----- + (0 rows) + Index: src/test/regress/expected/truncate.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/truncate.out,v retrieving revision 1.9 diff -c -r1.9 truncate.out *** src/test/regress/expected/truncate.out 10 Jun 2004 17:56:01 -0000 1.9 --- src/test/regress/expected/truncate.out 6 Nov 2004 04:43:24 -0000 *************** *** 30,52 **** ------ (0 rows) ! -- Test foreign constraint check ! CREATE TABLE truncate_b(col1 integer references truncate_a); INSERT INTO truncate_a VALUES (1); ! SELECT * FROM truncate_a; ! col1 ! ------ ! 1 ! (1 row) ! ! TRUNCATE truncate_a; 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; col1 ------ ! 1 ! (1 row) ! DROP TABLE truncate_b; ! DROP TABLE truncate_a; --- 30,113 ---- ------ (0 rows) ! -- 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); ! 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 "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 ------ ! (0 rows) ! ! SELECT * FROM trunc_e; ! a | b ! ---+--- ! (0 rows) ! 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 Index: src/test/regress/sql/temp.sql =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/sql/temp.sql,v retrieving revision 1.5 diff -c -r1.5 temp.sql *** src/test/regress/sql/temp.sql 14 May 2003 03:26:03 -0000 1.5 --- src/test/regress/sql/temp.sql 6 Nov 2004 04:31:36 -0000 *************** *** 83,85 **** --- 83,97 ---- -- 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) ON COMMIT DELETE ROWS; + 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; Index: src/test/regress/sql/truncate.sql =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/sql/truncate.sql,v retrieving revision 1.2 diff -c -r1.2 truncate.sql *** src/test/regress/sql/truncate.sql 23 Nov 2002 04:05:52 -0000 1.2 --- src/test/regress/sql/truncate.sql 6 Nov 2004 04:42:24 -0000 *************** *** 14,25 **** COMMIT; SELECT * FROM truncate_a; ! -- Test foreign constraint check ! CREATE TABLE truncate_b(col1 integer references truncate_a); INSERT INTO truncate_a VALUES (1); ! SELECT * FROM truncate_a; ! TRUNCATE truncate_a; ! SELECT * FROM truncate_a; ! DROP TABLE truncate_b; ! DROP TABLE truncate_a; --- 14,58 ---- COMMIT; SELECT * FROM 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); ! 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_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;