|
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 specified relation. |
| 1988 | + * This routine deletes all data within all the specified relations. |
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(Oid rid) |
| 1995 | +heap_truncate(List *relids) |
1996 | 1996 | {
|
1997 |
| - Relation rel; |
1998 |
| - Oid toastrelid; |
| 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 | + Oid rid = lfirst_oid(cell); |
| 2004 | + Relation rel; |
| 2005 | + Oid toastrelid; |
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 | + Relation rel = 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 the indexes too */ |
2016 |
| - RelationTruncateIndexes(rid); |
| 2033 | + /* Now truncate the actual 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(Relation rel) |
| 2060 | +heap_truncate_check_FKs(List *relations, bool tempTables) |
2041 | 2061 | {
|
2042 |
| - Oid relid = RelationGetRelid(rel); |
2043 |
| - ScanKeyData key; |
| 2062 | + List *oids = NIL; |
| 2063 | + ListCell *cell; |
2044 | 2064 | Relation fkeyRel;
|
2045 | 2065 | SysScanDesc fkeyScan;
|
2046 | 2066 | HeapTuple tuple;
|
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 | + Relation rel = 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_constraint con = (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);
|
|
0 commit comments