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
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, 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-03-29 20:06:29 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.74 2002/03/29 19:06:03 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"
|
|
|
|
#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"
|
2001-01-01 22:35:00 +01:00
|
|
|
#include "commands/command.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "commands/rename.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/syscache.h"
|
2001-01-10 02:12:28 +01:00
|
|
|
#include "utils/temprel.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-01-10 02:12:28 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static Oid copy_heap(Oid OIDOldHeap, char *NewName, bool istemp);
|
2002-03-26 20:17:02 +01:00
|
|
|
static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName,
|
|
|
|
bool istemp);
|
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,
|
|
|
|
OIDNewHeap;
|
|
|
|
Relation OldHeap,
|
|
|
|
OldIndex;
|
2001-01-10 02:12:28 +01:00
|
|
|
bool istemp;
|
1997-09-08 04:41:22 +02:00
|
|
|
char NewHeapName[NAMEDATALEN];
|
2000-11-08 23:10:03 +01:00
|
|
|
char NewIndexName[NAMEDATALEN];
|
2002-03-26 20:17:02 +01:00
|
|
|
RangeVar *saveoldrelation;
|
|
|
|
RangeVar *saveoldindex;
|
|
|
|
RangeVar *NewHeap;
|
|
|
|
RangeVar *NewIndex;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2002-03-26 20:17:02 +01:00
|
|
|
* FIXME SCHEMAS: The old code had the comment:
|
|
|
|
* "Copy the arguments into local storage, just to be safe."
|
|
|
|
* By using copyObject we are not using local storage.
|
|
|
|
* Was that really necessary?
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-03-26 20:17:02 +01:00
|
|
|
saveoldrelation = copyObject(oldrelation);
|
|
|
|
saveoldindex = copyObject(oldrelation);
|
|
|
|
saveoldindex->relname = pstrdup(oldindexname);
|
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-26 20:17:02 +01:00
|
|
|
OldHeap = heap_openrv(saveoldrelation, AccessExclusiveLock);
|
1999-09-18 21:08:25 +02:00
|
|
|
OIDOldHeap = RelationGetRelid(OldHeap);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
OldIndex = index_openrv(saveoldindex);
|
1999-09-18 21:08:25 +02:00
|
|
|
LockRelation(OldIndex, AccessExclusiveLock);
|
|
|
|
OIDOldIndex = RelationGetRelid(OldIndex);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
istemp = is_temp_rel_name(saveoldrelation->relname);
|
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-26 20:17:02 +01:00
|
|
|
saveoldindex->relname, saveoldrelation->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);
|
|
|
|
|
2001-01-10 02:12:28 +01:00
|
|
|
OIDNewHeap = copy_heap(OIDOldHeap, NewHeapName, istemp);
|
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-26 20:17:02 +01:00
|
|
|
copy_index(OIDOldIndex, OIDNewHeap, NewIndexName, istemp);
|
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-03-29 20:06:29 +01:00
|
|
|
heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
CommandCounterIncrement();
|
1998-01-10 06:19:22 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
NewHeap = copyObject(saveoldrelation);
|
|
|
|
NewHeap->relname = NewHeapName;
|
|
|
|
NewIndex = copyObject(saveoldindex);
|
|
|
|
NewIndex->relname = NewIndexName;
|
|
|
|
|
|
|
|
renamerel(NewHeap, saveoldrelation->relname);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
|
|
|
/* This one might be unnecessary, but let's be safe. */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
renamerel(NewIndex, saveoldindex->relname);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
static Oid
|
2001-01-10 02:12:28 +01:00
|
|
|
copy_heap(Oid OIDOldHeap, char *NewName, bool istemp)
|
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,
|
|
|
|
OldHeap->rd_rel->relhasoids,
|
|
|
|
istemp,
|
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
|
|
|
}
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
static void
|
2002-03-26 20:17:02 +01:00
|
|
|
copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName, bool istemp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
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-26 20:17:02 +01:00
|
|
|
index_create(OIDNewHeap,
|
1997-09-07 07:04:48 +02:00
|
|
|
NewIndexName,
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo,
|
2002-02-19 21:11:20 +01:00
|
|
|
OldIndex->rd_rel->relam,
|
|
|
|
OldIndex->rd_index->indclass,
|
2002-03-26 20:17:02 +01:00
|
|
|
istemp,
|
2002-02-19 21:11:20 +01:00
|
|
|
OldIndex->rd_index->indisprimary,
|
2000-07-04 08:11:54 +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);
|
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;
|
|
|
|
RetrieveIndexResult ScanResult;
|
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
|
|
|
|
|
|
|
ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
|
|
|
|
|
1998-08-21 00:24:11 +02:00
|
|
|
while ((ScanResult = index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-05-11 05:54:18 +02:00
|
|
|
HeapTupleData LocalHeapTuple;
|
|
|
|
Buffer LocalBuffer;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-01-06 01:37:44 +01:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
1998-11-27 20:52:36 +01:00
|
|
|
LocalHeapTuple.t_self = ScanResult->heap_iptr;
|
1999-12-16 23:20:03 +01:00
|
|
|
LocalHeapTuple.t_datamcxt = NULL;
|
|
|
|
LocalHeapTuple.t_data = NULL;
|
2001-06-22 21:16:24 +02:00
|
|
|
heap_fetch(LocalOldHeap, SnapshotNow, &LocalHeapTuple, &LocalBuffer,
|
2001-10-25 07:50:21 +02:00
|
|
|
ScanDesc);
|
2001-03-22 05:01:46 +01:00
|
|
|
if (LocalHeapTuple.t_data != NULL)
|
|
|
|
{
|
2000-05-12 18:10:09 +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,
|
2001-03-22 05:01:46 +01: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 18:10:09 +02:00
|
|
|
*/
|
|
|
|
HeapTuple copiedTuple = heap_copytuple(&LocalHeapTuple);
|
|
|
|
|
|
|
|
ReleaseBuffer(LocalBuffer);
|
|
|
|
heap_insert(LocalNewHeap, copiedTuple);
|
|
|
|
heap_freetuple(copiedTuple);
|
|
|
|
}
|
2000-05-11 05:54:18 +02:00
|
|
|
pfree(ScanResult);
|
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
|
|
|
}
|