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
*
*
2007-01-05 23:20:05 +01:00
* Portions Copyright ( c ) 1996 - 2007 , 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
2007-04-08 03:26:33 +02:00
* $ PostgreSQL : pgsql / src / backend / commands / cluster . c , v 1.159 2007 / 04 / 08 01 : 26 : 28 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"
2007-04-08 03:26:33 +02:00
# include "access/rewriteheap.h"
2006-07-13 18:49:20 +02:00
# include "access/xact.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"
2002-11-15 04:09:39 +01:00
# include "catalog/namespace.h"
2006-07-31 03:16:38 +02:00
# include "catalog/toasting.h"
1999-07-16 01:04:24 +02:00
# include "commands/cluster.h"
2000-07-04 08:11:54 +02:00
# include "miscadmin.h"
2007-04-08 03:26:33 +02:00
# include "storage/procarray.h"
2002-11-15 04:09:39 +01:00
# include "utils/acl.h"
2002-08-10 22:43:46 +02:00
# include "utils/fmgroids.h"
2004-05-06 18:10:57 +02:00
# include "utils/inval.h"
2002-03-29 23:10:34 +01:00
# include "utils/lsyscache.h"
2005-05-06 19:24:55 +02:00
# include "utils/memutils.h"
2002-08-10 22:43:46 +02:00
# include "utils/relcache.h"
2007-04-08 03:26:33 +02:00
# include "utils/syscache.h"
1996-07-09 08:22:35 +02:00
2002-12-30 19:42:17 +01:00
/*
* This struct is used to pass around the information on tables to be
2002-11-15 04:09:39 +01:00
* clustered . We need this so we can make a list of them when invoked without
* a specific table / index pair .
*/
typedef struct
{
2003-08-04 02:43:34 +02:00
Oid tableOid ;
Oid indexOid ;
2003-08-08 23:42:59 +02:00
} RelToCluster ;
2002-11-15 04:09:39 +01:00
2002-12-30 19:42:17 +01:00
2003-08-08 23:42:59 +02:00
static void cluster_rel ( RelToCluster * rv , bool recheck ) ;
2004-05-08 02:34:49 +02:00
static void rebuild_relation ( Relation OldHeap , Oid indexOid ) ;
2002-08-11 23:17:35 +02:00
static void copy_heap_data ( Oid OIDNewHeap , Oid OIDOldHeap , Oid OIDOldIndex ) ;
2002-12-30 19:42:17 +01:00
static List * get_tables_to_cluster ( MemoryContext cluster_context ) ;
/*---------------------------------------------------------------------------
2003-08-04 02:43:34 +02:00
* This cluster code allows for clustering multiple tables at once . Because
2002-12-30 19:42:17 +01:00
* of this , we cannot just run everything on a single transaction , or we
* would be forced to acquire exclusive locks on all the tables being
* clustered , simultaneously - - - very likely leading to deadlock .
*
* To solve this we follow a similar strategy to VACUUM code ,
* clustering each relation in a separate transaction . For this to work ,
* we need to :
2003-08-04 02:43:34 +02:00
* - provide a separate memory context so that we can pass information in
* a way that survives across transactions
* - start a new transaction every time a new relation is clustered
* - check for validity of the information on to - be - clustered relations ,
* as someone might have deleted a relation behind our back , or
* clustered one on a different index
* - end the transaction
2002-12-30 19:42:17 +01:00
*
* The single - relation case does not have any such overhead .
*
2007-04-08 03:26:33 +02:00
* We also allow a relation to be specified without index . In that case ,
2002-12-30 19:42:17 +01:00
* the indisclustered bit will be looked up , and an ERROR will be thrown
* if there is no index with the bit set .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void
2007-03-13 01:33:44 +01:00
cluster ( ClusterStmt * stmt , bool isTopLevel )
2002-12-30 19:42:17 +01:00
{
if ( stmt - > relation ! = NULL )
{
/* This is the single-relation case. */
2003-08-04 02:43:34 +02:00
Oid tableOid ,
indexOid = InvalidOid ;
Relation rel ;
RelToCluster rvtc ;
2002-12-30 19:42:17 +01:00
/* Find and lock the table */
2002-12-30 20:45:17 +01:00
rel = heap_openrv ( stmt - > relation , AccessExclusiveLock ) ;
2002-12-30 19:42:17 +01:00
2002-12-30 20:45:17 +01:00
tableOid = RelationGetRelid ( rel ) ;
2002-12-30 19:42:17 +01:00
/* Check permissions */
2003-08-01 02:15:26 +02:00
if ( ! pg_class_ownercheck ( tableOid , GetUserId ( ) ) )
aclcheck_error ( ACLCHECK_NOT_OWNER , ACL_KIND_CLASS ,
RelationGetRelationName ( rel ) ) ;
2002-12-30 19:42:17 +01:00
if ( stmt - > indexname = = NULL )
{
2004-05-26 06:41:50 +02:00
ListCell * index ;
2002-12-30 19:42:17 +01:00
/* We need to find the index that has indisclustered set. */
2003-08-04 02:43:34 +02:00
foreach ( index , RelationGetIndexList ( rel ) )
2002-12-30 19:42:17 +01:00
{
2003-08-04 02:43:34 +02:00
HeapTuple idxtuple ;
Form_pg_index indexForm ;
2002-12-30 19:42:17 +01:00
2004-05-26 06:41:50 +02:00
indexOid = lfirst_oid ( index ) ;
2002-12-30 19:42:17 +01:00
idxtuple = SearchSysCache ( INDEXRELID ,
ObjectIdGetDatum ( indexOid ) ,
0 , 0 , 0 ) ;
if ( ! HeapTupleIsValid ( idxtuple ) )
2003-07-20 23:56:35 +02:00
elog ( ERROR , " cache lookup failed for index %u " , indexOid ) ;
2002-12-30 19:42:17 +01:00
indexForm = ( Form_pg_index ) GETSTRUCT ( idxtuple ) ;
if ( indexForm - > indisclustered )
{
ReleaseSysCache ( idxtuple ) ;
break ;
}
ReleaseSysCache ( idxtuple ) ;
indexOid = InvalidOid ;
}
if ( ! OidIsValid ( indexOid ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_OBJECT ) ,
errmsg ( " there is no previously clustered index for table \" %s \" " ,
stmt - > relation - > relname ) ) ) ;
2002-12-30 19:42:17 +01:00
}
else
{
2003-08-04 02:43:34 +02:00
/*
* The index is expected to be in the same namespace as the
* relation .
*/
2002-12-30 19:42:17 +01:00
indexOid = get_relname_relid ( stmt - > indexname ,
rel - > rd_rel - > relnamespace ) ;
if ( ! OidIsValid ( indexOid ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_OBJECT ) ,
2005-10-15 04:49:52 +02:00
errmsg ( " index \" %s \" for table \" %s \" does not exist " ,
stmt - > indexname , stmt - > relation - > relname ) ) ) ;
2002-12-30 19:42:17 +01:00
}
2003-03-03 05:37:37 +01:00
/* All other checks are done in cluster_rel() */
2002-12-30 19:42:17 +01:00
rvtc . tableOid = tableOid ;
rvtc . indexOid = indexOid ;
/* close relation, keep lock till commit */
heap_close ( rel , NoLock ) ;
/* Do the job */
cluster_rel ( & rvtc , false ) ;
}
else
{
/*
2005-10-15 04:49:52 +02:00
* This is the " multi relation " case . We need to cluster all tables
* that have some index with indisclustered set .
2002-12-30 19:42:17 +01:00
*/
2003-08-04 02:43:34 +02:00
MemoryContext cluster_context ;
2004-05-26 06:41:50 +02:00
List * rvs ;
ListCell * rv ;
2002-12-30 19:42:17 +01:00
/*
2005-10-15 04:49:52 +02:00
* We cannot run this form of CLUSTER inside a user transaction block ;
* we ' d be holding locks way too long .
2002-12-30 19:42:17 +01:00
*/
2007-03-13 01:33:44 +01:00
PreventTransactionChain ( isTopLevel , " CLUSTER " ) ;
2002-12-30 19:42:17 +01:00
/*
* Create special memory context for cross - transaction storage .
*
2005-11-22 19:17:34 +01:00
* Since it is a child of PortalContext , it will go away even in case
* of error .
2002-12-30 19:42:17 +01:00
*/
2003-05-02 22:54:36 +02:00
cluster_context = AllocSetContextCreate ( PortalContext ,
2002-12-30 19:42:17 +01:00
" Cluster " ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
/*
2005-10-15 04:49:52 +02:00
* Build the list of relations to cluster . Note that this lives in
* cluster_context .
2002-12-30 19:42:17 +01:00
*/
rvs = get_tables_to_cluster ( cluster_context ) ;
/* Commit to get out of starting transaction */
2003-05-14 05:26:03 +02:00
CommitTransactionCommand ( ) ;
2002-12-30 19:42:17 +01:00
/* Ok, now that we've got them all, cluster them one by one */
2003-08-04 02:43:34 +02:00
foreach ( rv , rvs )
2002-12-30 19:42:17 +01:00
{
2003-08-04 02:43:34 +02:00
RelToCluster * rvtc = ( RelToCluster * ) lfirst ( rv ) ;
2002-12-30 19:42:17 +01:00
/* Start a new transaction for each relation. */
2003-05-14 05:26:03 +02:00
StartTransactionCommand ( ) ;
2004-09-13 22:10:13 +02:00
/* functions in indexes may want a snapshot set */
ActiveSnapshot = CopySnapshot ( GetTransactionSnapshot ( ) ) ;
2002-12-30 19:42:17 +01:00
cluster_rel ( rvtc , true ) ;
2003-05-14 05:26:03 +02:00
CommitTransactionCommand ( ) ;
2002-12-30 19:42:17 +01:00
}
/* Start a new transaction for the cleanup work. */
2003-05-14 05:26:03 +02:00
StartTransactionCommand ( ) ;
2002-08-10 22:43:46 +02:00
2002-12-30 19:42:17 +01:00
/* Clean up working storage */
MemoryContextDelete ( cluster_context ) ;
}
}
1997-08-19 23:40:56 +02:00
1996-07-09 08:22:35 +02:00
/*
2002-12-30 19:42:17 +01:00
* cluster_rel
1996-07-09 08:22:35 +02:00
*
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 .
1996-07-09 08:22:35 +02:00
*/
2002-12-30 19:42:17 +01:00
static void
2003-08-08 23:42:59 +02:00
cluster_rel ( RelToCluster * rvtc , bool recheck )
1996-07-09 08:22:35 +02:00
{
2004-05-06 18:10:57 +02:00
Relation OldHeap ;
1997-09-07 07:04:48 +02:00
2002-11-15 04:09:39 +01:00
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS ( ) ;
2006-08-18 18:09:13 +02:00
/*
* We grab exclusive access to the target rel and index for the duration
* of the transaction . ( This is redundant for the single - transaction
* case , since cluster ( ) already did it . ) The index lock is taken inside
* check_index_is_clusterable .
*/
OldHeap = try_relation_open ( rvtc - > tableOid , AccessExclusiveLock ) ;
/* If the table has gone away, we can skip processing it */
if ( ! OldHeap )
return ;
2002-12-30 19:42:17 +01:00
/*
2005-10-15 04:49:52 +02:00
* Since we may open a new transaction for each relation , we have to check
* that the relation still is what we think it is .
2002-12-30 19:42:17 +01:00
*
2005-11-22 19:17:34 +01:00
* If this is a single - transaction CLUSTER , we can skip these tests . We
* * must * skip the one on indisclustered since it would reject an attempt
* to cluster a not - previously - clustered index .
2002-11-15 04:09:39 +01:00
*/
2002-12-30 19:42:17 +01:00
if ( recheck )
{
2003-08-04 02:43:34 +02:00
HeapTuple tuple ;
Form_pg_index indexForm ;
2002-12-30 19:42:17 +01:00
2006-08-18 18:09:13 +02:00
/* Check that the user still owns the relation */
if ( ! pg_class_ownercheck ( rvtc - > tableOid , GetUserId ( ) ) )
{
relation_close ( OldHeap , AccessExclusiveLock ) ;
return ;
}
2002-12-30 19:42:17 +01:00
/*
2006-08-18 18:09:13 +02:00
* Check that the index still exists
2002-12-30 19:42:17 +01:00
*/
if ( ! SearchSysCacheExists ( RELOID ,
2002-11-15 04:09:39 +01:00
ObjectIdGetDatum ( rvtc - > indexOid ) ,
0 , 0 , 0 ) )
2006-08-18 18:09:13 +02:00
{
relation_close ( OldHeap , AccessExclusiveLock ) ;
2002-12-30 19:42:17 +01:00
return ;
2006-08-18 18:09:13 +02:00
}
2002-11-15 04:09:39 +01:00
2002-12-30 19:42:17 +01:00
/*
* Check that the index is still the one with indisclustered set .
*/
2002-11-15 04:09:39 +01:00
tuple = SearchSysCache ( INDEXRELID ,
ObjectIdGetDatum ( rvtc - > indexOid ) ,
0 , 0 , 0 ) ;
2006-10-04 02:30:14 +02:00
if ( ! HeapTupleIsValid ( tuple ) ) /* probably can't happen */
2006-08-18 18:09:13 +02:00
{
relation_close ( OldHeap , AccessExclusiveLock ) ;
return ;
}
2002-11-15 04:09:39 +01:00
indexForm = ( Form_pg_index ) GETSTRUCT ( tuple ) ;
if ( ! indexForm - > indisclustered )
{
ReleaseSysCache ( tuple ) ;
2006-08-18 18:09:13 +02:00
relation_close ( OldHeap , AccessExclusiveLock ) ;
2002-11-15 04:09:39 +01:00
return ;
}
ReleaseSysCache ( tuple ) ;
}
2004-05-06 18:10:57 +02:00
/* Check index is valid to cluster on */
2005-05-10 15:16:26 +02:00
check_index_is_clusterable ( OldHeap , rvtc - > indexOid , recheck ) ;
2004-05-06 18:10:57 +02:00
/* rebuild_relation does all the dirty work */
rebuild_relation ( OldHeap , rvtc - > indexOid ) ;
/* NB: rebuild_relation does heap_close() on OldHeap */
}
/*
* Verify that the specified index is a legitimate index to cluster on
*
* Side effect : obtains exclusive lock on the index . The caller should
* already have exclusive lock on the table , so the index lock is likely
* redundant , but it seems best to grab it anyway to ensure the index
* definition can ' t change under us .
*/
void
2005-05-10 15:16:26 +02:00
check_index_is_clusterable ( Relation OldHeap , Oid indexOid , bool recheck )
2004-05-06 18:10:57 +02:00
{
Relation OldIndex ;
2006-07-31 22:09:10 +02:00
OldIndex = index_open ( indexOid , 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 | |
2004-05-06 18:10:57 +02:00
OldIndex - > rd_index - > indrelid ! = RelationGetRelid ( OldHeap ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " \" %s \" is not an index for table \" %s \" " ,
RelationGetRelationName ( OldIndex ) ,
RelationGetRelationName ( OldHeap ) ) ) ) ;
2000-11-08 23:10:03 +01:00
2003-03-03 05:37:37 +01:00
/*
2005-10-15 04:49:52 +02:00
* Disallow clustering on incomplete indexes ( those that might not index
* every row of the relation ) . We could relax this by making a separate
* seqscan pass over the table to copy the missing rows , but that seems
* expensive and tedious .
2003-03-03 05:37:37 +01:00
*/
2003-05-28 18:04:02 +02:00
if ( ! heap_attisnull ( OldIndex - > rd_indextuple , Anum_pg_index_indpred ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
2005-05-10 15:16:26 +02:00
errmsg ( " cannot cluster on partial index \" %s \" " ,
RelationGetRelationName ( OldIndex ) ) ) ) ;
2005-10-15 04:49:52 +02:00
2006-10-04 02:30:14 +02:00
if ( ! OldIndex - > rd_am - > amclusterable )
2006-05-03 00:25:10 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " cannot cluster on index \" %s \" because access method does not support clustering " ,
RelationGetRelationName ( OldIndex ) ) ) ) ;
2003-03-03 05:37:37 +01:00
if ( ! OldIndex - > rd_am - > amindexnulls )
{
AttrNumber colno ;
/*
2005-10-15 04:49:52 +02:00
* If the AM doesn ' t index nulls , then it ' s a partial index unless we
* can prove all the rows are non - null . Note we only need look at the
* first column ; multicolumn - capable AMs are * required * to index nulls
* in columns after the first .
2003-03-03 05:37:37 +01:00
*/
2005-03-29 02:17:27 +02:00
colno = OldIndex - > rd_index - > indkey . values [ 0 ] ;
2003-05-28 18:04:02 +02:00
if ( colno > 0 )
{
/* ordinary user attribute */
2003-03-03 05:37:37 +01:00
if ( ! OldHeap - > rd_att - > attrs [ colno - 1 ] - > attnotnull )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
2005-10-29 02:31:52 +02:00
errmsg ( " cannot cluster on index \" %s \" because access method does not handle null values " ,
2005-10-15 04:49:52 +02:00
RelationGetRelationName ( OldIndex ) ) ,
2005-10-29 02:31:52 +02:00
recheck
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
2007-02-01 20:10:30 +01:00
? errhint ( " You might be able to work around this by marking column \" %s \" NOT NULL, or use ALTER TABLE ... SET WITHOUT CLUSTER to remove the cluster specification from the table. " ,
2005-11-22 19:17:34 +01:00
NameStr ( OldHeap - > rd_att - > attrs [ colno - 1 ] - > attname ) )
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
2007-02-01 20:10:30 +01:00
: errhint ( " You might be able to work around this by marking column \" %s \" NOT NULL. " ,
2005-11-22 19:17:34 +01:00
NameStr ( OldHeap - > rd_att - > attrs [ colno - 1 ] - > attname ) ) ) ) ;
2003-05-28 18:04:02 +02:00
}
else if ( colno < 0 )
{
/* system column --- okay, always non-null */
}
else
/* index expression, lose... */
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
2005-10-29 02:31:52 +02:00
errmsg ( " cannot cluster on expressional index \" %s \" because its index access method does not handle null values " ,
2005-10-15 04:49:52 +02:00
RelationGetRelationName ( OldIndex ) ) ) ) ;
2003-03-03 05:37:37 +01:00
}
2002-08-11 23:17:35 +02:00
/*
2005-10-15 04:49:52 +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 ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " \" %s \" is a system catalog " ,
RelationGetRelationName ( OldHeap ) ) ) ) ;
1997-09-07 07:04:48 +02:00
2002-12-30 20:45:17 +01:00
/*
2005-10-15 04:49:52 +02:00
* Don ' t allow cluster on temp tables of other backends . . . their local
* buffer manager is not going to cope .
2002-12-30 20:45:17 +01:00
*/
if ( isOtherTempNamespace ( RelationGetNamespace ( OldHeap ) ) )
2003-07-20 23:56:35 +02:00
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
2005-10-15 04:49:52 +02:00
errmsg ( " cannot cluster temporary tables of other sessions " ) ) ) ;
2002-12-30 20:45:17 +01:00
2002-12-30 19:42:17 +01:00
/* Drop relcache refcnt on OldIndex, but keep lock */
2006-07-31 22:09:10 +02:00
index_close ( OldIndex , NoLock ) ;
2002-11-23 05:05:52 +01:00
}
2002-12-30 19:42:17 +01:00
/*
2004-05-08 02:34:49 +02:00
* mark_index_clustered : mark the specified index as the one clustered on
2002-12-30 19:42:17 +01:00
*
2004-05-08 02:34:49 +02:00
* 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 ;
2004-05-26 06:41:50 +02:00
ListCell * index ;
2004-05-08 02:34:49 +02:00
/*
* 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 .
*/
2005-04-14 22:03:27 +02:00
pg_index = heap_open ( IndexRelationId , RowExclusiveLock ) ;
2004-05-08 02:34:49 +02:00
foreach ( index , RelationGetIndexList ( rel ) )
{
2004-08-29 07:07:03 +02:00
Oid thisIndexOid = lfirst_oid ( index ) ;
2004-05-08 02:34:49 +02:00
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 ) ;
/*
2005-10-15 04:49:52 +02:00
* Unset the bit if set . We know it ' s wrong because we checked this
* earlier .
2004-05-08 02:34:49 +02:00
*/
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
2002-12-30 19:42:17 +01:00
*
* OldHeap : table to rebuild - - - must be opened and exclusive - locked !
2004-05-08 02:34:49 +02:00
* indexOid : index to cluster by
2002-12-30 19:42:17 +01:00
*
* NB : this routine closes OldHeap at the right time ; caller should not .
*/
2004-05-08 02:34:49 +02:00
static void
2002-12-30 19:42:17 +01:00
rebuild_relation ( Relation OldHeap , Oid indexOid )
2002-11-23 05:05:52 +01:00
{
2002-12-30 19:42:17 +01:00
Oid tableOid = RelationGetRelid ( OldHeap ) ;
2004-07-12 01:13:58 +02:00
Oid tableSpace = OldHeap - > rd_rel - > reltablespace ;
2002-11-23 05:05:52 +01:00
Oid OIDNewHeap ;
char NewHeapName [ NAMEDATALEN ] ;
ObjectAddress object ;
2004-05-08 02:34:49 +02:00
/* Mark the correct index as clustered */
mark_index_clustered ( OldHeap , indexOid ) ;
2002-12-30 19:42:17 +01:00
/* Close relcache entry, but keep lock until transaction commit */
heap_close ( OldHeap , NoLock ) ;
1997-09-07 07:04:48 +02:00
/*
2005-10-15 04:49:52 +02:00
* Create the new heap , using a temporary name in the same namespace 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
*/
2003-03-20 04:34:57 +01:00
snprintf ( NewHeapName , sizeof ( NewHeapName ) , " pg_temp_%u " , tableOid ) ;
1997-09-07 07:04:48 +02:00
2004-07-12 01:13:58 +02:00
OIDNewHeap = make_new_heap ( tableOid , NewHeapName , tableSpace ) ;
2003-08-04 02:43:34 +02:00
2002-09-04 22:31:48 +02:00
/*
2005-10-15 04:49:52 +02:00
* We don ' t need CommandCounterIncrement ( ) because make_new_heap did it .
2002-09-04 22:31:48 +02:00
*/
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 .
*/
2004-05-08 02:34:49 +02:00
copy_heap_data ( OIDNewHeap , tableOid , indexOid ) ;
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 ( ) ;
2004-07-12 01:13:58 +02:00
/* Swap the physical files of the old and new heaps. */
swap_relation_files ( tableOid , 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 */
2005-04-14 03:38:22 +02:00
object . classId = RelationRelationId ;
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
/*
2005-10-15 04:49:52 +02:00
* 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 .
2002-08-11 23:17:35 +02:00
*/
2004-05-08 02:34:49 +02:00
reindex_relation ( tableOid , false ) ;
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 .
*/
2004-05-05 06:48:48 +02:00
Oid
2004-07-12 01:13:58 +02:00
make_new_heap ( Oid OIDOldHeap , const char * NewName , Oid NewTableSpace )
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 ;
2006-07-02 04:23:23 +02:00
HeapTuple tuple ;
2006-07-04 00:45:41 +02:00
Datum reloptions ;
bool isNull ;
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
2006-07-02 04:23:23 +02:00
/*
* Use options of the old heap for new heap .
*/
tuple = SearchSysCache ( RELOID ,
ObjectIdGetDatum ( OIDOldHeap ) ,
0 , 0 , 0 ) ;
2006-07-04 00:45:41 +02:00
if ( ! HeapTupleIsValid ( tuple ) )
elog ( ERROR , " cache lookup failed for relation %u " , OIDOldHeap ) ;
reloptions = SysCacheGetAttr ( RELOID , tuple , Anum_pg_class_reloptions ,
& isNull ) ;
if ( isNull )
reloptions = ( Datum ) 0 ;
2006-07-02 04:23:23 +02:00
2002-03-26 20:17:02 +01:00
OIDNewHeap = heap_create_with_catalog ( NewName ,
RelationGetNamespace ( OldHeap ) ,
2004-08-29 07:07:03 +02:00
NewTableSpace ,
2005-04-14 03:38:22 +02:00
InvalidOid ,
2005-08-26 05:08:15 +02:00
OldHeap - > rd_rel - > relowner ,
2002-03-26 20:17:02 +01:00
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 ,
2004-03-23 20:35:17 +01:00
true ,
0 ,
2002-11-11 23:19:25 +01:00
ONCOMMIT_NOOP ,
2006-07-04 00:45:41 +02:00
reloptions ,
allowSystemTableMods ) ;
2006-07-02 04:23:23 +02:00
ReleaseSysCache ( tuple ) ;
1997-09-07 07:04:48 +02:00
2001-01-01 22:35:00 +01:00
/*
2005-10-15 04:49:52 +02: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
2005-10-15 04:49:52 +02:00
* AlterTableCreateToastTable ends with CommandCounterIncrement ( ) , so that
* the TOAST table will be visible for insertion .
2001-01-01 22:35:00 +01:00
*/
2006-07-31 03:16:38 +02:00
AlterTableCreateToastTable ( OIDNewHeap ) ;
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 ;
2005-02-06 21:19:08 +01:00
TupleDesc oldTupDesc ;
TupleDesc newTupDesc ;
int natts ;
Datum * values ;
2007-04-08 03:26:33 +02:00
bool * isnull ;
2002-08-11 23:17:35 +02:00
IndexScanDesc scan ;
HeapTuple tuple ;
2007-03-29 02:15:39 +02:00
bool use_wal ;
2007-04-08 03:26:33 +02:00
TransactionId OldestXmin ;
RewriteState rwstate ;
1997-09-07 07:04:48 +02:00
/*
2005-02-06 21:19:08 +01:00
* Open the relations we need .
1997-09-07 07:04:48 +02:00
*/
2002-08-11 23:17:35 +02:00
NewHeap = heap_open ( OIDNewHeap , AccessExclusiveLock ) ;
OldHeap = heap_open ( OIDOldHeap , AccessExclusiveLock ) ;
2006-07-31 22:09:10 +02:00
OldIndex = index_open ( OIDOldIndex , AccessExclusiveLock ) ;
1997-09-07 07:04:48 +02:00
2005-02-06 21:19:08 +01:00
/*
2005-10-15 04:49:52 +02:00
* Their tuple descriptors should be exactly alike , but here we only need
* assume that they have the same number of columns .
2005-02-06 21:19:08 +01:00
*/
oldTupDesc = RelationGetDescr ( OldHeap ) ;
newTupDesc = RelationGetDescr ( NewHeap ) ;
Assert ( newTupDesc - > natts = = oldTupDesc - > natts ) ;
2007-04-08 03:26:33 +02:00
/* Preallocate values/isnull arrays */
2005-02-06 21:19:08 +01:00
natts = newTupDesc - > natts ;
2007-04-08 03:26:33 +02:00
values = ( Datum * ) palloc ( natts * sizeof ( Datum ) ) ;
isnull = ( bool * ) palloc ( natts * sizeof ( bool ) ) ;
2005-02-06 21:19:08 +01:00
2007-03-29 02:15:39 +02:00
/*
* We need to log the copied data in WAL iff WAL archiving is enabled AND
2007-04-08 03:26:33 +02:00
* it ' s not a temp rel .
2007-03-29 02:15:39 +02:00
*/
use_wal = XLogArchivingActive ( ) & & ! NewHeap - > rd_istemp ;
/* use_wal off requires rd_targblock be initially invalid */
Assert ( NewHeap - > rd_targblock = = InvalidBlockNumber ) ;
2007-04-08 03:26:33 +02:00
/* Get the cutoff xmin we'll use to weed out dead tuples */
OldestXmin = GetOldestXmin ( OldHeap - > rd_rel - > relisshared , true ) ;
/* Initialize the rewrite operation */
rwstate = begin_heap_rewrite ( NewHeap , OldestXmin , use_wal ) ;
2005-02-06 21:19:08 +01:00
/*
2007-04-08 03:26:33 +02:00
* Scan through the OldHeap in OldIndex order and copy each tuple into the
* NewHeap . To ensure we see recently - dead tuples that still need to be
* copied , we scan with SnapshotAny and use HeapTupleSatisfiesVacuum
* for the visibility test .
2005-02-06 21:19:08 +01:00
*/
2006-07-31 22:09:10 +02:00
scan = index_beginscan ( OldHeap , OldIndex ,
2007-04-08 03:26:33 +02:00
SnapshotAny , 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
{
2007-04-08 03:26:33 +02:00
HeapTuple copiedTuple ;
bool isdead ;
int i ;
CHECK_FOR_INTERRUPTS ( ) ;
LockBuffer ( scan - > xs_cbuf , BUFFER_LOCK_SHARE ) ;
switch ( HeapTupleSatisfiesVacuum ( tuple - > t_data , OldestXmin ,
scan - > xs_cbuf ) )
{
case HEAPTUPLE_DEAD :
/* Definitely dead */
isdead = true ;
break ;
case HEAPTUPLE_LIVE :
case HEAPTUPLE_RECENTLY_DEAD :
/* Live or recently dead, must copy it */
isdead = false ;
break ;
case HEAPTUPLE_INSERT_IN_PROGRESS :
/*
* We should not see this unless it ' s been inserted earlier
* in our own transaction .
*/
if ( ! TransactionIdIsCurrentTransactionId (
HeapTupleHeaderGetXmin ( tuple - > t_data ) ) )
elog ( ERROR , " concurrent insert in progress " ) ;
/* treat as live */
isdead = false ;
break ;
case HEAPTUPLE_DELETE_IN_PROGRESS :
/*
* We should not see this unless it ' s been deleted earlier
* in our own transaction .
*/
Assert ( ! ( tuple - > t_data - > t_infomask & HEAP_XMAX_IS_MULTI ) ) ;
if ( ! TransactionIdIsCurrentTransactionId (
HeapTupleHeaderGetXmax ( tuple - > t_data ) ) )
elog ( ERROR , " concurrent delete in progress " ) ;
/* treat as recently dead */
isdead = false ;
break ;
default :
elog ( ERROR , " unexpected HeapTupleSatisfiesVacuum result " ) ;
isdead = false ; /* keep compiler quiet */
break ;
}
LockBuffer ( scan - > xs_cbuf , BUFFER_LOCK_UNLOCK ) ;
if ( isdead )
{
/* heap rewrite module still needs to see it... */
rewrite_heap_dead_tuple ( rwstate , tuple ) ;
continue ;
}
2002-05-21 01:51:44 +02:00
/*
2007-04-08 03:26:33 +02:00
* We cannot simply copy the tuple as - is , for several reasons :
2005-02-06 21:19:08 +01:00
*
2007-04-08 03:26:33 +02:00
* 1. We ' d like to squeeze out the values of any dropped columns , both
2005-11-22 19:17:34 +01:00
* to save space and to ensure we have no corner - case failures . ( It ' s
2005-10-15 04:49:52 +02:00
* possible for example that the new table hasn ' t got a TOAST table
* and so is unable to store any large values of dropped cols . )
2002-08-11 23:17:35 +02:00
*
2007-04-08 03:26:33 +02:00
* 2. The tuple might not even be legal for the new table ; this is
2005-02-06 21:19:08 +01:00
* currently only known to happen as an after - effect of ALTER TABLE
* SET WITHOUT OIDS .
*
* So , we must reconstruct the tuple from component Datums .
2002-05-21 01:51:44 +02:00
*/
2007-04-08 03:26:33 +02:00
heap_deform_tuple ( tuple , oldTupDesc , values , isnull ) ;
2005-02-06 21:19:08 +01:00
/* Be sure to null out any dropped columns */
for ( i = 0 ; i < natts ; i + + )
{
if ( newTupDesc - > attrs [ i ] - > attisdropped )
2007-04-08 03:26:33 +02:00
isnull [ i ] = true ;
2005-02-06 21:19:08 +01:00
}
2007-04-08 03:26:33 +02:00
copiedTuple = heap_form_tuple ( newTupDesc , values , isnull ) ;
2005-02-06 21:19:08 +01:00
/* Preserve OID, if any */
if ( NewHeap - > rd_rel - > relhasoids )
HeapTupleSetOid ( copiedTuple , HeapTupleGetOid ( tuple ) ) ;
2002-08-11 23:17:35 +02:00
2007-04-08 03:26:33 +02:00
/* The heap rewrite module does the rest */
rewrite_heap_tuple ( rwstate , tuple , copiedTuple ) ;
2002-05-21 01:51:44 +02:00
heap_freetuple ( copiedTuple ) ;
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
2007-04-08 03:26:33 +02:00
/* Write out any remaining tuples, and fsync if needed */
end_heap_rewrite ( rwstate ) ;
2005-02-06 21:19:08 +01:00
2007-04-08 03:26:33 +02:00
pfree ( values ) ;
pfree ( isnull ) ;
2007-03-29 02:15:39 +02:00
2006-07-31 22:09:10 +02:00
index_close ( OldIndex , NoLock ) ;
2002-08-11 23:17:35 +02:00
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
/*
2004-07-12 01:13:58 +02:00
* Swap the physical files of two given relations .
*
* We swap the physical identity ( reltablespace and relfilenode ) while
* keeping the same logical identities of the two relations .
2002-08-11 23:17:35 +02:00
*
* 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
*/
2004-05-05 06:48:48 +02:00
void
2004-07-12 01:13:58 +02:00
swap_relation_files ( Oid r1 , Oid r2 )
2002-08-10 22:43:46 +02:00
{
2005-03-20 23:00:54 +01:00
Relation relRelation ;
2002-08-11 23:17:35 +02:00
HeapTuple reltup1 ,
reltup2 ;
Form_pg_class relform1 ,
relform2 ;
Oid swaptemp ;
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. */
2005-04-14 22:03:27 +02:00
relRelation = heap_open ( RelationRelationId , RowExclusiveLock ) ;
2002-08-10 22:43:46 +02:00
2002-08-11 23:17:35 +02:00
reltup1 = SearchSysCacheCopy ( RELOID ,
ObjectIdGetDatum ( r1 ) ,
0 , 0 , 0 ) ;
if ( ! HeapTupleIsValid ( reltup1 ) )
2003-07-20 23:56:35 +02:00
elog ( ERROR , " cache lookup failed 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 ) )
2003-07-20 23:56:35 +02:00
elog ( ERROR , " cache lookup failed 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
/*
2004-07-12 01:13:58 +02:00
* Actually swap the fields in the two tuples
2002-08-11 23:17:35 +02:00
*/
swaptemp = relform1 - > relfilenode ;
relform1 - > relfilenode = relform2 - > relfilenode ;
relform2 - > relfilenode = swaptemp ;
2002-08-10 22:43:46 +02:00
2004-07-12 01:13:58 +02:00
swaptemp = relform1 - > reltablespace ;
relform1 - > reltablespace = relform2 - > reltablespace ;
relform2 - > reltablespace = swaptemp ;
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 */
{
2003-08-04 02:43:34 +02:00
int4 swap_pages ;
float4 swap_tuples ;
2002-11-02 22:20:40 +01:00
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
/*
2005-10-15 04:49:52 +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
*
2004-08-29 07:07:03 +02:00
* NOTE : it is possible that only one table has a toast table ; this can
2005-10-15 04:49:52 +02:00
* happen in CLUSTER if there were dropped columns in the old table , and
* in ALTER TABLE when adding or changing type of columns .
2002-08-11 23:17:35 +02:00
*
2005-11-22 19:17:34 +01: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
* more selective than deleteDependencyRecordsFor ( ) to get rid of only the
* link we want .
2002-08-11 23:17:35 +02:00
*/
if ( relform1 - > reltoastrelid | | relform2 - > reltoastrelid )
{
ObjectAddress baseobject ,
toastobject ;
long count ;
/* Delete old dependencies */
2004-05-05 06:48:48 +02:00
if ( relform1 - > reltoastrelid )
{
2005-04-14 03:38:22 +02:00
count = deleteDependencyRecordsFor ( RelationRelationId ,
2004-05-05 06:48:48 +02:00
relform1 - > reltoastrelid ) ;
if ( count ! = 1 )
elog ( ERROR , " expected one dependency record for TOAST table, found %ld " ,
count ) ;
}
if ( relform2 - > reltoastrelid )
{
2005-04-14 03:38:22 +02:00
count = deleteDependencyRecordsFor ( RelationRelationId ,
2004-05-05 06:48:48 +02:00
relform2 - > reltoastrelid ) ;
if ( count ! = 1 )
elog ( ERROR , " expected one dependency record for TOAST table, found %ld " ,
count ) ;
}
2002-08-11 23:17:35 +02:00
/* Register new dependencies */
2005-04-14 03:38:22 +02:00
baseobject . classId = RelationRelationId ;
2002-08-11 23:17:35 +02:00
baseobject . objectSubId = 0 ;
2005-04-14 03:38:22 +02:00
toastobject . classId = RelationRelationId ;
2002-08-11 23:17:35 +02:00
toastobject . objectSubId = 0 ;
2004-05-05 06:48:48 +02:00
if ( relform1 - > reltoastrelid )
{
baseobject . objectId = r1 ;
toastobject . objectId = relform1 - > reltoastrelid ;
recordDependencyOn ( & toastobject , & baseobject , DEPENDENCY_INTERNAL ) ;
}
2002-08-11 23:17:35 +02:00
2004-05-05 06:48:48 +02:00
if ( relform2 - > reltoastrelid )
{
baseobject . objectId = r2 ;
toastobject . objectId = relform2 - > reltoastrelid ;
recordDependencyOn ( & toastobject , & baseobject , DEPENDENCY_INTERNAL ) ;
}
2002-08-11 23:17:35 +02:00
}
/*
2002-09-04 22:31:48 +02:00
* Blow away the old relcache entries now . We need this kluge because
2005-10-15 04:49:52 +02:00
* relcache . c keeps a link to the smgr relation for the physical file , and
* that will be out of date as soon as we do CommandCounterIncrement .
* Whichever of the rels is the second to be cleared during cache
* invalidation will have a dangling reference to an already - deleted smgr
* relation . Rather than trying to avoid this by ordering operations just
* so , it ' s easiest to not have the relcache entries there at all .
* ( Fortunately , since one of the entries is local in our transaction ,
* it ' s sufficient to clear out our own relcache this way ; the problem
* cannot arise for other backends when they see our update on the
* non - local relation . )
2002-08-11 23:17:35 +02:00
*/
RelationForgetRelation ( r1 ) ;
RelationForgetRelation ( r2 ) ;
/* Clean up. */
heap_freetuple ( reltup1 ) ;
heap_freetuple ( reltup2 ) ;
heap_close ( relRelation , RowExclusiveLock ) ;
2002-08-10 22:43:46 +02:00
}
2002-11-15 04:09:39 +01:00
2002-12-30 19:42:17 +01:00
/*
* Get a list of tables that the current user owns and
2002-11-15 04:09:39 +01:00
* have indisclustered set . Return the list in a List * of rvsToCluster
2002-12-04 06:18:38 +01:00
* with the tableOid and the indexOid on which the table is already
2002-11-15 04:09:39 +01:00
* clustered .
*/
2002-12-30 19:42:17 +01:00
static List *
get_tables_to_cluster ( MemoryContext cluster_context )
2002-11-15 04:09:39 +01:00
{
2003-08-04 02:43:34 +02:00
Relation indRelation ;
HeapScanDesc scan ;
ScanKeyData entry ;
HeapTuple indexTuple ;
Form_pg_index index ;
MemoryContext old_context ;
RelToCluster * rvtc ;
List * rvs = NIL ;
2002-11-15 04:09:39 +01:00
/*
2002-12-30 19:42:17 +01:00
* Get all indexes that have indisclustered set and are owned by
2005-10-15 04:49:52 +02:00
* appropriate user . System relations or nailed - in relations cannot ever
* have indisclustered set , because CLUSTER will refuse to set it when
* called with one of them as argument .
2002-11-15 04:09:39 +01:00
*/
2005-04-14 22:03:27 +02:00
indRelation = heap_open ( IndexRelationId , AccessShareLock ) ;
2003-11-12 22:15:59 +01:00
ScanKeyInit ( & entry ,
Anum_pg_index_indisclustered ,
BTEqualStrategyNumber , F_BOOLEQ ,
BoolGetDatum ( true ) ) ;
2002-11-15 04:09:39 +01:00
scan = heap_beginscan ( indRelation , SnapshotNow , 1 , & entry ) ;
while ( ( indexTuple = heap_getnext ( scan , ForwardScanDirection ) ) ! = NULL )
{
index = ( Form_pg_index ) GETSTRUCT ( indexTuple ) ;
2003-08-01 02:15:26 +02:00
if ( ! pg_class_ownercheck ( index - > indrelid , GetUserId ( ) ) )
2002-11-15 04:09:39 +01:00
continue ;
/*
2005-10-15 04:49:52 +02:00
* We have to build the list in a different memory context so it will
* survive the cross - transaction processing
2002-11-15 04:09:39 +01:00
*/
old_context = MemoryContextSwitchTo ( cluster_context ) ;
2002-12-30 19:42:17 +01:00
rvtc = ( RelToCluster * ) palloc ( sizeof ( RelToCluster ) ) ;
2002-11-15 04:09:39 +01:00
rvtc - > tableOid = index - > indrelid ;
2002-12-30 19:42:17 +01:00
rvtc - > indexOid = index - > indexrelid ;
rvs = lcons ( rvtc , rvs ) ;
2002-11-15 04:09:39 +01:00
MemoryContextSwitchTo ( old_context ) ;
}
heap_endscan ( scan ) ;
2002-12-30 19:42:17 +01:00
relation_close ( indRelation , AccessShareLock ) ;
2002-11-15 04:09:39 +01:00
return rvs ;
}