Commit f07b9689 authored by Tom Lane's avatar Tom Lane

Generalize TRUNCATE to support truncating multiple tables in one

command.  This is useful because we can allow truncation of tables
referenced by foreign keys, so long as the referencing table is
truncated in the same command.

Alvaro Herrera
parent 4fe20123
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -11,7 +11,7 @@ PostgreSQL documentation ...@@ -11,7 +11,7 @@ PostgreSQL documentation
<refnamediv> <refnamediv>
<refname>TRUNCATE</refname> <refname>TRUNCATE</refname>
<refpurpose>empty a table</refpurpose> <refpurpose>empty a table or set of tables</refpurpose>
</refnamediv> </refnamediv>
<indexterm zone="sql-truncate"> <indexterm zone="sql-truncate">
...@@ -20,7 +20,7 @@ PostgreSQL documentation ...@@ -20,7 +20,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -28,10 +28,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> ...@@ -28,10 +28,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
<title>Description</title> <title>Description</title>
<para> <para>
<command>TRUNCATE</command> quickly removes all rows from a <command>TRUNCATE</command> quickly removes all rows from a set of
table. It has the same effect as an unqualified tables. It has the same effect as an unqualified
<command>DELETE</command> but since it does not actually scan the <command>DELETE</command> on each table, but since it does not actually
table it is faster. This is most useful on large tables. scan the tables it is faster. This is most useful on large tables.
</para> </para>
</refsect1> </refsect1>
...@@ -43,7 +43,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> ...@@ -43,7 +43,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="PARAMETER">name</replaceable></term> <term><replaceable class="PARAMETER">name</replaceable></term>
<listitem> <listitem>
<para> <para>
The name (optionally schema-qualified) of the table to be truncated. The name (optionally schema-qualified) of a table to be truncated.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -54,14 +54,15 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> ...@@ -54,14 +54,15 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
<title>Notes</title> <title>Notes</title>
<para> <para>
<command>TRUNCATE</> cannot be used if there are foreign-key references <command>TRUNCATE</> cannot be used on a table that has foreign-key
to the table from other tables. Checking validity in such cases would references from other tables, unless all such tables are also truncated
require table scans, and the whole point is not to do one. in the same command. Checking validity in such cases would require table
scans, and the whole point is not to do one.
</para> </para>
<para> <para>
<command>TRUNCATE</> will not run any user-defined <literal>ON <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> </para>
</refsect1> </refsect1>
...@@ -69,10 +70,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> ...@@ -69,10 +70,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
<title>Examples</title> <title>Examples</title>
<para> <para>
Truncate the table <literal>bigtable</literal>: Truncate the tables <literal>bigtable</literal> and <literal>fattable</literal>:
<programlisting> <programlisting>
TRUNCATE TABLE bigtable; TRUNCATE TABLE bigtable, fattable;
</programlisting> </programlisting>
</para> </para>
</refsect1> </refsect1>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -1985,23 +1985,44 @@ RelationTruncateIndexes(Oid heapId) ...@@ -1985,23 +1985,44 @@ RelationTruncateIndexes(Oid heapId)
/* /*
* heap_truncate * 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 * 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. * ON COMMIT truncation of temporary tables, where it doesn't matter.
*/ */
void void
heap_truncate(Oid rid) heap_truncate(List *relids)
{ {
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; Relation rel;
Oid toastrelid; 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 */ /* Don't allow truncate on tables that are referenced by foreign keys */
heap_truncate_check_FKs(rel); heap_truncate_check_FKs(relations, true);
/* OK to do it */
foreach(cell, relations)
{
Relation rel = lfirst(cell);
/* /*
* Release any buffers associated with this relation. If they're * Release any buffers associated with this relation. If they're
...@@ -2013,71 +2034,100 @@ heap_truncate(Oid rid) ...@@ -2013,71 +2034,100 @@ heap_truncate(Oid rid)
RelationTruncate(rel, 0); RelationTruncate(rel, 0);
/* If this relation has indexes, truncate the indexes too */ /* If this relation has indexes, truncate the indexes too */
RelationTruncateIndexes(rid); RelationTruncateIndexes(RelationGetRelid(rel));
/* 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. * Close the relation, but keep exclusive lock on it until commit.
*/ */
heap_close(rel, NoLock); heap_close(rel, NoLock);
}
} }
/* /*
* heap_truncate_check_FKs * 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 * 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. * 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. * 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 void
heap_truncate_check_FKs(Relation rel) heap_truncate_check_FKs(List *relations, bool tempTables)
{ {
Oid relid = RelationGetRelid(rel); List *oids = NIL;
ScanKeyData key; ListCell *cell;
Relation fkeyRel; Relation fkeyRel;
SysScanDesc fkeyScan; SysScanDesc fkeyScan;
HeapTuple tuple; HeapTuple tuple;
/* /*
* Fast path: if the relation has no triggers, it surely has no FKs * Build a list of OIDs of the interesting relations.
* either. *
* 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.
*/
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 (rel->rd_rel->reltriggers == 0) if (oids == NIL)
return; 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. * because there is no available index on confrelid.
*/ */
fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock);
ScanKeyInit(&key,
Anum_pg_constraint_confrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
fkeyScan = systable_beginscan(fkeyRel, NULL, false, fkeyScan = systable_beginscan(fkeyRel, NULL, false,
SnapshotNow, 1, &key); SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan)))
{ {
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
if (con->contype == CONSTRAINT_FOREIGN && con->conrelid != relid) /* 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, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot truncate a table referenced in a foreign key constraint"), errmsg("cannot truncate a table referenced in a foreign key constraint"),
errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".",
get_rel_name(con->conrelid), get_rel_name(con->conrelid),
RelationGetRelationName(rel), get_rel_name(con->confrelid),
NameStr(con->conname)))); NameStr(con->conname)),
errhint("Truncate table \"%s\" at the same time.",
get_rel_name(con->conrelid))));
}
} }
systable_endscan(fkeyScan); systable_endscan(fkeyScan);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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,18 +525,29 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior) ...@@ -525,18 +525,29 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
} }
/* /*
* TruncateRelation * ExecuteTruncate
* Removes all the rows from a relation. * 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 void
TruncateRelation(const RangeVar *relation) ExecuteTruncate(List *relations)
{ {
List *rels = NIL;
ListCell *cell;
foreach(cell, relations)
{
RangeVar *rv = lfirst(cell);
Relation rel; Relation rel;
Oid heap_relid;
Oid toast_relid;
/* Grab exclusive lock in preparation for truncate */ /* Grab exclusive lock in preparation for truncate */
rel = heap_openrv(relation, AccessExclusiveLock); rel = heap_openrv(rv, AccessExclusiveLock);
/* Only allow truncate on regular tables */ /* Only allow truncate on regular tables */
if (rel->rd_rel->relkind != RELKIND_RELATION) if (rel->rd_rel->relkind != RELKIND_RELATION)
...@@ -576,15 +587,28 @@ TruncateRelation(const RangeVar *relation) ...@@ -576,15 +587,28 @@ TruncateRelation(const RangeVar *relation)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot truncate temporary tables of other sessions"))); 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, false);
/* /*
* Don't allow truncate on tables which are referenced by foreign keys * OK, truncate each table.
*/ */
heap_truncate_check_FKs(rel); foreach(cell, rels)
{
Relation rel = lfirst(cell);
Oid heap_relid;
Oid toast_relid;
/* /*
* Okay, here we go: create a new empty storage file for the relation, * Create a new empty storage file for the relation, and assign it as
* and assign it as the relfilenode value. The old storage file is * the relfilenode value. The old storage file is scheduled for
* scheduled for deletion at commit. * deletion at commit.
*/ */
setNewRelfilenode(rel); setNewRelfilenode(rel);
...@@ -607,6 +631,7 @@ TruncateRelation(const RangeVar *relation) ...@@ -607,6 +631,7 @@ TruncateRelation(const RangeVar *relation)
* Reconstruct the indexes to match, and we're done. * Reconstruct the indexes to match, and we're done.
*/ */
reindex_relation(heap_relid, true); reindex_relation(heap_relid, true);
}
} }
/*---------- /*----------
...@@ -6013,6 +6038,7 @@ void ...@@ -6013,6 +6038,7 @@ void
PreCommit_on_commit_actions(void) PreCommit_on_commit_actions(void)
{ {
ListCell *l; ListCell *l;
List *oids_to_truncate = NIL;
foreach(l, on_commits) foreach(l, on_commits)
{ {
...@@ -6029,8 +6055,7 @@ PreCommit_on_commit_actions(void) ...@@ -6029,8 +6055,7 @@ PreCommit_on_commit_actions(void)
/* Do nothing (there shouldn't be such entries, actually) */ /* Do nothing (there shouldn't be such entries, actually) */
break; break;
case ONCOMMIT_DELETE_ROWS: case ONCOMMIT_DELETE_ROWS:
heap_truncate(oc->relid); oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
CommandCounterIncrement(); /* XXX needed? */
break; break;
case ONCOMMIT_DROP: case ONCOMMIT_DROP:
{ {
...@@ -6051,6 +6076,11 @@ PreCommit_on_commit_actions(void) ...@@ -6051,6 +6076,11 @@ PreCommit_on_commit_actions(void)
} }
} }
} }
if (oids_to_truncate != NIL)
{
heap_truncate(oids_to_truncate);
CommandCounterIncrement(); /* XXX needed? */
}
} }
/* /*
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,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/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) ...@@ -1815,7 +1815,7 @@ _copyTruncateStmt(TruncateStmt *from)
{ {
TruncateStmt *newnode = makeNode(TruncateStmt); TruncateStmt *newnode = makeNode(TruncateStmt);
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relations);
return newnode; return newnode;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,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/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) ...@@ -891,7 +891,7 @@ _equalDropStmt(DropStmt *a, DropStmt *b)
static bool static bool
_equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b)
{ {
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relations);
return true; return true;
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -2681,15 +2681,15 @@ attrs: '.' attr_name ...@@ -2681,15 +2681,15 @@ attrs: '.' attr_name
/***************************************************************************** /*****************************************************************************
* *
* QUERY: * QUERY:
* truncate table relname * truncate table relname1, relname2, ...
* *
*****************************************************************************/ *****************************************************************************/
TruncateStmt: TruncateStmt:
TRUNCATE opt_table qualified_name TRUNCATE opt_table qualified_name_list
{ {
TruncateStmt *n = makeNode(TruncateStmt); TruncateStmt *n = makeNode(TruncateStmt);
n->relation = $3; n->relations = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -575,7 +575,7 @@ ProcessUtility(Node *parsetree,
{ {
TruncateStmt *stmt = (TruncateStmt *) parsetree; TruncateStmt *stmt = (TruncateStmt *) parsetree;
TruncateRelation(stmt->relation); ExecuteTruncate(stmt->relations);
} }
break; break;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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/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, ...@@ -56,9 +56,9 @@ extern Oid heap_create_with_catalog(const char *relname,
extern void heap_drop_with_catalog(Oid relid); 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, extern List *AddRelationRawConstraints(Relation rel,
List *rawColDefaults, List *rawColDefaults,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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/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); ...@@ -27,7 +27,7 @@ extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
extern void AlterTableCreateToastTable(Oid relOid, bool silent); extern void AlterTableCreateToastTable(Oid relOid, bool silent);
extern void TruncateRelation(const RangeVar *relation); extern void ExecuteTruncate(List *relations);
extern void renameatt(Oid myrelid, extern void renameatt(Oid myrelid,
const char *oldattname, const char *oldattname,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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/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 ...@@ -1283,7 +1283,7 @@ typedef struct DropPropertyStmt
typedef struct TruncateStmt typedef struct TruncateStmt
{ {
NodeTag type; NodeTag type;
RangeVar *relation; /* relation to be truncated */ List *relations; /* relations (RangeVars) to be truncated */
} TruncateStmt; } TruncateStmt;
/* ---------------------- /* ----------------------
......
...@@ -82,3 +82,30 @@ ERROR: relation "temptest" does not exist ...@@ -82,3 +82,30 @@ ERROR: relation "temptest" does not exist
-- ON COMMIT is only allowed for TEMP -- ON COMMIT is only allowed for TEMP
CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS;
ERROR: ON COMMIT can only be used on temporary tables 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.
...@@ -30,23 +30,84 @@ SELECT * FROM truncate_a; ...@@ -30,23 +30,84 @@ SELECT * FROM truncate_a;
------ ------
(0 rows) (0 rows)
-- Test foreign constraint check -- Test foreign-key checks
CREATE TABLE truncate_b(col1 integer references truncate_a); 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 truncate_a VALUES (1);
SELECT * FROM truncate_a; INSERT INTO trunc_b VALUES (1);
col1 INSERT INTO trunc_d VALUES (1);
------ INSERT INTO trunc_e VALUES (1,1);
1 TRUNCATE TABLE trunc_c;
(1 row)
TRUNCATE truncate_a;
ERROR: cannot truncate a table referenced in a foreign key constraint 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". DETAIL: Table "trunc_d" references "trunc_c" via foreign key constraint "trunc_d_a_fkey".
SELECT * FROM truncate_a; 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 col1
------ ------
1 (0 rows)
(1 row)
SELECT * FROM trunc_e;
a | b
---+---
(0 rows)
DROP TABLE truncate_b; DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
DROP TABLE truncate_a; 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
...@@ -83,3 +83,19 @@ SELECT * FROM temptest; ...@@ -83,3 +83,19 @@ SELECT * FROM temptest;
-- ON COMMIT is only allowed for TEMP -- ON COMMIT is only allowed for TEMP
CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; 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;
...@@ -14,12 +14,45 @@ TRUNCATE truncate_a; ...@@ -14,12 +14,45 @@ TRUNCATE truncate_a;
COMMIT; COMMIT;
SELECT * FROM truncate_a; SELECT * FROM truncate_a;
-- Test foreign constraint check -- Test foreign-key checks
CREATE TABLE truncate_b(col1 integer references truncate_a); 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 truncate_a VALUES (1);
SELECT * FROM truncate_a; INSERT INTO trunc_b VALUES (1);
TRUNCATE truncate_a; INSERT INTO trunc_d VALUES (1);
SELECT * FROM truncate_a; 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,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
DROP TABLE truncate_a;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment