|
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); |
|