|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.279 2005/01/10 20:02:19 tgl Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.280 2005/01/27 03:17:17 tgl Exp $ |
12 | 12 | *
|
13 | 13 | *
|
14 | 14 | * INTERFACE ROUTINES
|
@@ -1985,99 +1985,149 @@ RelationTruncateIndexes(Oid heapId)
|
1985 | 1985 | /*
|
1986 | 1986 | * heap_truncate
|
1987 | 1987 | *
|
1988 |
| - * This routine deletes all data within the specifiedrelation. |
| 1988 | + * This routine deletes all data withinallthe specifiedrelations. |
1989 | 1989 | *
|
1990 | 1990 | * This is not transaction-safe! There is another, transaction-safe
|
1991 |
| - * implementation in commands/cluster.c. We now use this only for |
| 1991 | + * implementation in commands/tablecmds.c. We now use this only for |
1992 | 1992 | * ON COMMIT truncation of temporary tables, where it doesn't matter.
|
1993 | 1993 | */
|
1994 | 1994 | void
|
1995 |
| -heap_truncate(Oidrid) |
| 1995 | +heap_truncate(List*relids) |
1996 | 1996 | {
|
1997 |
| -Relationrel; |
1998 |
| -Oidtoastrelid; |
| 1997 | +List*relations=NIL; |
| 1998 | +ListCell*cell; |
| 1999 | + |
| 2000 | +/* Open relations for processing, and grab exclusive access on each */ |
| 2001 | +foreach(cell,relids) |
| 2002 | +{ |
| 2003 | +Oidrid=lfirst_oid(cell); |
| 2004 | +Relationrel; |
| 2005 | +Oidtoastrelid; |
1999 | 2006 |
|
2000 |
| -/* Open relation for processing, and grab exclusive access on it. */ |
2001 |
| -rel=heap_open(rid,AccessExclusiveLock); |
| 2007 | +rel=heap_open(rid,AccessExclusiveLock); |
| 2008 | +relations=lappend(relations,rel); |
| 2009 | + |
| 2010 | +/* If there is a toast table, add it to the list too */ |
| 2011 | +toastrelid=rel->rd_rel->reltoastrelid; |
| 2012 | +if (OidIsValid(toastrelid)) |
| 2013 | +{ |
| 2014 | +rel=heap_open(toastrelid,AccessExclusiveLock); |
| 2015 | +relations=lappend(relations,rel); |
| 2016 | +} |
| 2017 | +} |
2002 | 2018 |
|
2003 | 2019 | /* Don't allow truncate on tables that are referenced by foreign keys */
|
2004 |
| -heap_truncate_check_FKs(rel); |
| 2020 | +heap_truncate_check_FKs(relations, true); |
2005 | 2021 |
|
2006 |
| -/* |
2007 |
| - * Release any buffers associated with this relation. If they're |
2008 |
| - * dirty, they're just dropped without bothering to flush to disk. |
2009 |
| - */ |
2010 |
| -DropRelationBuffers(rel); |
| 2022 | +/* OK to do it */ |
| 2023 | +foreach(cell,relations) |
| 2024 | +{ |
| 2025 | +Relationrel=lfirst(cell); |
2011 | 2026 |
|
2012 |
| -/* Now truncate the actual data */ |
2013 |
| -RelationTruncate(rel,0); |
| 2027 | +/* |
| 2028 | + * Release any buffers associated with this relation. If they're |
| 2029 | + * dirty, they're just dropped without bothering to flush to disk. |
| 2030 | + */ |
| 2031 | +DropRelationBuffers(rel); |
2014 | 2032 |
|
2015 |
| -/*If this relation has indexes,truncate theindexes too */ |
2016 |
| -RelationTruncateIndexes(rid); |
| 2033 | +/*Nowtruncate theactual data */ |
| 2034 | +RelationTruncate(rel,0); |
2017 | 2035 |
|
2018 |
| -/* If it has a toast table, recursively truncate that too */ |
2019 |
| -toastrelid=rel->rd_rel->reltoastrelid; |
2020 |
| -if (OidIsValid(toastrelid)) |
2021 |
| -heap_truncate(toastrelid); |
| 2036 | +/* If this relation has indexes, truncate the indexes too */ |
| 2037 | +RelationTruncateIndexes(RelationGetRelid(rel)); |
2022 | 2038 |
|
2023 |
| -/* |
2024 |
| - * Close the relation, but keep exclusive lock on it until commit. |
2025 |
| - */ |
2026 |
| -heap_close(rel,NoLock); |
| 2039 | +/* |
| 2040 | + * Close the relation, but keep exclusive lock on it until commit. |
| 2041 | + */ |
| 2042 | +heap_close(rel,NoLock); |
| 2043 | +} |
2027 | 2044 | }
|
2028 | 2045 |
|
2029 | 2046 | /*
|
2030 | 2047 | * heap_truncate_check_FKs
|
2031 |
| - *Check for foreign keys referencing a relation that's to be truncated |
| 2048 | + *Check for foreign keys referencing a list of relations that |
| 2049 | + *are to be truncated |
2032 | 2050 | *
|
2033 | 2051 | * We disallow such FKs (except self-referential ones) since the whole point
|
2034 | 2052 | * of TRUNCATE is to not scan the individual rows to be thrown away.
|
2035 | 2053 | *
|
2036 | 2054 | * This is split out so it can be shared by both implementations of truncate.
|
2037 |
| - * Caller should already hold a suitable lock on the relation. |
| 2055 | + * Caller should already hold a suitable lock on the relations. |
| 2056 | + * |
| 2057 | + * tempTables is only used to select an appropriate error message. |
2038 | 2058 | */
|
2039 | 2059 | void
|
2040 |
| -heap_truncate_check_FKs(Relationrel) |
| 2060 | +heap_truncate_check_FKs(List*relations,booltempTables) |
2041 | 2061 | {
|
2042 |
| -Oidrelid=RelationGetRelid(rel); |
2043 |
| -ScanKeyDatakey; |
| 2062 | +List*oids=NIL; |
| 2063 | +ListCell*cell; |
2044 | 2064 | RelationfkeyRel;
|
2045 | 2065 | SysScanDescfkeyScan;
|
2046 | 2066 | HeapTupletuple;
|
2047 | 2067 |
|
2048 | 2068 | /*
|
2049 |
| - * Fast path: if the relation has no triggers, it surely has no FKs |
2050 |
| - * either. |
| 2069 | + * Build a list of OIDs of the interesting relations. |
| 2070 | + * |
| 2071 | + * If a relation has no triggers, then it can neither have FKs nor be |
| 2072 | + * referenced by a FK from another table, so we can ignore it. |
2051 | 2073 | */
|
2052 |
| -if (rel->rd_rel->reltriggers==0) |
| 2074 | +foreach(cell,relations) |
| 2075 | +{ |
| 2076 | +Relationrel=lfirst(cell); |
| 2077 | + |
| 2078 | +if (rel->rd_rel->reltriggers!=0) |
| 2079 | +oids=lappend_oid(oids,RelationGetRelid(rel)); |
| 2080 | +} |
| 2081 | + |
| 2082 | +/* |
| 2083 | + * Fast path: if no relation has triggers, none has FKs either. |
| 2084 | + */ |
| 2085 | +if (oids==NIL) |
2053 | 2086 | return;
|
2054 | 2087 |
|
2055 | 2088 | /*
|
2056 |
| - * Otherwise, must scan pg_constraint.Right now,this is a seqscan |
| 2089 | + * Otherwise, must scan pg_constraint.Right now,it is a seqscan |
2057 | 2090 | * because there is no available index on confrelid.
|
2058 | 2091 | */
|
2059 | 2092 | fkeyRel=heap_openr(ConstraintRelationName,AccessShareLock);
|
2060 | 2093 |
|
2061 |
| -ScanKeyInit(&key, |
2062 |
| -Anum_pg_constraint_confrelid, |
2063 |
| -BTEqualStrategyNumber,F_OIDEQ, |
2064 |
| -ObjectIdGetDatum(relid)); |
2065 |
| - |
2066 | 2094 | fkeyScan=systable_beginscan(fkeyRel,NULL, false,
|
2067 |
| -SnapshotNow,1,&key); |
| 2095 | +SnapshotNow,0,NULL); |
2068 | 2096 |
|
2069 | 2097 | while (HeapTupleIsValid(tuple=systable_getnext(fkeyScan)))
|
2070 | 2098 | {
|
2071 | 2099 | Form_pg_constraintcon= (Form_pg_constraint)GETSTRUCT(tuple);
|
2072 | 2100 |
|
2073 |
| -if (con->contype==CONSTRAINT_FOREIGN&&con->conrelid!=relid) |
2074 |
| -ereport(ERROR, |
2075 |
| -(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2076 |
| -errmsg("cannot truncate a table referenced in a foreign key constraint"), |
2077 |
| -errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", |
2078 |
| -get_rel_name(con->conrelid), |
2079 |
| -RelationGetRelationName(rel), |
2080 |
| -NameStr(con->conname)))); |
| 2101 | +/* Not a foreign key */ |
| 2102 | +if (con->contype!=CONSTRAINT_FOREIGN) |
| 2103 | +continue; |
| 2104 | + |
| 2105 | +/* Not for one of our list of tables */ |
| 2106 | +if (!list_member_oid(oids,con->confrelid)) |
| 2107 | +continue; |
| 2108 | + |
| 2109 | +/* The referencer should be in our list too */ |
| 2110 | +if (!list_member_oid(oids,con->conrelid)) |
| 2111 | +{ |
| 2112 | +if (tempTables) |
| 2113 | +ereport(ERROR, |
| 2114 | +(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 2115 | +errmsg("unsupported ON COMMIT and foreign key combination"), |
| 2116 | +errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\", but they do not have the same ON COMMIT setting.", |
| 2117 | +get_rel_name(con->conrelid), |
| 2118 | +get_rel_name(con->confrelid), |
| 2119 | +NameStr(con->conname)))); |
| 2120 | +else |
| 2121 | +ereport(ERROR, |
| 2122 | +(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 2123 | +errmsg("cannot truncate a table referenced in a foreign key constraint"), |
| 2124 | +errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", |
| 2125 | +get_rel_name(con->conrelid), |
| 2126 | +get_rel_name(con->confrelid), |
| 2127 | +NameStr(con->conname)), |
| 2128 | +errhint("Truncate table \"%s\" at the same time.", |
| 2129 | +get_rel_name(con->conrelid)))); |
| 2130 | +} |
2081 | 2131 | }
|
2082 | 2132 |
|
2083 | 2133 | systable_endscan(fkeyScan);
|
|