Code review of CLUSTER patch. Clean up problems with relcache getting
confused, toasted data getting lost, etc.
This commit is contained in:
parent
9bccdf17f7
commit
e44beef712
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.145 2002/08/02 18:15:04 tgl Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.146 2002/08/11 21:17:34 tgl Exp $
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<appendix id="release">
|
<appendix id="release">
|
||||||
|
@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
|
||||||
worries about funny characters.
|
worries about funny characters.
|
||||||
-->
|
-->
|
||||||
<literallayout><![CDATA[
|
<literallayout><![CDATA[
|
||||||
|
CLUSTER is no longer hazardous to your schema
|
||||||
COPY accepts a list of columns to copy
|
COPY accepts a list of columns to copy
|
||||||
ALTER TABLE DROP COLUMN
|
ALTER TABLE DROP COLUMN
|
||||||
CREATE OPERATOR CLASS/DROP OPERATOR CLASS
|
CREATE OPERATOR CLASS/DROP OPERATOR CLASS
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.219 2002/08/06 02:36:33 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.220 2002/08/11 21:17:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
|
@ -198,7 +198,8 @@ SystemAttributeByName(const char *attname, bool relhasoids)
|
||||||
* Remove the system relation specific code to elsewhere eventually.
|
* Remove the system relation specific code to elsewhere eventually.
|
||||||
*
|
*
|
||||||
* If storage_create is TRUE then heap_storage_create is called here,
|
* If storage_create is TRUE then heap_storage_create is called here,
|
||||||
* else caller must call heap_storage_create later.
|
* else caller must call heap_storage_create later (or not at all,
|
||||||
|
* if the relation doesn't need physical storage).
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
Relation
|
Relation
|
||||||
|
@ -291,7 +292,7 @@ heap_create(const char *relname,
|
||||||
nailme);
|
nailme);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* have the storage manager create the relation.
|
* have the storage manager create the relation's disk file, if wanted.
|
||||||
*/
|
*/
|
||||||
if (storage_create)
|
if (storage_create)
|
||||||
heap_storage_create(rel);
|
heap_storage_create(rel);
|
||||||
|
@ -684,20 +685,21 @@ heap_create_with_catalog(const char *relname,
|
||||||
tupdesc->tdhasoid = BoolToHasOid(relhasoids);
|
tupdesc->tdhasoid = BoolToHasOid(relhasoids);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell heap_create not to create a physical file; we'll do that below
|
* Create the relcache entry (mostly dummy at this point) and the
|
||||||
* after all our catalog updates are done. (This isn't really
|
* physical disk file. (If we fail further down, it's the smgr's
|
||||||
* necessary anymore, but we may as well avoid the cycles of creating
|
* responsibility to remove the disk file again.)
|
||||||
* and deleting the file in case we fail.)
|
*
|
||||||
|
* NB: create a physical file only if it's not a view.
|
||||||
*/
|
*/
|
||||||
new_rel_desc = heap_create(relname,
|
new_rel_desc = heap_create(relname,
|
||||||
relnamespace,
|
relnamespace,
|
||||||
tupdesc,
|
tupdesc,
|
||||||
shared_relation,
|
shared_relation,
|
||||||
false,
|
(relkind != RELKIND_VIEW),
|
||||||
allow_system_table_mods);
|
allow_system_table_mods);
|
||||||
|
|
||||||
/* Fetch the relation OID assigned by heap_create */
|
/* Fetch the relation OID assigned by heap_create */
|
||||||
new_rel_oid = new_rel_desc->rd_att->attrs[0]->attrelid;
|
new_rel_oid = RelationGetRelid(new_rel_desc);
|
||||||
|
|
||||||
/* Assign an OID for the relation's tuple type */
|
/* Assign an OID for the relation's tuple type */
|
||||||
new_type_oid = newoid();
|
new_type_oid = newoid();
|
||||||
|
@ -761,12 +763,6 @@ heap_create_with_catalog(const char *relname,
|
||||||
*/
|
*/
|
||||||
StoreConstraints(new_rel_desc, tupdesc);
|
StoreConstraints(new_rel_desc, tupdesc);
|
||||||
|
|
||||||
/*
|
|
||||||
* We create the disk file for this relation here
|
|
||||||
*/
|
|
||||||
if (relkind != RELKIND_VIEW)
|
|
||||||
heap_storage_create(new_rel_desc);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ok, the relation has been cataloged, so close our relations and
|
* ok, the relation has been cataloged, so close our relations and
|
||||||
* return the oid of the newly created relation.
|
* return the oid of the newly created relation.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.188 2002/08/05 03:29:16 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.189 2002/08/11 21:17:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
|
@ -578,14 +578,18 @@ index_create(Oid heapRelationId,
|
||||||
|
|
||||||
indexTupDesc->tdhasoid = WITHOUTOID;
|
indexTupDesc->tdhasoid = WITHOUTOID;
|
||||||
/*
|
/*
|
||||||
* create the index relation (but don't create storage yet)
|
* create the index relation's relcache entry and physical disk file.
|
||||||
|
* (If we fail further down, it's the smgr's responsibility to remove
|
||||||
|
* the disk file again.)
|
||||||
*/
|
*/
|
||||||
indexRelation = heap_create(indexRelationName,
|
indexRelation = heap_create(indexRelationName,
|
||||||
namespaceId,
|
namespaceId,
|
||||||
indexTupDesc,
|
indexTupDesc,
|
||||||
shared_relation,
|
shared_relation,
|
||||||
false,
|
true,
|
||||||
allow_system_table_mods);
|
allow_system_table_mods);
|
||||||
|
|
||||||
|
/* Fetch the relation OID assigned by heap_create */
|
||||||
indexoid = RelationGetRelid(indexRelation);
|
indexoid = RelationGetRelid(indexRelation);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -611,11 +615,6 @@ index_create(Oid heapRelationId,
|
||||||
*/
|
*/
|
||||||
UpdateRelationRelation(indexRelation);
|
UpdateRelationRelation(indexRelation);
|
||||||
|
|
||||||
/*
|
|
||||||
* We create the disk file for this relation here
|
|
||||||
*/
|
|
||||||
heap_storage_create(indexRelation);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* now update the object id's of all the attribute tuple forms in the
|
* now update the object id's of all the attribute tuple forms in the
|
||||||
* index relation's tuple descriptor
|
* index relation's tuple descriptor
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.4 2002/08/05 03:29:16 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.5 2002/08/11 21:17:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -121,15 +121,16 @@ recordMultipleDependencies(const ObjectAddress *depender,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* deleteDependencyRecordsFor -- delete all records with given depender
|
* deleteDependencyRecordsFor -- delete all records with given depender
|
||||||
* classId/objectId.
|
* classId/objectId. Returns the number of records deleted.
|
||||||
*
|
*
|
||||||
* This is used when redefining an existing object. Links leading to the
|
* This is used when redefining an existing object. Links leading to the
|
||||||
* object do not change, and links leading from it will be recreated
|
* object do not change, and links leading from it will be recreated
|
||||||
* (possibly with some differences from before).
|
* (possibly with some differences from before).
|
||||||
*/
|
*/
|
||||||
void
|
long
|
||||||
deleteDependencyRecordsFor(Oid classId, Oid objectId)
|
deleteDependencyRecordsFor(Oid classId, Oid objectId)
|
||||||
{
|
{
|
||||||
|
long count = 0;
|
||||||
Relation depRel;
|
Relation depRel;
|
||||||
ScanKeyData key[2];
|
ScanKeyData key[2];
|
||||||
SysScanDesc scan;
|
SysScanDesc scan;
|
||||||
|
@ -150,11 +151,14 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
|
||||||
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
{
|
{
|
||||||
simple_heap_delete(depRel, &tup->t_self);
|
simple_heap_delete(depRel, &tup->t_self);
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(scan);
|
systable_endscan(scan);
|
||||||
|
|
||||||
heap_close(depRel, RowExclusiveLock);
|
heap_close(depRel, RowExclusiveLock);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* cluster.c
|
* cluster.c
|
||||||
* Paul Brown's implementation of cluster index.
|
* CLUSTER a table on an index.
|
||||||
|
*
|
||||||
|
* There is hardly anything left of Paul Brown's original implementation...
|
||||||
*
|
*
|
||||||
* 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]
|
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.85 2002/08/10 21:00:34 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.86 2002/08/11 21:17:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
|
@ -40,25 +36,23 @@
|
||||||
/*
|
/*
|
||||||
* We need one of these structs for each index in the relation to be
|
* 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
|
* clustered. It's basically the data needed by index_create() so
|
||||||
* we can recreate the indexes after destroying the old heap.
|
* we can rebuild the indexes on the new heap.
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
Oid indexOID;
|
||||||
char *indexName;
|
char *indexName;
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
Oid accessMethodOID;
|
Oid accessMethodOID;
|
||||||
Oid *classOID;
|
Oid *classOID;
|
||||||
Oid indexOID;
|
|
||||||
bool isPrimary;
|
|
||||||
} IndexAttrs;
|
} IndexAttrs;
|
||||||
|
|
||||||
static Oid copy_heap(Oid OIDOldHeap, const char *NewName);
|
static Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
|
||||||
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
|
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
|
||||||
static List *get_indexattr_list (Oid OIDOldHeap);
|
static List *get_indexattr_list(Relation OldHeap);
|
||||||
static void recreate_indexattr(Oid OIDOldHeap, List *indexes);
|
static void recreate_indexattr(Oid OIDOldHeap, List *indexes);
|
||||||
static void swap_relfilenodes(Oid r1, Oid r2);
|
static void swap_relfilenodes(Oid r1, Oid r2);
|
||||||
|
|
||||||
Relation RelationIdGetRelation(Oid relationId);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cluster
|
* cluster
|
||||||
|
@ -67,17 +61,14 @@ Relation RelationIdGetRelation(Oid relationId);
|
||||||
* swapping the relfilenodes of the new table and the old table, so
|
* swapping the relfilenodes of the new table and the old table, so
|
||||||
* the OID of the original table is preserved. Thus we do not lose
|
* the OID of the original table is preserved. Thus we do not lose
|
||||||
* GRANT, inheritance nor references to this table (this was a bug
|
* GRANT, inheritance nor references to this table (this was a bug
|
||||||
* in releases thru 7.3)
|
* in releases thru 7.3).
|
||||||
*
|
*
|
||||||
* Also create new indexes and swap the filenodes with the old indexes
|
* Also create new indexes and swap the filenodes with the old indexes the
|
||||||
* the same way we do for the relation.
|
* 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.
|
||||||
*
|
*
|
||||||
* TODO:
|
* Permissions checks were done already.
|
||||||
* maybe we can get away with AccessShareLock for the table.
|
|
||||||
* Concurrency would be much improved. Only acquire
|
|
||||||
* AccessExclusiveLock right before swapping the filenodes.
|
|
||||||
* This would allow users to CLUSTER on a regular basis,
|
|
||||||
* practically eliminating the need for auto-clustered indexes.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
cluster(RangeVar *oldrelation, char *oldindexname)
|
cluster(RangeVar *oldrelation, char *oldindexname)
|
||||||
|
@ -105,43 +96,60 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
||||||
RelationGetNamespace(OldHeap));
|
RelationGetNamespace(OldHeap));
|
||||||
if (!OidIsValid(OIDOldIndex))
|
if (!OidIsValid(OIDOldIndex))
|
||||||
elog(ERROR, "CLUSTER: cannot find index \"%s\" for table \"%s\"",
|
elog(ERROR, "CLUSTER: cannot find index \"%s\" for table \"%s\"",
|
||||||
oldindexname, oldrelation->relname);
|
oldindexname, RelationGetRelationName(OldHeap));
|
||||||
OldIndex = index_open(OIDOldIndex);
|
OldIndex = index_open(OIDOldIndex);
|
||||||
LockRelation(OldIndex, AccessExclusiveLock);
|
LockRelation(OldIndex, AccessExclusiveLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that index is in fact an index on the given relation
|
* Check that index is in fact an index on the given relation
|
||||||
*/
|
*/
|
||||||
if (OldIndex->rd_index->indrelid != OIDOldHeap)
|
if (OldIndex->rd_index == NULL ||
|
||||||
|
OldIndex->rd_index->indrelid != OIDOldHeap)
|
||||||
elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
|
elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
|
||||||
oldindexname, oldrelation->relname);
|
RelationGetRelationName(OldIndex),
|
||||||
|
RelationGetRelationName(OldHeap));
|
||||||
/* Drop relcache refcnts, but do NOT give up the locks */
|
|
||||||
heap_close(OldHeap, NoLock);
|
|
||||||
index_close(OldIndex);
|
|
||||||
|
|
||||||
/* Save the information of all indexes on the relation. */
|
|
||||||
indexes = get_indexattr_list(OIDOldHeap);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the new heap with a temporary name.
|
* 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.
|
||||||
*/
|
*/
|
||||||
snprintf(NewHeapName, NAMEDATALEN, "temp_%u", OIDOldHeap);
|
if (IsSystemRelation(OldHeap))
|
||||||
|
elog(ERROR, "CLUSTER: cannot cluster system relation \"%s\"",
|
||||||
|
RelationGetRelationName(OldHeap));
|
||||||
|
|
||||||
OIDNewHeap = copy_heap(OIDOldHeap, NewHeapName);
|
/* Save the information of all indexes on the relation. */
|
||||||
|
indexes = get_indexattr_list(OldHeap);
|
||||||
|
|
||||||
/* We do not need CommandCounterIncrement() because copy_heap did it. */
|
/* Drop relcache refcnts, but do NOT give up the locks */
|
||||||
|
index_close(OldIndex);
|
||||||
|
heap_close(OldHeap, NoLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", OIDOldHeap);
|
||||||
|
|
||||||
|
OIDNewHeap = make_new_heap(OIDOldHeap, NewHeapName);
|
||||||
|
|
||||||
|
/* We don't need CommandCounterIncrement() because make_new_heap did it. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the heap data into the new table in the desired order.
|
* Copy the heap data into the new table in the desired order.
|
||||||
*/
|
*/
|
||||||
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
|
copy_heap_data(OIDNewHeap, OIDOldHeap, OIDOldIndex);
|
||||||
|
|
||||||
/* To make the new heap's data visible. */
|
/* To make the new heap's data visible (probably not needed?). */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Swap the relfilenodes of the old and new heaps. */
|
/* Swap the relfilenodes of the old and new heaps. */
|
||||||
swap_relfilenodes(OIDNewHeap, OIDOldHeap);
|
swap_relfilenodes(OIDOldHeap, OIDNewHeap);
|
||||||
|
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
@ -150,21 +158,26 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
||||||
object.objectId = OIDNewHeap;
|
object.objectId = OIDNewHeap;
|
||||||
object.objectSubId = 0;
|
object.objectSubId = 0;
|
||||||
|
|
||||||
/* The relation is local to our transaction and we know nothin
|
/*
|
||||||
|
* The new relation is local to our transaction and we know nothing
|
||||||
* depends on it, so DROP_RESTRICT should be OK.
|
* depends on it, so DROP_RESTRICT should be OK.
|
||||||
*/
|
*/
|
||||||
performDeletion(&object, DROP_RESTRICT);
|
performDeletion(&object, DROP_RESTRICT);
|
||||||
|
|
||||||
/* performDeletion does CommandCounterIncrement at end */
|
/* performDeletion does CommandCounterIncrement at end */
|
||||||
|
|
||||||
/* Recreate the indexes on the relation. We do not need
|
/*
|
||||||
* CommandCounterIncrement() because recreate_indexattr does it.
|
* Recreate each index on the relation. We do not need
|
||||||
*/
|
* CommandCounterIncrement() because recreate_indexattr does it.
|
||||||
recreate_indexattr(OIDOldHeap, indexes);
|
*/
|
||||||
|
recreate_indexattr(OIDOldHeap, indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the new table that we will fill with correctly-ordered data.
|
||||||
|
*/
|
||||||
static Oid
|
static Oid
|
||||||
copy_heap(Oid OIDOldHeap, const char *NewName)
|
make_new_heap(Oid OIDOldHeap, const char *NewName)
|
||||||
{
|
{
|
||||||
TupleDesc OldHeapDesc,
|
TupleDesc OldHeapDesc,
|
||||||
tupdesc;
|
tupdesc;
|
||||||
|
@ -206,27 +219,29 @@ copy_heap(Oid OIDOldHeap, const char *NewName)
|
||||||
return OIDNewHeap;
|
return OIDNewHeap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the physical copying of heap data.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
||||||
{
|
{
|
||||||
Relation LocalNewHeap,
|
Relation NewHeap,
|
||||||
LocalOldHeap,
|
OldHeap,
|
||||||
LocalOldIndex;
|
OldIndex;
|
||||||
IndexScanDesc ScanDesc;
|
IndexScanDesc scan;
|
||||||
HeapTuple LocalHeapTuple;
|
HeapTuple tuple;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the relations I need. Scan through the OldHeap on the OldIndex
|
* Open the relations I need. Scan through the OldHeap on the OldIndex
|
||||||
* and insert each tuple into the NewHeap.
|
* and insert each tuple into the NewHeap.
|
||||||
*/
|
*/
|
||||||
LocalNewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
|
||||||
LocalOldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
|
||||||
LocalOldIndex = index_open(OIDOldIndex);
|
OldIndex = index_open(OIDOldIndex);
|
||||||
|
|
||||||
ScanDesc = index_beginscan(LocalOldHeap, LocalOldIndex,
|
scan = index_beginscan(OldHeap, OldIndex, SnapshotNow, 0, (ScanKey) NULL);
|
||||||
SnapshotNow, 0, (ScanKey) NULL);
|
|
||||||
|
|
||||||
while ((LocalHeapTuple = index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
|
while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We must copy the tuple because heap_insert() will overwrite
|
* We must copy the tuple because heap_insert() will overwrite
|
||||||
|
@ -234,199 +249,280 @@ rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
||||||
* retrieved tuple will actually be in a disk buffer! Thus,
|
* retrieved tuple will actually be in a disk buffer! Thus,
|
||||||
* the source relation would get trashed, which is bad news if
|
* the source relation would get trashed, which is bad news if
|
||||||
* we abort later on. (This was a bug in releases thru 7.0)
|
* we abort later on. (This was a bug in releases thru 7.0)
|
||||||
|
*
|
||||||
|
* Note that the copied tuple will have the original OID, if any,
|
||||||
|
* so this does preserve OIDs.
|
||||||
*/
|
*/
|
||||||
HeapTuple copiedTuple = heap_copytuple(LocalHeapTuple);
|
HeapTuple copiedTuple = heap_copytuple(tuple);
|
||||||
|
|
||||||
|
simple_heap_insert(NewHeap, copiedTuple);
|
||||||
|
|
||||||
simple_heap_insert(LocalNewHeap, copiedTuple);
|
|
||||||
heap_freetuple(copiedTuple);
|
heap_freetuple(copiedTuple);
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
}
|
}
|
||||||
|
|
||||||
index_endscan(ScanDesc);
|
index_endscan(scan);
|
||||||
|
|
||||||
index_close(LocalOldIndex);
|
index_close(OldIndex);
|
||||||
heap_close(LocalOldHeap, NoLock);
|
heap_close(OldHeap, NoLock);
|
||||||
heap_close(LocalNewHeap, NoLock);
|
heap_close(NewHeap, NoLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the necessary info about the indexes in the relation and
|
/*
|
||||||
* return a List of IndexAttrs.
|
* Get the necessary info about the indexes of the relation and
|
||||||
|
* return a list of IndexAttrs structures.
|
||||||
*/
|
*/
|
||||||
List *
|
static List *
|
||||||
get_indexattr_list (Oid OIDOldHeap)
|
get_indexattr_list(Relation OldHeap)
|
||||||
{
|
{
|
||||||
ScanKeyData entry;
|
|
||||||
HeapScanDesc scan;
|
|
||||||
Relation indexRelation;
|
|
||||||
HeapTuple indexTuple;
|
|
||||||
List *indexes = NIL;
|
List *indexes = NIL;
|
||||||
IndexAttrs *attrs;
|
List *indlist;
|
||||||
HeapTuple tuple;
|
|
||||||
Form_pg_index index;
|
/* Ask the relcache to produce a list of the indexes of the old rel */
|
||||||
|
foreach(indlist, RelationGetIndexList(OldHeap))
|
||||||
/* Grab the index tuples by looking into RelationRelationName
|
|
||||||
* by the OID of the old heap.
|
|
||||||
*/
|
|
||||||
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
|
|
||||||
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
|
|
||||||
F_OIDEQ, ObjectIdGetDatum(OIDOldHeap));
|
|
||||||
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
|
|
||||||
while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
||||||
{
|
{
|
||||||
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
Oid indexOID = (Oid) lfirsti(indlist);
|
||||||
|
HeapTuple indexTuple;
|
||||||
|
HeapTuple classTuple;
|
||||||
|
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);
|
||||||
|
|
||||||
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
|
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
|
||||||
attrs->indexInfo = BuildIndexInfo(index);
|
attrs->indexOID = indexOID;
|
||||||
attrs->isPrimary = index->indisprimary;
|
attrs->indexInfo = BuildIndexInfo(indexForm);
|
||||||
attrs->indexOID = index->indexrelid;
|
attrs->classOID = (Oid *)
|
||||||
|
palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
||||||
|
memcpy(attrs->classOID, indexForm->indclass,
|
||||||
|
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
||||||
|
|
||||||
/* The opclasses are copied verbatim from the original indexes.
|
/* Name and access method of each index come from pg_class */
|
||||||
*/
|
classTuple = SearchSysCache(RELOID,
|
||||||
attrs->classOID = (Oid *)palloc(sizeof(Oid) *
|
ObjectIdGetDatum(indexOID),
|
||||||
attrs->indexInfo->ii_NumIndexAttrs);
|
0, 0, 0);
|
||||||
memcpy(attrs->classOID, index->indclass,
|
if (!HeapTupleIsValid(classTuple))
|
||||||
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
elog(ERROR, "Cache lookup failed for index %u", indexOID);
|
||||||
|
classForm = (Form_pg_class) GETSTRUCT(classTuple);
|
||||||
|
|
||||||
/* Name and access method of each index come from
|
attrs->indexName = pstrdup(NameStr(classForm->relname));
|
||||||
* RelationRelationName.
|
attrs->accessMethodOID = classForm->relam;
|
||||||
*/
|
|
||||||
tuple = SearchSysCache(RELOID,
|
ReleaseSysCache(classTuple);
|
||||||
ObjectIdGetDatum(attrs->indexOID),
|
ReleaseSysCache(indexTuple);
|
||||||
0, 0, 0);
|
|
||||||
if (!HeapTupleIsValid(tuple))
|
|
||||||
elog(ERROR, "CLUSTER: cannot find index %u", attrs->indexOID);
|
|
||||||
attrs->indexName = pstrdup(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname));
|
|
||||||
attrs->accessMethodOID = ((Form_pg_class) GETSTRUCT(tuple))->relam;
|
|
||||||
ReleaseSysCache(tuple);
|
|
||||||
|
|
||||||
/* Cons the gathered data into the list. We do not care about
|
/* Cons the gathered data into the list. We do not care about
|
||||||
* ordering, and this is more efficient than append.
|
* ordering, and this is more efficient than append.
|
||||||
*/
|
*/
|
||||||
indexes=lcons((void *)attrs, indexes);
|
indexes = lcons(attrs, indexes);
|
||||||
}
|
}
|
||||||
heap_endscan(scan);
|
|
||||||
heap_close(indexRelation, AccessShareLock);
|
|
||||||
return indexes;
|
return indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create new indexes and swap the filenodes with old indexes. Then drop
|
/*
|
||||||
* the new index (carrying the old heap along).
|
* Create new indexes and swap the filenodes with old indexes. Then drop
|
||||||
|
* the new index (carrying the old index filenode along).
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
recreate_indexattr(Oid OIDOldHeap, List *indexes)
|
recreate_indexattr(Oid OIDOldHeap, List *indexes)
|
||||||
{
|
{
|
||||||
IndexAttrs *attrs;
|
|
||||||
List *elem;
|
List *elem;
|
||||||
Oid newIndexOID;
|
|
||||||
char newIndexName[NAMEDATALEN];
|
|
||||||
ObjectAddress object;
|
|
||||||
|
|
||||||
foreach (elem, indexes)
|
foreach(elem, indexes)
|
||||||
{
|
{
|
||||||
attrs=(IndexAttrs *) lfirst(elem);
|
IndexAttrs *attrs = (IndexAttrs *) lfirst(elem);
|
||||||
|
Oid newIndexOID;
|
||||||
|
char newIndexName[NAMEDATALEN];
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
/* Create the new index under a temporary name */
|
/* Create the new index under a temporary name */
|
||||||
snprintf(newIndexName, NAMEDATALEN, "temp_%u", attrs->indexOID);
|
snprintf(newIndexName, NAMEDATALEN, "pg_temp_%u", attrs->indexOID);
|
||||||
|
|
||||||
/* The new index will have constraint status set to false,
|
/*
|
||||||
|
* The new index will have primary and constraint status set to false,
|
||||||
* but since we will only use its filenode it doesn't matter:
|
* but since we will only use its filenode it doesn't matter:
|
||||||
* after the filenode swap the index will keep the constraint
|
* after the filenode swap the index will keep the constraint
|
||||||
* status of the old index.
|
* status of the old index.
|
||||||
*/
|
*/
|
||||||
newIndexOID = index_create(OIDOldHeap, newIndexName,
|
newIndexOID = index_create(OIDOldHeap, newIndexName,
|
||||||
attrs->indexInfo, attrs->accessMethodOID,
|
attrs->indexInfo, attrs->accessMethodOID,
|
||||||
attrs->classOID, attrs->isPrimary,
|
attrs->classOID, false,
|
||||||
false, allowSystemTableMods);
|
false, allowSystemTableMods);
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Swap the filenodes. */
|
/* Swap the filenodes. */
|
||||||
swap_relfilenodes(newIndexOID, attrs->indexOID);
|
swap_relfilenodes(attrs->indexOID, newIndexOID);
|
||||||
setRelhasindex(OIDOldHeap, true, attrs->isPrimary, InvalidOid);
|
|
||||||
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Destroy new index with old filenode */
|
/* Destroy new index with old filenode */
|
||||||
object.classId = RelOid_pg_class;
|
object.classId = RelOid_pg_class;
|
||||||
object.objectId = newIndexOID;
|
object.objectId = newIndexOID;
|
||||||
object.objectSubId = 0;
|
object.objectSubId = 0;
|
||||||
|
|
||||||
/* The relation is local to our transaction and we know
|
/*
|
||||||
|
* The relation is local to our transaction and we know
|
||||||
* nothing depends on it, so DROP_RESTRICT should be OK.
|
* nothing depends on it, so DROP_RESTRICT should be OK.
|
||||||
*/
|
*/
|
||||||
performDeletion(&object, DROP_RESTRICT);
|
performDeletion(&object, DROP_RESTRICT);
|
||||||
|
|
||||||
/* performDeletion does CommandCounterIncrement() at its end */
|
/* performDeletion does CommandCounterIncrement() at its end */
|
||||||
|
|
||||||
pfree(attrs->classOID);
|
|
||||||
pfree(attrs);
|
|
||||||
}
|
}
|
||||||
freeList(indexes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Swap the relfilenodes for two given relations.
|
/*
|
||||||
|
* Swap the relfilenodes for two given relations.
|
||||||
|
*
|
||||||
|
* Also swap any TOAST links, so that the toast data moves along with
|
||||||
|
* the main-table data.
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
swap_relfilenodes(Oid r1, Oid r2)
|
swap_relfilenodes(Oid r1, Oid r2)
|
||||||
{
|
{
|
||||||
/* I can probably keep RelationRelationName open in the main
|
|
||||||
* function and pass the Relation around so I don't have to open
|
|
||||||
* it every time.
|
|
||||||
*/
|
|
||||||
Relation relRelation,
|
Relation relRelation,
|
||||||
rel;
|
rel;
|
||||||
HeapTuple reltup[2];
|
HeapTuple reltup1,
|
||||||
Oid tempRFNode;
|
reltup2;
|
||||||
|
Form_pg_class relform1,
|
||||||
|
relform2;
|
||||||
|
Oid swaptemp;
|
||||||
int i;
|
int i;
|
||||||
CatalogIndexState indstate;
|
CatalogIndexState indstate;
|
||||||
|
|
||||||
/* We need both RelationRelationName tuples. */
|
/* We need writable copies of both pg_class tuples. */
|
||||||
relRelation = heap_openr(RelationRelationName, RowExclusiveLock);
|
relRelation = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||||
|
|
||||||
reltup[0] = SearchSysCacheCopy(RELOID,
|
reltup1 = SearchSysCacheCopy(RELOID,
|
||||||
ObjectIdGetDatum(r1),
|
ObjectIdGetDatum(r1),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(reltup[0]))
|
if (!HeapTupleIsValid(reltup1))
|
||||||
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r1);
|
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r1);
|
||||||
reltup[1] = SearchSysCacheCopy(RELOID,
|
relform1 = (Form_pg_class) GETSTRUCT(reltup1);
|
||||||
ObjectIdGetDatum(r2),
|
|
||||||
0, 0, 0);
|
|
||||||
if (!HeapTupleIsValid(reltup[1]))
|
|
||||||
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r2);
|
|
||||||
|
|
||||||
/* The buffer manager gets confused if we swap relfilenodes for
|
reltup2 = SearchSysCacheCopy(RELOID,
|
||||||
|
ObjectIdGetDatum(r2),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(reltup2))
|
||||||
|
elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r2);
|
||||||
|
relform2 = (Form_pg_class) GETSTRUCT(reltup2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The buffer manager gets confused if we swap relfilenodes for
|
||||||
* relations that are not both local or non-local to this transaction.
|
* relations that are not both local or non-local to this transaction.
|
||||||
* Flush the buffers on both relations so the buffer manager can
|
* Flush the buffers on both relations so the buffer manager can
|
||||||
* forget about'em.
|
* forget about'em. (XXX this might not be necessary anymore?)
|
||||||
*/
|
*/
|
||||||
|
rel = relation_open(r1, NoLock);
|
||||||
rel = RelationIdGetRelation(r1);
|
|
||||||
i = FlushRelationBuffers(rel, 0);
|
i = FlushRelationBuffers(rel, 0);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
||||||
RelationClose(rel);
|
relation_close(rel, NoLock);
|
||||||
rel = RelationIdGetRelation(r1);
|
|
||||||
|
rel = relation_open(r2, NoLock);
|
||||||
i = FlushRelationBuffers(rel, 0);
|
i = FlushRelationBuffers(rel, 0);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i);
|
||||||
RelationClose(rel);
|
relation_close(rel, NoLock);
|
||||||
|
|
||||||
/* Actually swap the filenodes */
|
/*
|
||||||
|
* Actually swap the filenode and TOAST fields in the two tuples
|
||||||
|
*/
|
||||||
|
swaptemp = relform1->relfilenode;
|
||||||
|
relform1->relfilenode = relform2->relfilenode;
|
||||||
|
relform2->relfilenode = swaptemp;
|
||||||
|
|
||||||
tempRFNode = ((Form_pg_class) GETSTRUCT(reltup[0]))->relfilenode;
|
swaptemp = relform1->reltoastrelid;
|
||||||
((Form_pg_class) GETSTRUCT(reltup[0]))->relfilenode =
|
relform1->reltoastrelid = relform2->reltoastrelid;
|
||||||
((Form_pg_class) GETSTRUCT(reltup[1]))->relfilenode;
|
relform2->reltoastrelid = swaptemp;
|
||||||
((Form_pg_class) GETSTRUCT(reltup[1]))->relfilenode = tempRFNode;
|
|
||||||
|
|
||||||
/* Update the RelationRelationName tuples */
|
/* we should not swap reltoastidxid */
|
||||||
simple_heap_update(relRelation, &reltup[1]->t_self, reltup[1]);
|
|
||||||
simple_heap_update(relRelation, &reltup[0]->t_self, reltup[0]);
|
/* Update the tuples in pg_class */
|
||||||
|
simple_heap_update(relRelation, &reltup1->t_self, reltup1);
|
||||||
|
simple_heap_update(relRelation, &reltup2->t_self, reltup2);
|
||||||
|
|
||||||
/* To keep system catalogs current. */
|
/* Keep system catalogs current */
|
||||||
indstate = CatalogOpenIndexes(relRelation);
|
indstate = CatalogOpenIndexes(relRelation);
|
||||||
CatalogIndexInsert(indstate, reltup[1]);
|
CatalogIndexInsert(indstate, reltup1);
|
||||||
CatalogIndexInsert(indstate, reltup[0]);
|
CatalogIndexInsert(indstate, reltup2);
|
||||||
CatalogCloseIndexes(indstate);
|
CatalogCloseIndexes(indstate);
|
||||||
|
|
||||||
heap_close(relRelation, NoLock);
|
/*
|
||||||
heap_freetuple(reltup[0]);
|
* If we have toast tables associated with the relations being swapped,
|
||||||
heap_freetuple(reltup[1]);
|
* change their dependency links to re-associate them with their new
|
||||||
|
* owning relations. Otherwise the wrong one will get dropped ...
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* rd_node value when there is still another entry laying about with
|
||||||
|
* that same rd_node value. (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.)
|
||||||
|
*/
|
||||||
|
RelationForgetRelation(r1);
|
||||||
|
RelationForgetRelation(r2);
|
||||||
|
|
||||||
|
/* Clean up. */
|
||||||
|
heap_freetuple(reltup1);
|
||||||
|
heap_freetuple(reltup2);
|
||||||
|
|
||||||
|
heap_close(relRelation, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.128 2002/08/06 02:36:34 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.129 2002/08/11 21:17:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1038,11 +1038,6 @@ BufferReplace(BufferDesc *bufHdr)
|
||||||
* RelationGetNumberOfBlocks
|
* RelationGetNumberOfBlocks
|
||||||
* Determines the current number of pages in the relation.
|
* Determines the current number of pages in the relation.
|
||||||
* Side effect: relation->rd_nblocks is updated.
|
* Side effect: relation->rd_nblocks is updated.
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* XXX may fail for huge relations.
|
|
||||||
* XXX should be elsewhere.
|
|
||||||
* XXX maybe should be hidden
|
|
||||||
*/
|
*/
|
||||||
BlockNumber
|
BlockNumber
|
||||||
RelationGetNumberOfBlocks(Relation relation)
|
RelationGetNumberOfBlocks(Relation relation)
|
||||||
|
@ -1061,6 +1056,23 @@ RelationGetNumberOfBlocks(Relation relation)
|
||||||
return relation->rd_nblocks;
|
return relation->rd_nblocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RelationUpdateNumberOfBlocks
|
||||||
|
* Forcibly update relation->rd_nblocks.
|
||||||
|
*
|
||||||
|
* If the relcache drops an entry for a temp relation, it must call this
|
||||||
|
* routine after recreating the relcache entry, so that rd_nblocks is
|
||||||
|
* re-sync'd with reality. See RelationGetNumberOfBlocks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RelationUpdateNumberOfBlocks(Relation relation)
|
||||||
|
{
|
||||||
|
if (relation->rd_rel->relkind == RELKIND_VIEW)
|
||||||
|
relation->rd_nblocks = 0;
|
||||||
|
else
|
||||||
|
relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* DropRelationBuffers
|
* DropRelationBuffers
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.171 2002/08/06 02:36:35 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.172 2002/08/11 21:17:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -860,11 +860,12 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* normal relations are not nailed into the cache; nor can a pre-existing
|
* normal relations are not nailed into the cache; nor can a pre-existing
|
||||||
* relation be new or temp.
|
* relation be new. It could be temp though. (Actually, it could be new
|
||||||
|
* too, but it's okay to forget that fact if forced to flush the entry.)
|
||||||
*/
|
*/
|
||||||
relation->rd_isnailed = false;
|
relation->rd_isnailed = false;
|
||||||
relation->rd_isnew = false;
|
relation->rd_isnew = false;
|
||||||
relation->rd_istemp = false;
|
relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize the tuple descriptor (relation->rd_att).
|
* initialize the tuple descriptor (relation->rd_att).
|
||||||
|
@ -909,13 +910,23 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
||||||
relation->rd_fd = -1;
|
relation->rd_fd = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* insert newly created relation into proper relcaches, restore memory
|
* Insert newly created relation into relcache hash tables.
|
||||||
* context and return the new reldesc.
|
|
||||||
*/
|
*/
|
||||||
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
RelationCacheInsert(relation);
|
RelationCacheInsert(relation);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a temp rel, RelationGetNumberOfBlocks will assume that
|
||||||
|
* rd_nblocks is correct. Must forcibly update the block count when
|
||||||
|
* creating the relcache entry. But if we are doing a rebuild, don't
|
||||||
|
* do this yet; leave it to RelationClearRelation to do at the end.
|
||||||
|
* (Otherwise, an elog in RelationUpdateNumberOfBlocks would leave us
|
||||||
|
* with inconsistent relcache state.)
|
||||||
|
*/
|
||||||
|
if (relation->rd_istemp && oldrelation == NULL)
|
||||||
|
RelationUpdateNumberOfBlocks(relation);
|
||||||
|
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1601,8 +1612,7 @@ RelationClose(Relation relation)
|
||||||
|
|
||||||
#ifdef RELCACHE_FORCE_RELEASE
|
#ifdef RELCACHE_FORCE_RELEASE
|
||||||
if (RelationHasReferenceCountZero(relation) &&
|
if (RelationHasReferenceCountZero(relation) &&
|
||||||
!relation->rd_isnew &&
|
!relation->rd_isnew)
|
||||||
!relation->rd_istemp)
|
|
||||||
RelationClearRelation(relation, false);
|
RelationClearRelation(relation, false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1733,19 +1743,15 @@ RelationClearRelation(Relation relation, bool rebuild)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* When rebuilding an open relcache entry, must preserve ref count
|
* When rebuilding an open relcache entry, must preserve ref count
|
||||||
* and new/temp flags. Also attempt to preserve the tupledesc,
|
* and rd_isnew flag. Also attempt to preserve the tupledesc,
|
||||||
* rewrite rules, and trigger substructures in place. Furthermore
|
* rewrite rules, and trigger substructures in place.
|
||||||
* we save/restore rd_nblocks (in case it is a new/temp relation)
|
|
||||||
* *and* call RelationGetNumberOfBlocks (in case it isn't).
|
|
||||||
*/
|
*/
|
||||||
int old_refcnt = relation->rd_refcnt;
|
int old_refcnt = relation->rd_refcnt;
|
||||||
bool old_isnew = relation->rd_isnew;
|
bool old_isnew = relation->rd_isnew;
|
||||||
bool old_istemp = relation->rd_istemp;
|
|
||||||
TupleDesc old_att = relation->rd_att;
|
TupleDesc old_att = relation->rd_att;
|
||||||
RuleLock *old_rules = relation->rd_rules;
|
RuleLock *old_rules = relation->rd_rules;
|
||||||
MemoryContext old_rulescxt = relation->rd_rulescxt;
|
MemoryContext old_rulescxt = relation->rd_rulescxt;
|
||||||
TriggerDesc *old_trigdesc = relation->trigdesc;
|
TriggerDesc *old_trigdesc = relation->trigdesc;
|
||||||
BlockNumber old_nblocks = relation->rd_nblocks;
|
|
||||||
RelationBuildDescInfo buildinfo;
|
RelationBuildDescInfo buildinfo;
|
||||||
|
|
||||||
buildinfo.infotype = INFO_RELID;
|
buildinfo.infotype = INFO_RELID;
|
||||||
|
@ -1764,7 +1770,6 @@ RelationClearRelation(Relation relation, bool rebuild)
|
||||||
}
|
}
|
||||||
RelationSetReferenceCount(relation, old_refcnt);
|
RelationSetReferenceCount(relation, old_refcnt);
|
||||||
relation->rd_isnew = old_isnew;
|
relation->rd_isnew = old_isnew;
|
||||||
relation->rd_istemp = old_istemp;
|
|
||||||
if (equalTupleDescs(old_att, relation->rd_att))
|
if (equalTupleDescs(old_att, relation->rd_att))
|
||||||
{
|
{
|
||||||
FreeTupleDesc(relation->rd_att);
|
FreeTupleDesc(relation->rd_att);
|
||||||
|
@ -1791,13 +1796,14 @@ RelationClearRelation(Relation relation, bool rebuild)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
FreeTriggerDesc(old_trigdesc);
|
FreeTriggerDesc(old_trigdesc);
|
||||||
relation->rd_nblocks = old_nblocks;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this is kind of expensive, but I think we must do it in case
|
* Update rd_nblocks. This is kind of expensive, but I think we must
|
||||||
* relation has been truncated...
|
* do it in case relation has been truncated... we definitely must
|
||||||
|
* do it if the rel is new or temp, since RelationGetNumberOfBlocks
|
||||||
|
* will subsequently assume that the block count is correct.
|
||||||
*/
|
*/
|
||||||
relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
|
RelationUpdateNumberOfBlocks(relation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1811,18 +1817,19 @@ RelationFlushRelation(Relation relation)
|
||||||
{
|
{
|
||||||
bool rebuild;
|
bool rebuild;
|
||||||
|
|
||||||
if (relation->rd_isnew || relation->rd_istemp)
|
if (relation->rd_isnew)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* New and temp relcache entries must always be rebuilt, not
|
* New relcache entries are always rebuilt, not flushed; else we'd
|
||||||
* flushed; else we'd forget those two important status bits.
|
* forget the "new" status of the relation, which is a useful
|
||||||
|
* optimization to have.
|
||||||
*/
|
*/
|
||||||
rebuild = true;
|
rebuild = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Nonlocal rels can be dropped from the relcache if not open.
|
* Pre-existing rels can be dropped from the relcache if not open.
|
||||||
*/
|
*/
|
||||||
rebuild = !RelationHasReferenceCountZero(relation);
|
rebuild = !RelationHasReferenceCountZero(relation);
|
||||||
}
|
}
|
||||||
|
@ -1921,7 +1928,7 @@ RelationCacheInvalidate(void)
|
||||||
|
|
||||||
relcacheInvalsReceived++;
|
relcacheInvalsReceived++;
|
||||||
|
|
||||||
if (RelationHasReferenceCountZero(relation) && !relation->rd_istemp)
|
if (RelationHasReferenceCountZero(relation))
|
||||||
{
|
{
|
||||||
/* Delete this entry immediately */
|
/* Delete this entry immediately */
|
||||||
RelationClearRelation(relation, false);
|
RelationClearRelation(relation, false);
|
||||||
|
@ -1965,7 +1972,10 @@ AtEOXact_RelationCache(bool commit)
|
||||||
*
|
*
|
||||||
* During commit, reset the flag to false, since we are now out of the
|
* During commit, reset the flag to false, since we are now out of the
|
||||||
* creating transaction. During abort, simply delete the relcache
|
* creating transaction. During abort, simply delete the relcache
|
||||||
* entry --- it isn't interesting any longer.
|
* entry --- it isn't interesting any longer. (NOTE: if we have
|
||||||
|
* forgotten the isnew state of a new relation due to a forced cache
|
||||||
|
* flush, the entry will get deleted anyway by shared-cache-inval
|
||||||
|
* processing of the aborted pg_class insertion.)
|
||||||
*/
|
*/
|
||||||
if (relation->rd_isnew)
|
if (relation->rd_isnew)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: dependency.h,v 1.3 2002/07/16 22:12:20 tgl Exp $
|
* $Id: dependency.h,v 1.4 2002/08/11 21:17:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -98,6 +98,6 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
|
||||||
int nreferenced,
|
int nreferenced,
|
||||||
DependencyType behavior);
|
DependencyType behavior);
|
||||||
|
|
||||||
extern void deleteDependencyRecordsFor(Oid classId, Oid objectId);
|
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId);
|
||||||
|
|
||||||
#endif /* DEPENDENCY_H */
|
#endif /* DEPENDENCY_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: bufmgr.h,v 1.62 2002/08/06 02:36:35 tgl Exp $
|
* $Id: bufmgr.h,v 1.63 2002/08/11 21:17:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -161,6 +161,7 @@ extern void AtEOXact_Buffers(bool isCommit);
|
||||||
extern void FlushBufferPool(void);
|
extern void FlushBufferPool(void);
|
||||||
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
|
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
|
||||||
extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
|
extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
|
||||||
|
extern void RelationUpdateNumberOfBlocks(Relation relation);
|
||||||
extern int FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock);
|
extern int FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock);
|
||||||
extern void DropRelationBuffers(Relation rel);
|
extern void DropRelationBuffers(Relation rel);
|
||||||
extern void DropRelFileNodeBuffers(RelFileNode rnode, bool istemp);
|
extern void DropRelFileNodeBuffers(RelFileNode rnode, bool istemp);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: rel.h,v 1.61 2002/08/06 02:36:35 tgl Exp $
|
* $Id: rel.h,v 1.62 2002/08/11 21:17:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -113,6 +113,10 @@ typedef struct RelationData
|
||||||
* InvalidBlockNumber */
|
* InvalidBlockNumber */
|
||||||
int rd_refcnt; /* reference count */
|
int rd_refcnt; /* reference count */
|
||||||
bool rd_isnew; /* rel was created in current xact */
|
bool rd_isnew; /* rel was created in current xact */
|
||||||
|
/*
|
||||||
|
* NOTE: rd_isnew should be relied on only for optimization purposes;
|
||||||
|
* it is possible for new-ness to be "forgotten" (eg, after CLUSTER).
|
||||||
|
*/
|
||||||
bool rd_istemp; /* rel uses the local buffer mgr */
|
bool rd_istemp; /* rel uses the local buffer mgr */
|
||||||
bool rd_isnailed; /* rel is nailed in cache */
|
bool rd_isnailed; /* rel is nailed in cache */
|
||||||
bool rd_indexfound; /* true if rd_indexlist is valid */
|
bool rd_indexfound; /* true if rd_indexlist is valid */
|
||||||
|
|
|
@ -8,6 +8,7 @@ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'clstr_tst_s_pkey
|
||||||
CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY,
|
CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY,
|
||||||
b INT,
|
b INT,
|
||||||
c TEXT,
|
c TEXT,
|
||||||
|
d TEXT,
|
||||||
CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s);
|
CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s);
|
||||||
NOTICE: CREATE TABLE will create implicit sequence 'clstr_tst_a_seq' for SERIAL column 'clstr_tst.a'
|
NOTICE: CREATE TABLE will create implicit sequence 'clstr_tst_a_seq' for SERIAL column 'clstr_tst.a'
|
||||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'clstr_tst_pkey' for table 'clstr_tst'
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'clstr_tst_pkey' for table 'clstr_tst'
|
||||||
|
@ -54,220 +55,222 @@ INSERT INTO clstr_tst (b, c) VALUES (15, 'quince');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (7, 'siete');
|
INSERT INTO clstr_tst (b, c) VALUES (7, 'siete');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis');
|
INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho');
|
INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (6, 'seis');
|
-- This entry is needed to test that TOASTED values are copied correctly.
|
||||||
|
INSERT INTO clstr_tst (b, c, d) VALUES (6, 'seis', repeat('xyzzy', 100000));
|
||||||
CLUSTER clstr_tst_c ON clstr_tst;
|
CLUSTER clstr_tst_c ON clstr_tst;
|
||||||
SELECT * from clstr_tst;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
|
||||||
a | b | c
|
a | b | c | substring | length
|
||||||
----+----+---------------
|
----+----+---------------+--------------------------------+--------
|
||||||
10 | 14 | catorce
|
10 | 14 | catorce | |
|
||||||
18 | 5 | cinco
|
18 | 5 | cinco | |
|
||||||
9 | 4 | cuatro
|
9 | 4 | cuatro | |
|
||||||
26 | 19 | diecinueve
|
26 | 19 | diecinueve | |
|
||||||
12 | 18 | dieciocho
|
12 | 18 | dieciocho | |
|
||||||
30 | 16 | dieciseis
|
30 | 16 | dieciseis | |
|
||||||
24 | 17 | diecisiete
|
24 | 17 | diecisiete | |
|
||||||
2 | 10 | diez
|
2 | 10 | diez | |
|
||||||
23 | 12 | doce
|
23 | 12 | doce | |
|
||||||
11 | 2 | dos
|
11 | 2 | dos | |
|
||||||
25 | 9 | nueve
|
25 | 9 | nueve | |
|
||||||
31 | 8 | ocho
|
31 | 8 | ocho | |
|
||||||
1 | 11 | once
|
1 | 11 | once | |
|
||||||
28 | 15 | quince
|
28 | 15 | quince | |
|
||||||
32 | 6 | seis
|
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
|
||||||
29 | 7 | siete
|
29 | 7 | siete | |
|
||||||
15 | 13 | trece
|
15 | 13 | trece | |
|
||||||
22 | 30 | treinta
|
22 | 30 | treinta | |
|
||||||
17 | 32 | treinta y dos
|
17 | 32 | treinta y dos | |
|
||||||
3 | 31 | treinta y uno
|
3 | 31 | treinta y uno | |
|
||||||
5 | 3 | tres
|
5 | 3 | tres | |
|
||||||
20 | 1 | uno
|
20 | 1 | uno | |
|
||||||
6 | 20 | veinte
|
6 | 20 | veinte | |
|
||||||
14 | 25 | veinticinco
|
14 | 25 | veinticinco | |
|
||||||
21 | 24 | veinticuatro
|
21 | 24 | veinticuatro | |
|
||||||
4 | 22 | veintidos
|
4 | 22 | veintidos | |
|
||||||
19 | 29 | veintinueve
|
19 | 29 | veintinueve | |
|
||||||
16 | 28 | veintiocho
|
16 | 28 | veintiocho | |
|
||||||
27 | 26 | veintiseis
|
27 | 26 | veintiseis | |
|
||||||
13 | 27 | veintisiete
|
13 | 27 | veintisiete | |
|
||||||
7 | 23 | veintitres
|
7 | 23 | veintitres | |
|
||||||
8 | 21 | veintiuno
|
8 | 21 | veintiuno | |
|
||||||
(32 rows)
|
(32 rows)
|
||||||
|
|
||||||
SELECT * from clstr_tst ORDER BY a;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY a;
|
||||||
a | b | c
|
a | b | c | substring | length
|
||||||
----+----+---------------
|
----+----+---------------+--------------------------------+--------
|
||||||
1 | 11 | once
|
1 | 11 | once | |
|
||||||
2 | 10 | diez
|
2 | 10 | diez | |
|
||||||
3 | 31 | treinta y uno
|
3 | 31 | treinta y uno | |
|
||||||
4 | 22 | veintidos
|
4 | 22 | veintidos | |
|
||||||
5 | 3 | tres
|
5 | 3 | tres | |
|
||||||
6 | 20 | veinte
|
6 | 20 | veinte | |
|
||||||
7 | 23 | veintitres
|
7 | 23 | veintitres | |
|
||||||
8 | 21 | veintiuno
|
8 | 21 | veintiuno | |
|
||||||
9 | 4 | cuatro
|
9 | 4 | cuatro | |
|
||||||
10 | 14 | catorce
|
10 | 14 | catorce | |
|
||||||
11 | 2 | dos
|
11 | 2 | dos | |
|
||||||
12 | 18 | dieciocho
|
12 | 18 | dieciocho | |
|
||||||
13 | 27 | veintisiete
|
13 | 27 | veintisiete | |
|
||||||
14 | 25 | veinticinco
|
14 | 25 | veinticinco | |
|
||||||
15 | 13 | trece
|
15 | 13 | trece | |
|
||||||
16 | 28 | veintiocho
|
16 | 28 | veintiocho | |
|
||||||
17 | 32 | treinta y dos
|
17 | 32 | treinta y dos | |
|
||||||
18 | 5 | cinco
|
18 | 5 | cinco | |
|
||||||
19 | 29 | veintinueve
|
19 | 29 | veintinueve | |
|
||||||
20 | 1 | uno
|
20 | 1 | uno | |
|
||||||
21 | 24 | veinticuatro
|
21 | 24 | veinticuatro | |
|
||||||
22 | 30 | treinta
|
22 | 30 | treinta | |
|
||||||
23 | 12 | doce
|
23 | 12 | doce | |
|
||||||
24 | 17 | diecisiete
|
24 | 17 | diecisiete | |
|
||||||
25 | 9 | nueve
|
25 | 9 | nueve | |
|
||||||
26 | 19 | diecinueve
|
26 | 19 | diecinueve | |
|
||||||
27 | 26 | veintiseis
|
27 | 26 | veintiseis | |
|
||||||
28 | 15 | quince
|
28 | 15 | quince | |
|
||||||
29 | 7 | siete
|
29 | 7 | siete | |
|
||||||
30 | 16 | dieciseis
|
30 | 16 | dieciseis | |
|
||||||
31 | 8 | ocho
|
31 | 8 | ocho | |
|
||||||
32 | 6 | seis
|
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
|
||||||
(32 rows)
|
(32 rows)
|
||||||
|
|
||||||
SELECT * from clstr_tst ORDER BY b;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY b;
|
||||||
a | b | c
|
a | b | c | substring | length
|
||||||
----+----+---------------
|
----+----+---------------+--------------------------------+--------
|
||||||
20 | 1 | uno
|
20 | 1 | uno | |
|
||||||
11 | 2 | dos
|
11 | 2 | dos | |
|
||||||
5 | 3 | tres
|
5 | 3 | tres | |
|
||||||
9 | 4 | cuatro
|
9 | 4 | cuatro | |
|
||||||
18 | 5 | cinco
|
18 | 5 | cinco | |
|
||||||
32 | 6 | seis
|
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
|
||||||
29 | 7 | siete
|
29 | 7 | siete | |
|
||||||
31 | 8 | ocho
|
31 | 8 | ocho | |
|
||||||
25 | 9 | nueve
|
25 | 9 | nueve | |
|
||||||
2 | 10 | diez
|
2 | 10 | diez | |
|
||||||
1 | 11 | once
|
1 | 11 | once | |
|
||||||
23 | 12 | doce
|
23 | 12 | doce | |
|
||||||
15 | 13 | trece
|
15 | 13 | trece | |
|
||||||
10 | 14 | catorce
|
10 | 14 | catorce | |
|
||||||
28 | 15 | quince
|
28 | 15 | quince | |
|
||||||
30 | 16 | dieciseis
|
30 | 16 | dieciseis | |
|
||||||
24 | 17 | diecisiete
|
24 | 17 | diecisiete | |
|
||||||
12 | 18 | dieciocho
|
12 | 18 | dieciocho | |
|
||||||
26 | 19 | diecinueve
|
26 | 19 | diecinueve | |
|
||||||
6 | 20 | veinte
|
6 | 20 | veinte | |
|
||||||
8 | 21 | veintiuno
|
8 | 21 | veintiuno | |
|
||||||
4 | 22 | veintidos
|
4 | 22 | veintidos | |
|
||||||
7 | 23 | veintitres
|
7 | 23 | veintitres | |
|
||||||
21 | 24 | veinticuatro
|
21 | 24 | veinticuatro | |
|
||||||
14 | 25 | veinticinco
|
14 | 25 | veinticinco | |
|
||||||
27 | 26 | veintiseis
|
27 | 26 | veintiseis | |
|
||||||
13 | 27 | veintisiete
|
13 | 27 | veintisiete | |
|
||||||
16 | 28 | veintiocho
|
16 | 28 | veintiocho | |
|
||||||
19 | 29 | veintinueve
|
19 | 29 | veintinueve | |
|
||||||
22 | 30 | treinta
|
22 | 30 | treinta | |
|
||||||
3 | 31 | treinta y uno
|
3 | 31 | treinta y uno | |
|
||||||
17 | 32 | treinta y dos
|
17 | 32 | treinta y dos | |
|
||||||
(32 rows)
|
(32 rows)
|
||||||
|
|
||||||
SELECT * from clstr_tst ORDER BY c;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY c;
|
||||||
a | b | c
|
a | b | c | substring | length
|
||||||
----+----+---------------
|
----+----+---------------+--------------------------------+--------
|
||||||
10 | 14 | catorce
|
10 | 14 | catorce | |
|
||||||
18 | 5 | cinco
|
18 | 5 | cinco | |
|
||||||
9 | 4 | cuatro
|
9 | 4 | cuatro | |
|
||||||
26 | 19 | diecinueve
|
26 | 19 | diecinueve | |
|
||||||
12 | 18 | dieciocho
|
12 | 18 | dieciocho | |
|
||||||
30 | 16 | dieciseis
|
30 | 16 | dieciseis | |
|
||||||
24 | 17 | diecisiete
|
24 | 17 | diecisiete | |
|
||||||
2 | 10 | diez
|
2 | 10 | diez | |
|
||||||
23 | 12 | doce
|
23 | 12 | doce | |
|
||||||
11 | 2 | dos
|
11 | 2 | dos | |
|
||||||
25 | 9 | nueve
|
25 | 9 | nueve | |
|
||||||
31 | 8 | ocho
|
31 | 8 | ocho | |
|
||||||
1 | 11 | once
|
1 | 11 | once | |
|
||||||
28 | 15 | quince
|
28 | 15 | quince | |
|
||||||
32 | 6 | seis
|
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
|
||||||
29 | 7 | siete
|
29 | 7 | siete | |
|
||||||
15 | 13 | trece
|
15 | 13 | trece | |
|
||||||
22 | 30 | treinta
|
22 | 30 | treinta | |
|
||||||
17 | 32 | treinta y dos
|
17 | 32 | treinta y dos | |
|
||||||
3 | 31 | treinta y uno
|
3 | 31 | treinta y uno | |
|
||||||
5 | 3 | tres
|
5 | 3 | tres | |
|
||||||
20 | 1 | uno
|
20 | 1 | uno | |
|
||||||
6 | 20 | veinte
|
6 | 20 | veinte | |
|
||||||
14 | 25 | veinticinco
|
14 | 25 | veinticinco | |
|
||||||
21 | 24 | veinticuatro
|
21 | 24 | veinticuatro | |
|
||||||
4 | 22 | veintidos
|
4 | 22 | veintidos | |
|
||||||
19 | 29 | veintinueve
|
19 | 29 | veintinueve | |
|
||||||
16 | 28 | veintiocho
|
16 | 28 | veintiocho | |
|
||||||
27 | 26 | veintiseis
|
27 | 26 | veintiseis | |
|
||||||
13 | 27 | veintisiete
|
13 | 27 | veintisiete | |
|
||||||
7 | 23 | veintitres
|
7 | 23 | veintitres | |
|
||||||
8 | 21 | veintiuno
|
8 | 21 | veintiuno | |
|
||||||
(32 rows)
|
(32 rows)
|
||||||
|
|
||||||
-- Verify that inheritance link still works
|
-- Verify that inheritance link still works
|
||||||
INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table');
|
INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table');
|
||||||
SELECT * from clstr_tst;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
|
||||||
a | b | c
|
a | b | c | substring | length
|
||||||
----+-----+----------------
|
----+-----+----------------+--------------------------------+--------
|
||||||
10 | 14 | catorce
|
10 | 14 | catorce | |
|
||||||
18 | 5 | cinco
|
18 | 5 | cinco | |
|
||||||
9 | 4 | cuatro
|
9 | 4 | cuatro | |
|
||||||
26 | 19 | diecinueve
|
26 | 19 | diecinueve | |
|
||||||
12 | 18 | dieciocho
|
12 | 18 | dieciocho | |
|
||||||
30 | 16 | dieciseis
|
30 | 16 | dieciseis | |
|
||||||
24 | 17 | diecisiete
|
24 | 17 | diecisiete | |
|
||||||
2 | 10 | diez
|
2 | 10 | diez | |
|
||||||
23 | 12 | doce
|
23 | 12 | doce | |
|
||||||
11 | 2 | dos
|
11 | 2 | dos | |
|
||||||
25 | 9 | nueve
|
25 | 9 | nueve | |
|
||||||
31 | 8 | ocho
|
31 | 8 | ocho | |
|
||||||
1 | 11 | once
|
1 | 11 | once | |
|
||||||
28 | 15 | quince
|
28 | 15 | quince | |
|
||||||
32 | 6 | seis
|
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
|
||||||
29 | 7 | siete
|
29 | 7 | siete | |
|
||||||
15 | 13 | trece
|
15 | 13 | trece | |
|
||||||
22 | 30 | treinta
|
22 | 30 | treinta | |
|
||||||
17 | 32 | treinta y dos
|
17 | 32 | treinta y dos | |
|
||||||
3 | 31 | treinta y uno
|
3 | 31 | treinta y uno | |
|
||||||
5 | 3 | tres
|
5 | 3 | tres | |
|
||||||
20 | 1 | uno
|
20 | 1 | uno | |
|
||||||
6 | 20 | veinte
|
6 | 20 | veinte | |
|
||||||
14 | 25 | veinticinco
|
14 | 25 | veinticinco | |
|
||||||
21 | 24 | veinticuatro
|
21 | 24 | veinticuatro | |
|
||||||
4 | 22 | veintidos
|
4 | 22 | veintidos | |
|
||||||
19 | 29 | veintinueve
|
19 | 29 | veintinueve | |
|
||||||
16 | 28 | veintiocho
|
16 | 28 | veintiocho | |
|
||||||
27 | 26 | veintiseis
|
27 | 26 | veintiseis | |
|
||||||
13 | 27 | veintisiete
|
13 | 27 | veintisiete | |
|
||||||
7 | 23 | veintitres
|
7 | 23 | veintitres | |
|
||||||
8 | 21 | veintiuno
|
8 | 21 | veintiuno | |
|
||||||
0 | 100 | in child table
|
0 | 100 | in child table | |
|
||||||
(33 rows)
|
(33 rows)
|
||||||
|
|
||||||
-- Verify that foreign key link still works
|
-- Verify that foreign key link still works
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
|
INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
|
||||||
ERROR: clstr_tst_con referential integrity violation - key referenced from clstr_tst not found in clstr_tst_s
|
ERROR: clstr_tst_con referential integrity violation - key referenced from clstr_tst not found in clstr_tst_s
|
||||||
SELECT conname FROM pg_constraint WHERE conrelid=(SELECT oid FROM pg_class
|
SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass;
|
||||||
WHERE relname='clstr_tst');
|
|
||||||
conname
|
conname
|
||||||
----------------
|
----------------
|
||||||
clstr_tst_pkey
|
clstr_tst_pkey
|
||||||
clstr_tst_con
|
clstr_tst_con
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
SELECT relname FROM pg_class WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
|
SELECT relname, relkind,
|
||||||
relname
|
EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
|
||||||
----------------------
|
FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
|
||||||
clstr_tst
|
relname | relkind | hastoast
|
||||||
clstr_tst_a_seq
|
----------------------+---------+----------
|
||||||
clstr_tst_b
|
clstr_tst | r | t
|
||||||
clstr_tst_b_c
|
clstr_tst_a_seq | S | f
|
||||||
clstr_tst_c
|
clstr_tst_b | i | f
|
||||||
clstr_tst_c_b
|
clstr_tst_b_c | i | f
|
||||||
clstr_tst_inh
|
clstr_tst_c | i | f
|
||||||
clstr_tst_pkey
|
clstr_tst_c_b | i | f
|
||||||
clstr_tst_s
|
clstr_tst_inh | r | t
|
||||||
clstr_tst_s_pkey
|
clstr_tst_pkey | i | f
|
||||||
clstr_tst_s_rf_a_seq
|
clstr_tst_s | r | f
|
||||||
|
clstr_tst_s_pkey | i | f
|
||||||
|
clstr_tst_s_rf_a_seq | S | f
|
||||||
(11 rows)
|
(11 rows)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ CREATE TABLE clstr_tst_s (rf_a SERIAL PRIMARY KEY,
|
||||||
CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY,
|
CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY,
|
||||||
b INT,
|
b INT,
|
||||||
c TEXT,
|
c TEXT,
|
||||||
|
d TEXT,
|
||||||
CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s);
|
CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s);
|
||||||
|
|
||||||
CREATE INDEX clstr_tst_b ON clstr_tst (b);
|
CREATE INDEX clstr_tst_b ON clstr_tst (b);
|
||||||
|
@ -55,24 +56,26 @@ INSERT INTO clstr_tst (b, c) VALUES (15, 'quince');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (7, 'siete');
|
INSERT INTO clstr_tst (b, c) VALUES (7, 'siete');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis');
|
INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho');
|
INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho');
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (6, 'seis');
|
-- This entry is needed to test that TOASTED values are copied correctly.
|
||||||
|
INSERT INTO clstr_tst (b, c, d) VALUES (6, 'seis', repeat('xyzzy', 100000));
|
||||||
|
|
||||||
CLUSTER clstr_tst_c ON clstr_tst;
|
CLUSTER clstr_tst_c ON clstr_tst;
|
||||||
|
|
||||||
SELECT * from clstr_tst;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
|
||||||
SELECT * from clstr_tst ORDER BY a;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY a;
|
||||||
SELECT * from clstr_tst ORDER BY b;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY b;
|
||||||
SELECT * from clstr_tst ORDER BY c;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY c;
|
||||||
|
|
||||||
-- Verify that inheritance link still works
|
-- Verify that inheritance link still works
|
||||||
INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table');
|
INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table');
|
||||||
SELECT * from clstr_tst;
|
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
|
||||||
|
|
||||||
-- Verify that foreign key link still works
|
-- Verify that foreign key link still works
|
||||||
INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
|
INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
|
||||||
|
|
||||||
SELECT conname FROM pg_constraint WHERE conrelid=(SELECT oid FROM pg_class
|
SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass;
|
||||||
WHERE relname='clstr_tst');
|
|
||||||
|
|
||||||
|
|
||||||
SELECT relname FROM pg_class WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
|
SELECT relname, relkind,
|
||||||
|
EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
|
||||||
|
FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
|
||||||
|
|
Loading…
Reference in New Issue