1996-07-09 06:22:35 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* cluster.c
|
1997-09-07 05:04:48 +00:00
|
|
|
* Paul Brown's implementation of cluster index.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
1997-09-07 05:04:48 +00: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 06:22:35 +00:00
|
|
|
*
|
2001-01-24 19:43:33 +00:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 05:58:53 +00:00
|
|
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-04-15 05:22:04 +00:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.78 2002/04/15 05:22:03 tgl Exp $
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1997-01-10 20:19:49 +00:00
|
|
|
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "postgres.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "access/genam.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/heap.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "catalog/index.h"
|
2000-06-15 03:33:12 +00:00
|
|
|
#include "catalog/pg_index.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "catalog/pg_proc.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "commands/cluster.h"
|
2002-04-15 05:22:04 +00:00
|
|
|
#include "commands/tablecmds.h"
|
2000-07-04 06:11:54 +00:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "utils/builtins.h"
|
2002-03-29 22:10:34 +00:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "utils/syscache.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
|
2001-01-10 01:12:28 +00:00
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
static Oid copy_heap(Oid OIDOldHeap, const char *NewName);
|
|
|
|
static Oid copy_index(Oid OIDOldIndex, Oid OIDNewHeap,
|
|
|
|
const char *NewIndexName);
|
1997-09-08 02:41:22 +00:00
|
|
|
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
|
1997-08-19 21:40:56 +00:00
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
/*
|
|
|
|
* cluster
|
|
|
|
*
|
2000-11-08 22:10:03 +00:00
|
|
|
* STILL TO DO:
|
2001-07-12 20:35:54 +00:00
|
|
|
* Create a list of all the other indexes on this relation. Because
|
1997-09-07 05:04:48 +00:00
|
|
|
* the cluster will wreck all the tids, I'll need to destroy bogus
|
2001-07-12 20:35:54 +00:00
|
|
|
* indexes. The user will have to re-create them. Not nice, but
|
1997-09-07 05:04:48 +00: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 20:35:54 +00:00
|
|
|
* hand, re-creating n indexes may blow out the space.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
void
|
2002-03-26 19:17:02 +00:00
|
|
|
cluster(RangeVar *oldrelation, char *oldindexname)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Oid OIDOldHeap,
|
|
|
|
OIDOldIndex,
|
2002-03-31 07:49:30 +00:00
|
|
|
OIDNewHeap,
|
|
|
|
OIDNewIndex;
|
1997-09-08 02:41:22 +00:00
|
|
|
Relation OldHeap,
|
|
|
|
OldIndex;
|
|
|
|
char NewHeapName[NAMEDATALEN];
|
2000-11-08 22:10:03 +00:00
|
|
|
char NewIndexName[NAMEDATALEN];
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
2001-03-22 04:01:46 +00:00
|
|
|
* We grab exclusive access to the target rel and index for the
|
|
|
|
* duration of the transaction.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2002-03-29 22:10:34 +00:00
|
|
|
OldHeap = heap_openrv(oldrelation, AccessExclusiveLock);
|
1999-09-18 19:08:25 +00:00
|
|
|
OIDOldHeap = RelationGetRelid(OldHeap);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 22:10:34 +00: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 01:12:28 +00:00
|
|
|
|
2000-05-11 03:54:18 +00:00
|
|
|
/*
|
2000-11-08 22:10:03 +00:00
|
|
|
* Check that index is in fact an index on the given relation
|
2000-05-11 03:54:18 +00:00
|
|
|
*/
|
2002-02-19 20:11:20 +00:00
|
|
|
if (OldIndex->rd_index->indrelid != OIDOldHeap)
|
2000-11-08 22:10:03 +00:00
|
|
|
elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
|
2002-03-29 22:10:34 +00:00
|
|
|
oldindexname, oldrelation->relname);
|
2000-11-08 22:10:03 +00:00
|
|
|
|
|
|
|
/* Drop relcache refcnts, but do NOT give up the locks */
|
|
|
|
heap_close(OldHeap, NoLock);
|
1997-09-07 05:04:48 +00:00
|
|
|
index_close(OldIndex);
|
|
|
|
|
|
|
|
/*
|
2000-11-08 22:10:03 +00:00
|
|
|
* Create the new heap with a temporary name.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2000-11-08 22:10:03 +00:00
|
|
|
snprintf(NewHeapName, NAMEDATALEN, "temp_%u", OIDOldHeap);
|
|
|
|
|
2002-03-31 06:26:32 +00:00
|
|
|
OIDNewHeap = copy_heap(OIDOldHeap, NewHeapName);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2001-01-01 21:35:00 +00:00
|
|
|
/* We do not need CommandCounterIncrement() because copy_heap did it. */
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
/*
|
|
|
|
* Copy the heap data into the new table in the desired order.
|
|
|
|
*/
|
1997-09-07 05:04:48 +00:00
|
|
|
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
|
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
/* To make the new heap's data visible. */
|
1997-09-07 05:04:48 +00:00
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/* Create new index over the tuples of the new heap. */
|
2000-11-08 22:10:03 +00:00
|
|
|
snprintf(NewIndexName, NAMEDATALEN, "temp_%u", OIDOldIndex);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
OIDNewIndex = copy_index(OIDOldIndex, OIDNewHeap, NewIndexName);
|
2000-11-08 22:10:03 +00:00
|
|
|
|
|
|
|
CommandCounterIncrement();
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/* Destroy old heap (along with its index) and rename new. */
|
2002-03-29 19:06:29 +00:00
|
|
|
heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
CommandCounterIncrement();
|
1998-01-10 05:19:22 +00:00
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
renamerel(OIDNewHeap, oldrelation->relname);
|
2000-11-08 22:10:03 +00:00
|
|
|
|
|
|
|
/* This one might be unnecessary, but let's be safe. */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
renamerel(OIDNewIndex, oldindexname);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
static Oid
|
2002-03-31 07:49:30 +00:00
|
|
|
copy_heap(Oid OIDOldHeap, const char *NewName)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
TupleDesc OldHeapDesc,
|
|
|
|
tupdesc;
|
|
|
|
Oid OIDNewHeap;
|
2000-11-08 22:10:03 +00:00
|
|
|
Relation OldHeap;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-09-18 19:08:25 +00:00
|
|
|
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
1998-09-01 03:29:17 +00:00
|
|
|
OldHeapDesc = RelationGetDescr(OldHeap);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
2001-03-22 04:01:46 +00:00
|
|
|
* Need to make a copy of the tuple descriptor, since
|
|
|
|
* heap_create_with_catalog modifies it.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2001-01-12 01:22:21 +00:00
|
|
|
tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-26 19:17:02 +00:00
|
|
|
OIDNewHeap = heap_create_with_catalog(NewName,
|
|
|
|
RelationGetNamespace(OldHeap),
|
|
|
|
tupdesc,
|
2001-08-10 18:57:42 +00:00
|
|
|
OldHeap->rd_rel->relkind,
|
|
|
|
OldHeap->rd_rel->relhasoids,
|
2000-07-04 06:11:54 +00:00
|
|
|
allowSystemTableMods);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2001-01-01 21:35:00 +00:00
|
|
|
/*
|
2001-03-22 04:01:46 +00:00
|
|
|
* Advance command counter so that the newly-created relation's
|
|
|
|
* catalog tuples will be visible to heap_open.
|
2001-01-01 21:35:00 +00:00
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/*
|
2001-03-22 04:01:46 +00: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 21:35:00 +00:00
|
|
|
*/
|
2002-03-26 19:17:02 +00:00
|
|
|
AlterTableCreateToastTable(OIDNewHeap, true);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
heap_close(OldHeap, NoLock);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-11-08 22:10:03 +00:00
|
|
|
return OIDNewHeap;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
static Oid
|
|
|
|
copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2002-03-31 07:49:30 +00:00
|
|
|
Oid OIDNewIndex;
|
1999-05-25 16:15:34 +00:00
|
|
|
Relation OldIndex,
|
|
|
|
NewHeap;
|
2000-07-14 22:18:02 +00:00
|
|
|
IndexInfo *indexInfo;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-09-18 19:08:25 +00:00
|
|
|
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
1997-09-07 05:04:48 +00:00
|
|
|
OldIndex = index_open(OIDOldIndex);
|
|
|
|
|
|
|
|
/*
|
2001-01-10 01:12:28 +00:00
|
|
|
* Create a new index like the old one. To do this I get the info
|
2001-03-22 04:01:46 +00:00
|
|
|
* from pg_index, and add a new index with a temporary name (that will
|
|
|
|
* be changed later).
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2002-02-19 20:11:20 +00:00
|
|
|
indexInfo = BuildIndexInfo(OldIndex->rd_index);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-31 07:49:30 +00:00
|
|
|
OIDNewIndex = index_create(OIDNewHeap,
|
|
|
|
NewIndexName,
|
|
|
|
indexInfo,
|
|
|
|
OldIndex->rd_rel->relam,
|
|
|
|
OldIndex->rd_index->indclass,
|
|
|
|
OldIndex->rd_index->indisprimary,
|
|
|
|
allowSystemTableMods);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2001-08-10 18:57:42 +00:00
|
|
|
setRelhasindex(OIDNewHeap, true,
|
2002-02-19 20:11:20 +00:00
|
|
|
OldIndex->rd_index->indisprimary, InvalidOid);
|
2000-11-16 22:30:52 +00:00
|
|
|
|
1999-09-18 19:08:25 +00:00
|
|
|
index_close(OldIndex);
|
2000-11-08 22:10:03 +00:00
|
|
|
heap_close(NewHeap, NoLock);
|
2002-03-31 07:49:30 +00:00
|
|
|
|
|
|
|
return OIDNewIndex;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-08-19 21:40:56 +00:00
|
|
|
static void
|
1996-07-09 06:22:35 +00:00
|
|
|
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
|
|
|
{
|
1999-05-25 16:15:34 +00:00
|
|
|
Relation LocalNewHeap,
|
|
|
|
LocalOldHeap,
|
|
|
|
LocalOldIndex;
|
|
|
|
IndexScanDesc ScanDesc;
|
|
|
|
RetrieveIndexResult ScanResult;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the relations I need. Scan through the OldHeap on the OldIndex
|
|
|
|
* and insert each tuple into the NewHeap.
|
|
|
|
*/
|
1999-09-18 19:08:25 +00:00
|
|
|
LocalNewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
|
|
|
LocalOldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
2000-05-11 03:54:18 +00:00
|
|
|
LocalOldIndex = index_open(OIDOldIndex);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
|
|
|
|
|
1998-08-20 22:24:11 +00:00
|
|
|
while ((ScanResult = index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2000-05-11 03:54:18 +00:00
|
|
|
HeapTupleData LocalHeapTuple;
|
|
|
|
Buffer LocalBuffer;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-01-06 00:37:44 +00:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
1998-11-27 19:52:36 +00:00
|
|
|
LocalHeapTuple.t_self = ScanResult->heap_iptr;
|
1999-12-16 22:20:03 +00:00
|
|
|
LocalHeapTuple.t_datamcxt = NULL;
|
|
|
|
LocalHeapTuple.t_data = NULL;
|
2001-06-22 19:16:24 +00:00
|
|
|
heap_fetch(LocalOldHeap, SnapshotNow, &LocalHeapTuple, &LocalBuffer,
|
2001-10-25 05:50:21 +00:00
|
|
|
ScanDesc);
|
2001-03-22 04:01:46 +00:00
|
|
|
if (LocalHeapTuple.t_data != NULL)
|
|
|
|
{
|
2000-05-12 16:10:09 +00: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,
|
2001-03-22 04:01:46 +00:00
|
|
|
* the source relation would get trashed, which is bad news if
|
|
|
|
* we abort later on. (This was a bug in releases thru 7.0)
|
2000-05-12 16:10:09 +00:00
|
|
|
*/
|
|
|
|
HeapTuple copiedTuple = heap_copytuple(&LocalHeapTuple);
|
|
|
|
|
|
|
|
ReleaseBuffer(LocalBuffer);
|
|
|
|
heap_insert(LocalNewHeap, copiedTuple);
|
|
|
|
heap_freetuple(copiedTuple);
|
|
|
|
}
|
2000-05-11 03:54:18 +00:00
|
|
|
pfree(ScanResult);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2000-05-11 03:54:18 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
index_endscan(ScanDesc);
|
|
|
|
|
|
|
|
index_close(LocalOldIndex);
|
2000-11-08 22:10:03 +00:00
|
|
|
heap_close(LocalOldHeap, NoLock);
|
|
|
|
heap_close(LocalNewHeap, NoLock);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|