1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* cluster.c
|
2002-08-11 23:17:35 +02:00
|
|
|
* CLUSTER a table on an index.
|
|
|
|
*
|
|
|
|
* There is hardly anything left of Paul Brown's original implementation...
|
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-11-11 23:19:25 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.93 2002/11/11 22:19:21 tgl Exp $
|
1996-07-09 08:22:35 +02: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-08-11 23:17:35 +02:00
|
|
|
#include "catalog/catalog.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"
|
2002-08-10 22:43:46 +02:00
|
|
|
#include "catalog/indexing.h"
|
|
|
|
#include "catalog/catname.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"
|
2002-08-10 22:43:46 +02:00
|
|
|
#include "utils/fmgroids.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"
|
2002-08-10 22:43:46 +02:00
|
|
|
#include "utils/relcache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/*
|
|
|
|
* 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
|
2002-08-11 23:17:35 +02:00
|
|
|
* we can rebuild the indexes on the new heap.
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
2002-08-11 23:17:35 +02:00
|
|
|
Oid indexOID;
|
2002-08-10 22:43:46 +02:00
|
|
|
char *indexName;
|
|
|
|
IndexInfo *indexInfo;
|
|
|
|
Oid accessMethodOID;
|
|
|
|
Oid *classOID;
|
2002-08-27 05:38:28 +02:00
|
|
|
bool isclustered;
|
2002-08-10 22:43:46 +02:00
|
|
|
} IndexAttrs;
|
2001-01-10 02:12:28 +01:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
static Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
|
|
|
|
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
|
2002-08-27 05:38:28 +02:00
|
|
|
static List *get_indexattr_list(Relation OldHeap, Oid OldIndex);
|
2002-08-10 22:43:46 +02:00
|
|
|
static void recreate_indexattr(Oid OIDOldHeap, List *indexes);
|
|
|
|
static void swap_relfilenodes(Oid r1, Oid r2);
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* cluster
|
|
|
|
*
|
2002-08-10 22:43:46 +02:00
|
|
|
* This clusters the table by creating a new, clustered table and
|
|
|
|
* swapping the relfilenodes of the new table and the old table, so
|
2002-09-04 22:31:48 +02:00
|
|
|
* the OID of the original table is preserved. Thus we do not lose
|
2002-08-10 22:43:46 +02:00
|
|
|
* GRANT, inheritance nor references to this table (this was a bug
|
2002-08-11 23:17:35 +02:00
|
|
|
* in releases thru 7.3).
|
2002-08-10 22:43:46 +02:00
|
|
|
*
|
2002-08-11 23:17:35 +02:00
|
|
|
* Also create new indexes and swap the filenodes with the old indexes the
|
|
|
|
* same way we do for the relation. Since we are effectively bulk-loading
|
|
|
|
* the new table, it's better to create the indexes afterwards than to fill
|
|
|
|
* them incrementally while we load the table.
|
2002-08-10 22:43:46 +02:00
|
|
|
*
|
2002-08-11 23:17:35 +02:00
|
|
|
* Permissions checks were done already.
|
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-08-10 22:43:46 +02:00
|
|
|
OIDNewHeap;
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation OldHeap,
|
|
|
|
OldIndex;
|
|
|
|
char NewHeapName[NAMEDATALEN];
|
2002-07-12 20:43:19 +02:00
|
|
|
ObjectAddress object;
|
2002-08-10 22:43:46 +02:00
|
|
|
List *indexes;
|
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\"",
|
2002-08-11 23:17:35 +02:00
|
|
|
oldindexname, RelationGetRelationName(OldHeap));
|
2002-03-29 23:10:34 +01:00
|
|
|
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-08-11 23:17:35 +02:00
|
|
|
if (OldIndex->rd_index == NULL ||
|
|
|
|
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-08-11 23:17:35 +02:00
|
|
|
RelationGetRelationName(OldIndex),
|
|
|
|
RelationGetRelationName(OldHeap));
|
2000-11-08 23:10:03 +01:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Disallow clustering system relations. This will definitely NOT
|
|
|
|
* work for shared relations (we have no way to update pg_class rows
|
|
|
|
* in other databases), nor for nailed-in-cache relations (the
|
|
|
|
* relfilenode values for those are hardwired, see relcache.c). It
|
|
|
|
* might work for other system relations, but I ain't gonna risk it.
|
2002-08-11 23:17:35 +02:00
|
|
|
*/
|
|
|
|
if (IsSystemRelation(OldHeap))
|
|
|
|
elog(ERROR, "CLUSTER: cannot cluster system relation \"%s\"",
|
|
|
|
RelationGetRelationName(OldHeap));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/* Save the information of all indexes on the relation. */
|
2002-08-27 05:38:28 +02:00
|
|
|
indexes = get_indexattr_list(OldHeap, OIDOldIndex);
|
2002-08-11 23:17:35 +02:00
|
|
|
|
|
|
|
/* Drop relcache refcnts, but do NOT give up the locks */
|
|
|
|
index_close(OldIndex);
|
|
|
|
heap_close(OldHeap, NoLock);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2002-08-11 23:17:35 +02:00
|
|
|
* Create the new heap, using a temporary name in the same namespace
|
2002-09-04 22:31:48 +02:00
|
|
|
* as the existing table. NOTE: there is some risk of collision with
|
|
|
|
* user relnames. Working around this seems more trouble than it's
|
|
|
|
* worth; in particular, we can't create the new heap in a different
|
|
|
|
* namespace from the old, or we will have problems with the TEMP
|
|
|
|
* status of temp tables.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", OIDOldHeap);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
OIDNewHeap = make_new_heap(OIDOldHeap, NewHeapName);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
|
|
|
* We don't need CommandCounterIncrement() because make_new_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.
|
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
copy_heap_data(OIDNewHeap, OIDOldHeap, OIDOldIndex);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* To make the new heap's data visible (probably not needed?). */
|
1997-09-07 07:04:48 +02:00
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/* Swap the relfilenodes of the old and new heaps. */
|
2002-08-11 23:17:35 +02:00
|
|
|
swap_relfilenodes(OIDOldHeap, OIDNewHeap);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
|
|
|
CommandCounterIncrement();
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/* Destroy new heap with old filenode */
|
2002-07-12 20:43:19 +02:00
|
|
|
object.classId = RelOid_pg_class;
|
2002-08-10 22:43:46 +02:00
|
|
|
object.objectId = OIDNewHeap;
|
2002-07-12 20:43:19 +02:00
|
|
|
object.objectSubId = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* The new relation is local to our transaction and we know nothing
|
2002-08-10 22:43:46 +02:00
|
|
|
* depends on it, so DROP_RESTRICT should be OK.
|
|
|
|
*/
|
2002-07-12 20:43:19 +02:00
|
|
|
performDeletion(&object, DROP_RESTRICT);
|
|
|
|
|
|
|
|
/* performDeletion does CommandCounterIncrement at end */
|
1998-01-10 06:19:22 +01:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* Recreate each index on the relation. We do not need
|
|
|
|
* CommandCounterIncrement() because recreate_indexattr does it.
|
|
|
|
*/
|
|
|
|
recreate_indexattr(OIDOldHeap, indexes);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* Create the new table that we will fill with correctly-ordered data.
|
|
|
|
*/
|
2000-11-08 23:10:03 +01:00
|
|
|
static Oid
|
2002-08-11 23:17:35 +02:00
|
|
|
make_new_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,
|
2002-11-11 23:19:25 +01:00
|
|
|
ONCOMMIT_NOOP,
|
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-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* Do the physical copying of heap data.
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static void
|
2002-08-11 23:17:35 +02:00
|
|
|
copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-08-11 23:17:35 +02:00
|
|
|
Relation NewHeap,
|
|
|
|
OldHeap,
|
|
|
|
OldIndex;
|
|
|
|
IndexScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
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.
|
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
|
|
|
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
|
|
|
OldIndex = index_open(OIDOldIndex);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
scan = index_beginscan(OldHeap, OldIndex, SnapshotNow, 0, (ScanKey) NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-05-21 01:51:44 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +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)
|
2002-08-11 23:17:35 +02:00
|
|
|
*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Note that the copied tuple will have the original OID, if any, so
|
|
|
|
* this does preserve OIDs.
|
2002-05-21 01:51:44 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
HeapTuple copiedTuple = heap_copytuple(tuple);
|
|
|
|
|
|
|
|
simple_heap_insert(NewHeap, 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
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
index_endscan(scan);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
index_close(OldIndex);
|
|
|
|
heap_close(OldHeap, NoLock);
|
|
|
|
heap_close(NewHeap, NoLock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* Get the necessary info about the indexes of the relation and
|
|
|
|
* return a list of IndexAttrs structures.
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
static List *
|
2002-08-27 05:38:28 +02:00
|
|
|
get_indexattr_list(Relation OldHeap, Oid OldIndex)
|
2002-08-10 22:43:46 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
List *indexes = NIL;
|
2002-08-11 23:17:35 +02:00
|
|
|
List *indlist;
|
|
|
|
|
|
|
|
/* Ask the relcache to produce a list of the indexes of the old rel */
|
|
|
|
foreach(indlist, RelationGetIndexList(OldHeap))
|
2002-08-10 22:43:46 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid indexOID = (Oid) lfirsti(indlist);
|
|
|
|
HeapTuple indexTuple;
|
|
|
|
HeapTuple classTuple;
|
2002-08-11 23:17:35 +02:00
|
|
|
Form_pg_index indexForm;
|
|
|
|
Form_pg_class classForm;
|
|
|
|
IndexAttrs *attrs;
|
|
|
|
|
|
|
|
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);
|
|
|
|
Assert(indexForm->indexrelid == indexOID);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
|
|
|
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
|
2002-08-11 23:17:35 +02:00
|
|
|
attrs->indexOID = indexOID;
|
|
|
|
attrs->indexInfo = BuildIndexInfo(indexForm);
|
|
|
|
attrs->classOID = (Oid *)
|
|
|
|
palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
|
|
|
memcpy(attrs->classOID, indexForm->indclass,
|
|
|
|
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
2002-09-03 03:04:41 +02:00
|
|
|
attrs->isclustered = (OldIndex == indexOID);
|
2002-08-27 05:38:28 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* Name and access method of each index come from pg_class */
|
|
|
|
classTuple = SearchSysCache(RELOID,
|
|
|
|
ObjectIdGetDatum(indexOID),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(classTuple))
|
|
|
|
elog(ERROR, "Cache lookup failed for index %u", indexOID);
|
|
|
|
classForm = (Form_pg_class) GETSTRUCT(classTuple);
|
|
|
|
|
|
|
|
attrs->indexName = pstrdup(NameStr(classForm->relname));
|
|
|
|
attrs->accessMethodOID = classForm->relam;
|
|
|
|
|
|
|
|
ReleaseSysCache(classTuple);
|
|
|
|
ReleaseSysCache(indexTuple);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
|
|
|
* Cons the gathered data into the list. We do not care about
|
2002-08-10 22:43:46 +02:00
|
|
|
* ordering, and this is more efficient than append.
|
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
indexes = lcons(attrs, indexes);
|
2002-08-10 22:43:46 +02:00
|
|
|
}
|
2002-08-11 23:17:35 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
return indexes;
|
|
|
|
}
|
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Create new indexes and swap the filenodes with old indexes. Then drop
|
2002-08-11 23:17:35 +02:00
|
|
|
* the new index (carrying the old index filenode along).
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
static void
|
2002-08-10 22:43:46 +02:00
|
|
|
recreate_indexattr(Oid OIDOldHeap, List *indexes)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
List *elem;
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
foreach(elem, indexes)
|
2002-08-10 22:43:46 +02:00
|
|
|
{
|
2002-08-11 23:17:35 +02:00
|
|
|
IndexAttrs *attrs = (IndexAttrs *) lfirst(elem);
|
|
|
|
Oid newIndexOID;
|
|
|
|
char newIndexName[NAMEDATALEN];
|
|
|
|
ObjectAddress object;
|
2002-08-27 05:38:28 +02:00
|
|
|
Form_pg_index index;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Relation pg_index;
|
2002-08-10 22:43:46 +02:00
|
|
|
|
|
|
|
/* Create the new index under a temporary name */
|
2002-08-11 23:17:35 +02:00
|
|
|
snprintf(newIndexName, NAMEDATALEN, "pg_temp_%u", attrs->indexOID);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* 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.
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
|
|
|
newIndexOID = index_create(OIDOldHeap, newIndexName,
|
2002-09-04 22:31:48 +02:00
|
|
|
attrs->indexInfo, attrs->accessMethodOID,
|
2002-08-11 23:17:35 +02:00
|
|
|
attrs->classOID, false,
|
2002-08-10 22:43:46 +02:00
|
|
|
false, allowSystemTableMods);
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/* Swap the filenodes. */
|
2002-08-11 23:17:35 +02:00
|
|
|
swap_relfilenodes(attrs->indexOID, newIndexOID);
|
|
|
|
|
|
|
|
CommandCounterIncrement();
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-09-03 03:04:41 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Make sure that indisclustered is correct: it should be set only
|
|
|
|
* for the index we just clustered on.
|
2002-08-27 05:38:28 +02:00
|
|
|
*/
|
|
|
|
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
|
|
|
|
tuple = SearchSysCacheCopy(INDEXRELID,
|
2002-09-03 03:04:41 +02:00
|
|
|
ObjectIdGetDatum(attrs->indexOID),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for index %u", attrs->indexOID);
|
2002-08-27 05:38:28 +02:00
|
|
|
index = (Form_pg_index) GETSTRUCT(tuple);
|
2002-09-03 03:04:41 +02:00
|
|
|
if (index->indisclustered != attrs->isclustered)
|
|
|
|
{
|
|
|
|
index->indisclustered = attrs->isclustered;
|
|
|
|
simple_heap_update(pg_index, &tuple->t_self, tuple);
|
|
|
|
CatalogUpdateIndexes(pg_index, tuple);
|
|
|
|
}
|
2002-08-27 05:38:28 +02:00
|
|
|
heap_freetuple(tuple);
|
2002-09-03 03:04:41 +02:00
|
|
|
heap_close(pg_index, RowExclusiveLock);
|
2002-08-27 05:38:28 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/* Destroy new index with old filenode */
|
|
|
|
object.classId = RelOid_pg_class;
|
|
|
|
object.objectId = newIndexOID;
|
|
|
|
object.objectSubId = 0;
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* The relation is local to our transaction and we know nothing
|
|
|
|
* depends on it, so DROP_RESTRICT should be OK.
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
|
|
|
performDeletion(&object, DROP_RESTRICT);
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2002-08-10 22:43:46 +02:00
|
|
|
/* performDeletion does CommandCounterIncrement() at its end */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* Swap the relfilenodes for two given relations.
|
|
|
|
*
|
|
|
|
* Also swap any TOAST links, so that the toast data moves along with
|
|
|
|
* the main-table data.
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
static void
|
2002-08-10 22:43:46 +02:00
|
|
|
swap_relfilenodes(Oid r1, Oid r2)
|
|
|
|
{
|
|
|
|
Relation relRelation,
|
|
|
|
rel;
|
2002-08-11 23:17:35 +02:00
|
|
|
HeapTuple reltup1,
|
|
|
|
reltup2;
|
|
|
|
Form_pg_class relform1,
|
|
|
|
relform2;
|
|
|
|
Oid swaptemp;
|
2002-08-10 22:43:46 +02:00
|
|
|
int i;
|
2002-08-10 23:00:34 +02:00
|
|
|
CatalogIndexState indstate;
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* We need writable copies of both pg_class tuples. */
|
2002-08-10 22:43:46 +02:00
|
|
|
relRelation = heap_openr(RelationRelationName, RowExclusiveLock);
|
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
reltup1 = SearchSysCacheCopy(RELOID,
|
|
|
|
ObjectIdGetDatum(r1),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(reltup1))
|
2002-08-10 22:43:46 +02:00
|
|
|
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r1);
|
2002-08-11 23:17:35 +02:00
|
|
|
relform1 = (Form_pg_class) GETSTRUCT(reltup1);
|
|
|
|
|
|
|
|
reltup2 = SearchSysCacheCopy(RELOID,
|
|
|
|
ObjectIdGetDatum(r2),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(reltup2))
|
2002-08-10 22:43:46 +02:00
|
|
|
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r2);
|
2002-08-11 23:17:35 +02:00
|
|
|
relform2 = (Form_pg_class) GETSTRUCT(reltup2);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
|
|
|
* The buffer manager gets confused if we swap relfilenodes for
|
2002-08-10 22:43:46 +02:00
|
|
|
* relations that are not both local or non-local to this transaction.
|
|
|
|
* Flush the buffers on both relations so the buffer manager can
|
2002-08-11 23:17:35 +02:00
|
|
|
* forget about'em. (XXX this might not be necessary anymore?)
|
2002-08-10 22:43:46 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
rel = relation_open(r1, NoLock);
|
2002-08-10 22:43:46 +02:00
|
|
|
i = FlushRelationBuffers(rel, 0);
|
|
|
|
if (i < 0)
|
|
|
|
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
2002-08-11 23:17:35 +02:00
|
|
|
relation_close(rel, NoLock);
|
|
|
|
|
|
|
|
rel = relation_open(r2, NoLock);
|
2002-08-10 22:43:46 +02:00
|
|
|
i = FlushRelationBuffers(rel, 0);
|
|
|
|
if (i < 0)
|
|
|
|
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
2002-08-11 23:17:35 +02:00
|
|
|
relation_close(rel, NoLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Actually swap the filenode and TOAST fields in the two tuples
|
|
|
|
*/
|
|
|
|
swaptemp = relform1->relfilenode;
|
|
|
|
relform1->relfilenode = relform2->relfilenode;
|
|
|
|
relform2->relfilenode = swaptemp;
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
swaptemp = relform1->reltoastrelid;
|
|
|
|
relform1->reltoastrelid = relform2->reltoastrelid;
|
|
|
|
relform2->reltoastrelid = swaptemp;
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* we should not swap reltoastidxid */
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-11-02 22:20:40 +01:00
|
|
|
/* swap size statistics too, since new rel has freshly-updated stats */
|
|
|
|
{
|
|
|
|
int4 swap_pages;
|
|
|
|
float4 swap_tuples;
|
|
|
|
|
|
|
|
swap_pages = relform1->relpages;
|
|
|
|
relform1->relpages = relform2->relpages;
|
|
|
|
relform2->relpages = swap_pages;
|
|
|
|
|
|
|
|
swap_tuples = relform1->reltuples;
|
|
|
|
relform1->reltuples = relform2->reltuples;
|
|
|
|
relform2->reltuples = swap_tuples;
|
|
|
|
}
|
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* Update the tuples in pg_class */
|
|
|
|
simple_heap_update(relRelation, &reltup1->t_self, reltup1);
|
|
|
|
simple_heap_update(relRelation, &reltup2->t_self, reltup2);
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/* Keep system catalogs current */
|
2002-08-10 23:00:34 +02:00
|
|
|
indstate = CatalogOpenIndexes(relRelation);
|
2002-08-11 23:17:35 +02:00
|
|
|
CatalogIndexInsert(indstate, reltup1);
|
|
|
|
CatalogIndexInsert(indstate, reltup2);
|
2002-08-10 23:00:34 +02:00
|
|
|
CatalogCloseIndexes(indstate);
|
2002-08-10 22:43:46 +02:00
|
|
|
|
2002-08-11 23:17:35 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* If we have toast tables associated with the relations being
|
|
|
|
* swapped, change their dependency links to re-associate them with
|
|
|
|
* their new owning relations. Otherwise the wrong one will get
|
|
|
|
* dropped ...
|
2002-08-11 23:17:35 +02:00
|
|
|
*
|
2002-09-04 22:31:48 +02:00
|
|
|
* NOTE: for now, we can assume the new table will have a TOAST table if
|
|
|
|
* and only if the old one does. This logic might need work if we get
|
|
|
|
* smarter about dropped columns.
|
2002-08-11 23:17:35 +02:00
|
|
|
*
|
2002-09-04 22:31:48 +02:00
|
|
|
* NOTE: at present, a TOAST table's only dependency is the one on its
|
|
|
|
* owning table. If more are ever created, we'd need to use something
|
2002-08-11 23:17:35 +02:00
|
|
|
* more selective than deleteDependencyRecordsFor() to get rid of only
|
|
|
|
* the link we want.
|
|
|
|
*/
|
|
|
|
if (relform1->reltoastrelid || relform2->reltoastrelid)
|
|
|
|
{
|
|
|
|
ObjectAddress baseobject,
|
|
|
|
toastobject;
|
|
|
|
long count;
|
|
|
|
|
|
|
|
if (!(relform1->reltoastrelid && relform2->reltoastrelid))
|
|
|
|
elog(ERROR, "CLUSTER: expected both swapped tables to have TOAST tables");
|
|
|
|
|
|
|
|
/* Delete old dependencies */
|
|
|
|
count = deleteDependencyRecordsFor(RelOid_pg_class,
|
|
|
|
relform1->reltoastrelid);
|
|
|
|
if (count != 1)
|
|
|
|
elog(ERROR, "CLUSTER: expected one dependency record for TOAST table, found %ld",
|
|
|
|
count);
|
|
|
|
count = deleteDependencyRecordsFor(RelOid_pg_class,
|
|
|
|
relform2->reltoastrelid);
|
|
|
|
if (count != 1)
|
|
|
|
elog(ERROR, "CLUSTER: expected one dependency record for TOAST table, found %ld",
|
|
|
|
count);
|
|
|
|
|
|
|
|
/* Register new dependencies */
|
|
|
|
baseobject.classId = RelOid_pg_class;
|
|
|
|
baseobject.objectId = r1;
|
|
|
|
baseobject.objectSubId = 0;
|
|
|
|
toastobject.classId = RelOid_pg_class;
|
|
|
|
toastobject.objectId = relform1->reltoastrelid;
|
|
|
|
toastobject.objectSubId = 0;
|
|
|
|
|
|
|
|
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
|
|
|
|
|
|
|
|
baseobject.objectId = r2;
|
|
|
|
toastobject.objectId = relform2->reltoastrelid;
|
|
|
|
|
|
|
|
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Blow away the old relcache entries now. We need this kluge because
|
|
|
|
* relcache.c indexes relcache entries by rd_node as well as OID. It
|
|
|
|
* will get confused if it is asked to (re)build an entry with a new
|
2002-08-11 23:17:35 +02:00
|
|
|
* rd_node value when there is still another entry laying about with
|
2002-09-04 22:31:48 +02:00
|
|
|
* that same rd_node value. (Fortunately, since one of the entries is
|
|
|
|
* local in our transaction, it's sufficient to clear out our own
|
2002-08-11 23:17:35 +02:00
|
|
|
* relcache this way; the problem cannot arise for other backends when
|
|
|
|
* they see our update on the non-local relation.)
|
|
|
|
*/
|
|
|
|
RelationForgetRelation(r1);
|
|
|
|
RelationForgetRelation(r2);
|
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
heap_freetuple(reltup1);
|
|
|
|
heap_freetuple(reltup2);
|
|
|
|
|
|
|
|
heap_close(relRelation, RowExclusiveLock);
|
2002-08-10 22:43:46 +02:00
|
|
|
}
|