1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* cluster.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Paul Brown's implementation of cluster index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* I am going to use the rename function as a model for this in the
|
|
|
|
* parser and executor, and the vacuum code as an example in this
|
|
|
|
* file. As I go - in contrast to the rest of postgres - there will
|
|
|
|
* be BUCKETS of comments. This is to allow reviewers to understand
|
|
|
|
* my (probably bogus) assumptions about the way this works.
|
|
|
|
* [pbrown '94]
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-06-20 22:29:54 +02:00
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-07-12 20:43:19 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1997-01-10 21:19:49 +01:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/genam.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/heapam.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "catalog/dependency.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/heap.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/index.h"
|
2000-06-15 05:33:12 +02:00
|
|
|
#include "catalog/pg_index.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "commands/cluster.h"
|
2002-04-15 07:22:04 +02:00
|
|
|
#include "commands/tablecmds.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
2002-03-29 23:10:34 +01:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-01-10 02:12:28 +01:00
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
static Oid copy_heap(Oid OIDOldHeap, const char *NewName);
|
|
|
|
static Oid copy_index(Oid OIDOldIndex, Oid OIDNewHeap,
|
|
|
|
const char *NewIndexName);
|
1997-09-08 04:41:22 +02:00
|
|
|
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
|
1997-08-19 23:40:56 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* cluster
|
|
|
|
*
|
2000-11-08 23:10:03 +01:00
|
|
|
* STILL TO DO:
|
2001-07-12 22:35:54 +02:00
|
|
|
* Create a list of all the other indexes on this relation. Because
|
1997-09-07 07:04:48 +02:00
|
|
|
* the cluster will wreck all the tids, I'll need to destroy bogus
|
2001-07-12 22:35:54 +02:00
|
|
|
* indexes. The user will have to re-create them. Not nice, but
|
1997-09-07 07:04:48 +02:00
|
|
|
* I'm not a nice guy. The alternative is to try some kind of post
|
|
|
|
* destroy re-build. This may be possible. I'll check out what the
|
|
|
|
* index create functiond want in the way of paramaters. On the other
|
2001-07-12 22:35:54 +02:00
|
|
|
* hand, re-creating n indexes may blow out the space.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2002-03-26 20:17:02 +01:00
|
|
|
cluster(RangeVar *oldrelation, char *oldindexname)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid OIDOldHeap,
|
|
|
|
OIDOldIndex,
|
2002-03-31 09:49:30 +02:00
|
|
|
OIDNewHeap,
|
|
|
|
OIDNewIndex;
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation OldHeap,
|
|
|
|
OldIndex;
|
|
|
|
char NewHeapName[NAMEDATALEN];
|
2000-11-08 23:10:03 +01:00
|
|
|
char NewIndexName[NAMEDATALEN];
|
2002-07-12 20:43:19 +02:00
|
|
|
ObjectAddress object;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We grab exclusive access to the target rel and index for the
|
|
|
|
* duration of the transaction.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-03-29 23:10:34 +01:00
|
|
|
OldHeap = heap_openrv(oldrelation, AccessExclusiveLock);
|
1999-09-18 21:08:25 +02:00
|
|
|
OIDOldHeap = RelationGetRelid(OldHeap);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-29 23:10:34 +01:00
|
|
|
/*
|
|
|
|
* The index is expected to be in the same namespace as the relation.
|
|
|
|
*/
|
|
|
|
OIDOldIndex = get_relname_relid(oldindexname,
|
|
|
|
RelationGetNamespace(OldHeap));
|
|
|
|
if (!OidIsValid(OIDOldIndex))
|
|
|
|
elog(ERROR, "CLUSTER: cannot find index \"%s\" for table \"%s\"",
|
|
|
|
oldindexname, oldrelation->relname);
|
|
|
|
OldIndex = index_open(OIDOldIndex);
|
|
|
|
LockRelation(OldIndex, AccessExclusiveLock);
|
2001-01-10 02:12:28 +01:00
|
|
|
|
2000-05-11 05:54:18 +02:00
|
|
|
/*
|
2000-11-08 23:10:03 +01:00
|
|
|
* Check that index is in fact an index on the given relation
|
2000-05-11 05:54:18 +02:00
|
|
|
*/
|
2002-02-19 21:11:20 +01:00
|
|
|
if (OldIndex->rd_index->indrelid != OIDOldHeap)
|
2000-11-08 23:10:03 +01:00
|
|
|
elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
|
2002-03-29 23:10:34 +01:00
|
|
|
oldindexname, oldrelation->relname);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
|
|
|
/* Drop relcache refcnts, but do NOT give up the locks */
|
|
|
|
heap_close(OldHeap, NoLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
index_close(OldIndex);
|
|
|
|
|
|
|
|
/*
|
2000-11-08 23:10:03 +01:00
|
|
|
* Create the new heap with a temporary name.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-11-08 23:10:03 +01:00
|
|
|
snprintf(NewHeapName, NAMEDATALEN, "temp_%u", OIDOldHeap);
|
|
|
|
|
2002-03-31 08:26:32 +02:00
|
|
|
OIDNewHeap = copy_heap(OIDOldHeap, NewHeapName);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-01 22:35:00 +01:00
|
|
|
/* We do not need CommandCounterIncrement() because copy_heap did it. */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
/*
|
|
|
|
* Copy the heap data into the new table in the desired order.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
|
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
/* To make the new heap's data visible. */
|
1997-09-07 07:04:48 +02:00
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/* Create new index over the tuples of the new heap. */
|
2000-11-08 23:10:03 +01:00
|
|
|
snprintf(NewIndexName, NAMEDATALEN, "temp_%u", OIDOldIndex);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
OIDNewIndex = copy_index(OIDOldIndex, OIDNewHeap, NewIndexName);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
|
|
|
CommandCounterIncrement();
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* Destroy old heap (along with its index) and rename new. */
|
2002-07-12 20:43:19 +02:00
|
|
|
object.classId = RelOid_pg_class;
|
|
|
|
object.objectId = OIDOldHeap;
|
|
|
|
object.objectSubId = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
/* XXX better to use DROP_CASCADE here? */
|
|
|
|
performDeletion(&object, DROP_RESTRICT);
|
|
|
|
|
|
|
|
/* performDeletion does CommandCounterIncrement at end */
|
1998-01-10 06:19:22 +01:00
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
renamerel(OIDNewHeap, oldrelation->relname);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
|
|
|
/* This one might be unnecessary, but let's be safe. */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
renamerel(OIDNewIndex, oldindexname);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
static Oid
|
2002-03-31 09:49:30 +02:00
|
|
|
copy_heap(Oid OIDOldHeap, const char *NewName)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc OldHeapDesc,
|
|
|
|
tupdesc;
|
|
|
|
Oid OIDNewHeap;
|
2000-11-08 23:10:03 +01:00
|
|
|
Relation OldHeap;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
OldHeapDesc = RelationGetDescr(OldHeap);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Need to make a copy of the tuple descriptor, since
|
|
|
|
* heap_create_with_catalog modifies it.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2001-01-12 02:22:21 +01:00
|
|
|
tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
OIDNewHeap = heap_create_with_catalog(NewName,
|
|
|
|
RelationGetNamespace(OldHeap),
|
|
|
|
tupdesc,
|
2001-08-10 20:57:42 +02:00
|
|
|
OldHeap->rd_rel->relkind,
|
2002-04-27 23:24:34 +02:00
|
|
|
OldHeap->rd_rel->relisshared,
|
2001-08-10 20:57:42 +02:00
|
|
|
OldHeap->rd_rel->relhasoids,
|
2000-07-04 08:11:54 +02:00
|
|
|
allowSystemTableMods);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-01 22:35:00 +01:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Advance command counter so that the newly-created relation's
|
|
|
|
* catalog tuples will be visible to heap_open.
|
2001-01-01 22:35:00 +01:00
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* If necessary, create a TOAST table for the new relation. Note that
|
|
|
|
* AlterTableCreateToastTable ends with CommandCounterIncrement(), so
|
|
|
|
* that the TOAST table will be visible for insertion.
|
2001-01-01 22:35:00 +01:00
|
|
|
*/
|
2002-03-26 20:17:02 +01:00
|
|
|
AlterTableCreateToastTable(OIDNewHeap, true);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
heap_close(OldHeap, NoLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
return OIDNewHeap;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
static Oid
|
|
|
|
copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-03-31 09:49:30 +02:00
|
|
|
Oid OIDNewIndex;
|
1999-05-25 18:15:34 +02:00
|
|
|
Relation OldIndex,
|
|
|
|
NewHeap;
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexInfo *indexInfo;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
OldIndex = index_open(OIDOldIndex);
|
|
|
|
|
|
|
|
/*
|
2001-01-10 02:12:28 +01:00
|
|
|
* Create a new index like the old one. To do this I get the info
|
2001-03-22 05:01:46 +01:00
|
|
|
* from pg_index, and add a new index with a temporary name (that will
|
|
|
|
* be changed later).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-02-19 21:11:20 +01:00
|
|
|
indexInfo = BuildIndexInfo(OldIndex->rd_index);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-31 09:49:30 +02:00
|
|
|
OIDNewIndex = index_create(OIDNewHeap,
|
|
|
|
NewIndexName,
|
|
|
|
indexInfo,
|
|
|
|
OldIndex->rd_rel->relam,
|
|
|
|
OldIndex->rd_index->indclass,
|
|
|
|
OldIndex->rd_index->indisprimary,
|
2002-07-12 20:43:19 +02:00
|
|
|
false, /* XXX losing constraint status */
|
2002-03-31 09:49:30 +02:00
|
|
|
allowSystemTableMods);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
setRelhasindex(OIDNewHeap, true,
|
2002-02-19 21:11:20 +01:00
|
|
|
OldIndex->rd_index->indisprimary, InvalidOid);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
index_close(OldIndex);
|
2000-11-08 23:10:03 +01:00
|
|
|
heap_close(NewHeap, NoLock);
|
2002-03-31 09:49:30 +02:00
|
|
|
|
|
|
|
return OIDNewIndex;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
static void
|
1996-07-09 08:22:35 +02:00
|
|
|
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
Relation LocalNewHeap,
|
|
|
|
LocalOldHeap,
|
|
|
|
LocalOldIndex;
|
|
|
|
IndexScanDesc ScanDesc;
|
2002-05-21 01:51:44 +02:00
|
|
|
HeapTuple LocalHeapTuple;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the relations I need. Scan through the OldHeap on the OldIndex
|
|
|
|
* and insert each tuple into the NewHeap.
|
|
|
|
*/
|
1999-09-18 21:08:25 +02:00
|
|
|
LocalNewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
|
|
|
LocalOldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
2000-05-11 05:54:18 +02:00
|
|
|
LocalOldIndex = index_open(OIDOldIndex);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
ScanDesc = index_beginscan(LocalOldHeap, LocalOldIndex,
|
|
|
|
SnapshotNow, 0, (ScanKey) NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
while ((LocalHeapTuple = index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-05-21 01:51:44 +02:00
|
|
|
/*
|
|
|
|
* We must copy the tuple because heap_insert() will overwrite
|
|
|
|
* the commit-status fields of the tuple it's handed, and the
|
|
|
|
* retrieved tuple will actually be in a disk buffer! Thus,
|
|
|
|
* the source relation would get trashed, which is bad news if
|
|
|
|
* we abort later on. (This was a bug in releases thru 7.0)
|
|
|
|
*/
|
|
|
|
HeapTuple copiedTuple = heap_copytuple(LocalHeapTuple);
|
|
|
|
|
2002-05-22 00:05:55 +02:00
|
|
|
simple_heap_insert(LocalNewHeap, copiedTuple);
|
2002-05-21 01:51:44 +02:00
|
|
|
heap_freetuple(copiedTuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-01-06 01:37:44 +01:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-05-11 05:54:18 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
index_endscan(ScanDesc);
|
|
|
|
|
|
|
|
index_close(LocalOldIndex);
|
2000-11-08 23:10:03 +01:00
|
|
|
heap_close(LocalOldHeap, NoLock);
|
|
|
|
heap_close(LocalNewHeap, NoLock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|