aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/index.c13
-rw-r--r--src/backend/commands/cluster.c251
-rw-r--r--src/backend/commands/indexcmds.c6
-rw-r--r--src/backend/commands/tablecmds.c142
-rw-r--r--src/include/catalog/index.h4
-rw-r--r--src/include/commands/cluster.h7
6 files changed, 161 insertions, 262 deletions
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 5a8d5b22671..6a994aa0ccc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.229 2004/05/05 04:48:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.230 2004/05/08 00:34:49 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1729,12 +1729,13 @@ reindex_index(Oid indexId)
/*
* reindex_relation - This routine is used to recreate all indexes
- * of a relation (and its toast relation too, if any).
+ * of a relation (and optionally its toast relation too, if any).
*
- * Returns true if any indexes were rebuilt.
+ * Returns true if any indexes were rebuilt. Note that a
+ * CommandCounterIncrement will occur after each index rebuild.
*/
bool
-reindex_relation(Oid relid)
+reindex_relation(Oid relid, bool toast_too)
{
Relation rel;
Oid toast_relid;
@@ -1810,8 +1811,8 @@ reindex_relation(Oid relid)
* If the relation has a secondary toast rel, reindex that too while we
* still hold the lock on the master table.
*/
- if (toast_relid != InvalidOid)
- result |= reindex_relation(toast_relid);
+ if (toast_too && OidIsValid(toast_relid))
+ result |= reindex_relation(toast_relid, false);
return result;
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ffd2aff2364..168ae749fa0 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.122 2004/05/06 16:10:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.123 2004/05/08 00:34:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,20 +38,6 @@
/*
- * We need one of these structs for each index in the relation to be
- * clustered. It's basically the data needed by index_create() so
- * we can rebuild the indexes on the new heap.
- */
-typedef struct
-{
- Oid indexOID;
- char *indexName;
- IndexInfo *indexInfo;
- Oid accessMethodOID;
- Oid *classOID;
-} IndexAttrs;
-
-/*
* This struct is used to pass around the information on tables to be
* clustered. We need this so we can make a list of them when invoked without
* a specific table/index pair.
@@ -64,6 +50,7 @@ typedef struct
static void cluster_rel(RelToCluster *rv, bool recheck);
+static void rebuild_relation(Relation OldHeap, Oid indexOid);
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
static List *get_tables_to_cluster(MemoryContext cluster_context);
@@ -411,30 +398,99 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid)
}
/*
- * rebuild_relation: rebuild an existing relation
+ * mark_index_clustered: mark the specified index as the one clustered on
*
- * This is shared code between CLUSTER and TRUNCATE. In the TRUNCATE
- * case, the new relation is built and left empty. In the CLUSTER case,
- * it is filled with data read from the old relation in the order specified
- * by the index.
+ * With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
+ */
+void
+mark_index_clustered(Relation rel, Oid indexOid)
+{
+ HeapTuple indexTuple;
+ Form_pg_index indexForm;
+ Relation pg_index;
+ List *index;
+
+ /*
+ * If the index is already marked clustered, no need to do anything.
+ */
+ if (OidIsValid(indexOid))
+ {
+ indexTuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "cache lookup failed for index %u", indexOid);
+ indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
+
+ if (indexForm->indisclustered)
+ {
+ ReleaseSysCache(indexTuple);
+ return;
+ }
+
+ ReleaseSysCache(indexTuple);
+ }
+
+ /*
+ * Check each index of the relation and set/clear the bit as needed.
+ */
+ pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
+
+ foreach(index, RelationGetIndexList(rel))
+ {
+ Oid thisIndexOid = lfirsto(index);
+
+ indexTuple = SearchSysCacheCopy(INDEXRELID,
+ ObjectIdGetDatum(thisIndexOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
+ indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
+
+ /*
+ * Unset the bit if set. We know it's wrong because we checked
+ * this earlier.
+ */
+ if (indexForm->indisclustered)
+ {
+ indexForm->indisclustered = false;
+ simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
+ CatalogUpdateIndexes(pg_index, indexTuple);
+ /* Ensure we see the update in the index's relcache entry */
+ CacheInvalidateRelcacheByRelid(thisIndexOid);
+ }
+ else if (thisIndexOid == indexOid)
+ {
+ indexForm->indisclustered = true;
+ simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
+ CatalogUpdateIndexes(pg_index, indexTuple);
+ /* Ensure we see the update in the index's relcache entry */
+ CacheInvalidateRelcacheByRelid(thisIndexOid);
+ }
+ heap_freetuple(indexTuple);
+ }
+
+ heap_close(pg_index, RowExclusiveLock);
+}
+
+/*
+ * rebuild_relation: rebuild an existing relation in index order
*
* OldHeap: table to rebuild --- must be opened and exclusive-locked!
- * indexOid: index to cluster by, or InvalidOid in TRUNCATE case
+ * indexOid: index to cluster by
*
* NB: this routine closes OldHeap at the right time; caller should not.
*/
-void
+static void
rebuild_relation(Relation OldHeap, Oid indexOid)
{
Oid tableOid = RelationGetRelid(OldHeap);
- Oid oldClusterIndex;
- List *indexes;
Oid OIDNewHeap;
char NewHeapName[NAMEDATALEN];
ObjectAddress object;
- /* Save the information about all indexes on the relation. */
- indexes = get_indexattr_list(OldHeap, &oldClusterIndex);
+ /* Mark the correct index as clustered */
+ mark_index_clustered(OldHeap, indexOid);
/* Close relcache entry, but keep lock until transaction commit */
heap_close(OldHeap, NoLock);
@@ -459,8 +515,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/*
* Copy the heap data into the new table in the desired order.
*/
- if (OidIsValid(indexOid))
- copy_heap_data(OIDNewHeap, tableOid, indexOid);
+ copy_heap_data(OIDNewHeap, tableOid, indexOid);
/* To make the new heap's data visible (probably not needed?). */
CommandCounterIncrement();
@@ -484,11 +539,11 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/* performDeletion does CommandCounterIncrement at end */
/*
- * Recreate each index on the relation. We do not need
- * CommandCounterIncrement() because rebuild_indexes does it.
+ * Rebuild each index on the relation (but not the toast table,
+ * which is all-new at this point). We do not need
+ * CommandCounterIncrement() because reindex_relation does it.
*/
- rebuild_indexes(tableOid, indexes,
- (OidIsValid(indexOid) ? indexOid : oldClusterIndex));
+ reindex_relation(tableOid, false);
}
/*
@@ -590,138 +645,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
}
/*
- * Get the necessary info about the indexes of the relation and
- * return a list of IndexAttrs structures. Also, *OldClusterIndex
- * is set to the OID of the existing clustered index, or InvalidOid
- * if there is none.
- */
-List *
-get_indexattr_list(Relation OldHeap, Oid *OldClusterIndex)
-{
- List *indexes = NIL;
- List *indlist;
-
- *OldClusterIndex = InvalidOid;
-
- /* Ask the relcache to produce a list of the indexes of the old rel */
- foreach(indlist, RelationGetIndexList(OldHeap))
- {
- Oid indexOID = lfirsto(indlist);
- Relation oldIndex;
- IndexAttrs *attrs;
-
- oldIndex = index_open(indexOID);
-
- attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
- attrs->indexOID = indexOID;
- attrs->indexName = pstrdup(NameStr(oldIndex->rd_rel->relname));
- attrs->accessMethodOID = oldIndex->rd_rel->relam;
- attrs->indexInfo = BuildIndexInfo(oldIndex);
- attrs->classOID = (Oid *)
- palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
- memcpy(attrs->classOID, oldIndex->rd_index->indclass,
- sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
- if (oldIndex->rd_index->indisclustered)
- *OldClusterIndex = indexOID;
-
- index_close(oldIndex);
-
- indexes = lappend(indexes, attrs);
- }
-
- return indexes;
-}
-
-/*
- * Create new indexes and swap the filenodes with old indexes. Then drop
- * the new index (carrying the old index filenode along).
- *
- * OIDClusterIndex is the OID of the index to be marked as clustered, or
- * InvalidOid if none should be marked clustered.
- */
-void
-rebuild_indexes(Oid OIDOldHeap, List *indexes, Oid OIDClusterIndex)
-{
- List *elem;
-
- foreach(elem, indexes)
- {
- IndexAttrs *attrs = (IndexAttrs *) lfirst(elem);
- Oid oldIndexOID = attrs->indexOID;
- Oid newIndexOID;
- char newIndexName[NAMEDATALEN];
- bool isclustered;
- ObjectAddress object;
- Form_pg_index index;
- HeapTuple tuple;
- Relation pg_index;
-
- /* Create the new index under a temporary name */
- snprintf(newIndexName, sizeof(newIndexName),
- "pg_temp_%u", oldIndexOID);
-
- /*
- * The new index will have primary and constraint status set to
- * false, but since we will only use its filenode it doesn't
- * matter: after the filenode swap the index will keep the
- * constraint status of the old index.
- */
- newIndexOID = index_create(OIDOldHeap,
- newIndexName,
- attrs->indexInfo,
- attrs->accessMethodOID,
- attrs->classOID,
- false,
- false,
- allowSystemTableMods,
- false);
- CommandCounterIncrement();
-
- /* Swap the filenodes. */
- swap_relfilenodes(oldIndexOID, newIndexOID);
-
- CommandCounterIncrement();
-
- /*
- * Make sure that indisclustered is correct: it should be set only
- * for the index specified by the caller.
- */
- isclustered = (oldIndexOID == OIDClusterIndex);
-
- pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
- tuple = SearchSysCacheCopy(INDEXRELID,
- ObjectIdGetDatum(oldIndexOID),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for index %u", oldIndexOID);
- index = (Form_pg_index) GETSTRUCT(tuple);
- if (index->indisclustered != isclustered)
- {
- index->indisclustered = isclustered;
- simple_heap_update(pg_index, &tuple->t_self, tuple);
- CatalogUpdateIndexes(pg_index, tuple);
- /* Ensure we see the update in the index's relcache entry */
- CacheInvalidateRelcacheByRelid(oldIndexOID);
- }
- heap_freetuple(tuple);
- heap_close(pg_index, RowExclusiveLock);
-
- /* Destroy new index with old filenode */
- object.classId = RelOid_pg_class;
- object.objectId = newIndexOID;
- object.objectSubId = 0;
-
- /*
- * The relation is local to our transaction and we know nothing
- * depends on it, so DROP_RESTRICT should be OK.
- */
- performDeletion(&object, DROP_RESTRICT);
-
- /* performDeletion does CommandCounterIncrement() at its end */
- }
-}
-
-/*
* Swap the relfilenodes for two given relations.
*
* Also swap any TOAST links, so that the toast data moves along with
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index efcc70c6856..448f99e1d37 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.118 2004/05/05 04:48:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.119 2004/05/08 00:34:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -826,7 +826,7 @@ ReindexTable(RangeVar *relation, bool force /* currently unused */ )
ReleaseSysCache(tuple);
- if (!reindex_relation(heapOid))
+ if (!reindex_relation(heapOid, true))
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes",
relation->relname)));
@@ -936,7 +936,7 @@ ReindexDatabase(const char *dbname, bool force /* currently unused */,
StartTransactionCommand();
SetQuerySnapshot(); /* might be needed for functions in
* indexes */
- if (reindex_relation(relid))
+ if (reindex_relation(relid, true))
ereport(NOTICE,
(errmsg("table \"%s\" was reindexed",
get_rel_name(relid))));
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d74e1e7ab31..5adafd630ac 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.105 2004/05/07 00:24:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.106 2004/05/08 00:34:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -490,14 +490,13 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
/*
* TruncateRelation
* Removes all the rows from a relation.
- *
- * Note: This routine only does safety and permissions checks;
- * rebuild_relation in cluster.c does the actual work.
*/
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);
@@ -521,6 +520,16 @@ TruncateRelation(const RangeVar *relation)
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.
*/
@@ -535,17 +544,31 @@ TruncateRelation(const RangeVar *relation)
heap_truncate_check_FKs(rel);
/*
- * Do the real work using the same technique as cluster, but without
- * the data-copying portion
+ * 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.
*/
- rebuild_relation(rel, InvalidOid);
+ setNewRelfilenode(rel);
+
+ heap_relid = RelationGetRelid(rel);
+ toast_relid = rel->rd_rel->reltoastrelid;
+
+ heap_close(rel, NoLock);
- /* NB: rebuild_relation does heap_close() */
+ /*
+ * The same for the toast table, if any.
+ */
+ if (OidIsValid(toast_relid))
+ {
+ rel = relation_open(toast_relid, AccessExclusiveLock);
+ setNewRelfilenode(rel);
+ heap_close(rel, NoLock);
+ }
/*
- * You might think we need to truncate the rel's toast table here too,
- * but actually we don't; it will have been rebuilt in an empty state.
+ * Reconstruct the indexes to match, and we're done.
*/
+ reindex_relation(heap_relid, true);
}
/*----------
@@ -2098,14 +2121,31 @@ ATRewriteTables(List **wqueue)
/* Build a temporary relation and copy data */
Oid OIDNewHeap;
char NewHeapName[NAMEDATALEN];
- List *indexes;
- Oid oldClusterIndex;
Relation OldHeap;
ObjectAddress object;
- /* Save the information about all indexes on the relation. */
OldHeap = heap_open(tab->relid, NoLock);
- indexes = get_indexattr_list(OldHeap, &oldClusterIndex);
+
+ /*
+ * We can never allow rewriting of shared or nailed-in-cache
+ * relations, because we can't support changing their relfilenode
+ * values.
+ */
+ if (OldHeap->rd_rel->relisshared || OldHeap->rd_isnailed)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot rewrite system relation \"%s\"",
+ RelationGetRelationName(OldHeap))));
+
+ /*
+ * Don't allow rewrite on temp tables of other backends ... their
+ * local buffer manager is not going to cope.
+ */
+ if (isOtherTempNamespace(RelationGetNamespace(OldHeap)))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot rewrite temporary tables of other sessions")));
+
heap_close(OldHeap, NoLock);
/*
@@ -2146,10 +2186,11 @@ ATRewriteTables(List **wqueue)
/* performDeletion does CommandCounterIncrement at end */
/*
- * Rebuild each index on the relation. We do not need
- * CommandCounterIncrement() because rebuild_indexes does it.
+ * Rebuild each index on the relation (but not the toast table,
+ * which is all-new anyway). We do not need
+ * CommandCounterIncrement() because reindex_relation does it.
*/
- rebuild_indexes(tab->relid, indexes, oldClusterIndex);
+ reindex_relation(tab->relid, false);
}
else
{
@@ -4991,11 +5032,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
static void
ATExecClusterOn(Relation rel, const char *indexName)
{
- Relation pg_index;
- List *index;
Oid indexOid;
- HeapTuple indexTuple;
- Form_pg_index indexForm;
indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
@@ -5008,67 +5045,8 @@ ATExecClusterOn(Relation rel, const char *indexName)
/* Check index is valid to cluster on */
check_index_is_clusterable(rel, indexOid);
- indexTuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(indexOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "cache lookup failed for index %u", indexOid);
- indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
-
- /*
- * If this is the same index the relation was previously clustered on,
- * no need to do anything.
- */
- if (indexForm->indisclustered)
- {
- ReleaseSysCache(indexTuple);
- return;
- }
-
- ReleaseSysCache(indexTuple);
-
- pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
-
- /*
- * Now check each index in the relation and set the bit where needed.
- */
- foreach(index, RelationGetIndexList(rel))
- {
- HeapTuple idxtuple;
- Form_pg_index idxForm;
-
- indexOid = lfirsto(index);
- idxtuple = SearchSysCacheCopy(INDEXRELID,
- ObjectIdGetDatum(indexOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(idxtuple))
- elog(ERROR, "cache lookup failed for index %u", indexOid);
- idxForm = (Form_pg_index) GETSTRUCT(idxtuple);
-
- /*
- * Unset the bit if set. We know it's wrong because we checked
- * this earlier.
- */
- if (idxForm->indisclustered)
- {
- idxForm->indisclustered = false;
- simple_heap_update(pg_index, &idxtuple->t_self, idxtuple);
- CatalogUpdateIndexes(pg_index, idxtuple);
- /* Ensure we see the update in the index's relcache entry */
- CacheInvalidateRelcacheByRelid(indexOid);
- }
- else if (idxForm->indexrelid == indexForm->indexrelid)
- {
- idxForm->indisclustered = true;
- simple_heap_update(pg_index, &idxtuple->t_self, idxtuple);
- CatalogUpdateIndexes(pg_index, idxtuple);
- /* Ensure we see the update in the index's relcache entry */
- CacheInvalidateRelcacheByRelid(indexOid);
- }
- heap_freetuple(idxtuple);
- }
-
- heap_close(pg_index, RowExclusiveLock);
+ /* And do the work */
+ mark_index_clustered(rel, indexOid);
}
/*
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 13c367c9022..841387effcc 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.55 2004/05/05 04:48:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.56 2004/05/08 00:34:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -68,6 +68,6 @@ extern double IndexBuildHeapScan(Relation heapRelation,
void *callback_state);
extern void reindex_index(Oid indexId);
-extern bool reindex_relation(Oid relid);
+extern bool reindex_relation(Oid relid, bool toast_too);
#endif /* INDEX_H */
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index d97be66d73a..820c9521080 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.22 2004/05/06 16:10:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.23 2004/05/08 00:34:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,11 +20,8 @@
extern void cluster(ClusterStmt *stmt);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid);
-extern void rebuild_relation(Relation OldHeap, Oid indexOid);
+extern void mark_index_clustered(Relation rel, Oid indexOid);
extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
-extern List *get_indexattr_list(Relation OldHeap, Oid *OldClusterIndex);
-extern void rebuild_indexes(Oid OIDOldHeap, List *indexes,
- Oid OIDClusterIndex);
extern void swap_relfilenodes(Oid r1, Oid r2);
#endif /* CLUSTER_H */