postgresql/src/backend/catalog/index.c

1957 lines
52 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* index.c
* code to create and destroy POSTGRES index relations
*
2003-08-04 04:40:20 +02:00
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.216 2003/09/23 01:51:09 inoue Exp $
*
*
* INTERFACE ROUTINES
* index_create() - Create a cataloged index relation
* index_drop() - Removes index relation from catalogs
* BuildIndexInfo() - Prepare to insert index tuples
* FormIndexDatum() - Construct datum vector for one index tuple
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
2000-12-09 21:32:44 +01:00
#include <unistd.h>
1999-07-16 07:00:38 +02:00
#include "access/genam.h"
#include "access/heapam.h"
#include "access/istrat.h"
#include "bootstrap/bootstrap.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
2000-06-15 05:33:12 +02:00
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
#include "optimizer/prep.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "storage/sinval.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
1996-11-05 12:57:55 +01:00
/*
* macros used in guessing how many tuples are on a page.
*/
1999-07-07 11:27:28 +02:00
#define AVG_ATTR_SIZE 8
#define NTUPLES_PER_PAGE(natts) \
((BLCKSZ - MAXALIGN(sizeof(PageHeaderData))) / \
1999-07-07 11:27:28 +02:00
((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData))))
/* non-export function prototypes */
static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
2003-08-04 02:43:34 +02:00
IndexInfo *indexInfo,
Oid *classObjectId);
static void UpdateRelationRelation(Relation indexRelation);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
static void AppendAttributeTuples(Relation indexRelation, int numatts);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *classOids,
bool primary);
static Oid IndexGetRelation(Oid indexId);
static bool activate_index(Oid indexId, bool activate, bool inplace);
static bool reindexing = false;
bool
SetReindexProcessing(bool reindexmode)
2000-02-18 10:30:20 +01:00
{
bool old = reindexing;
2000-02-18 10:30:20 +01:00
reindexing = reindexmode;
return old;
}
bool
IsReindexProcessing(void)
2000-02-18 10:30:20 +01:00
{
return reindexing;
}
/*
* ConstructTupleDescriptor
*
* Build an index tuple descriptor for a new index
*/
static TupleDesc
ConstructTupleDescriptor(Relation heapRelation,
IndexInfo *indexInfo,
Oid *classObjectId)
{
int numatts = indexInfo->ii_NumIndexAttrs;
List *indexprs = indexInfo->ii_Expressions;
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
int natts; /* #atts in heap rel --- for error checks */
int i;
heapTupDesc = RelationGetDescr(heapRelation);
natts = RelationGetForm(heapRelation)->relnatts;
/*
* allocate the new tuple descriptor
*/
indexTupDesc = CreateTemplateTupleDesc(numatts, false);
/*
* For simple index columns, we copy the pg_attribute row from the
2003-08-04 02:43:34 +02:00
* parent relation and modify it as necessary. For expressions we
* have to cons up a pg_attribute row the hard way.
*/
for (i = 0; i < numatts; i++)
{
AttrNumber atnum = indexInfo->ii_KeyAttrNumbers[i];
Form_pg_attribute to;
HeapTuple tuple;
Form_pg_type typeTup;
Oid keyType;
indexTupDesc->attrs[i] = to =
(Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE);
if (atnum != 0)
{
/* Simple index column */
Form_pg_attribute from;
if (atnum < 0)
{
/*
* here we are indexing on a system attribute (-1...-n)
*/
from = SystemAttributeDefinition(atnum,
2003-08-04 02:43:34 +02:00
heapRelation->rd_rel->relhasoids);
}
else
{
/*
* here we are indexing on a normal attribute (1...n)
*/
if (atnum > natts) /* safety check */
elog(ERROR, "invalid column number %d", atnum);
from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)];
}
/*
2003-08-04 02:43:34 +02:00
* now that we've determined the "from", let's copy the tuple
* desc data...
*/
memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
/*
* Fix the stuff that should not be the same as the underlying
* attr
*/
to->attnum = i + 1;
to->attstattarget = 0;
to->attcacheoff = -1;
to->attnotnull = false;
to->atthasdef = false;
to->attislocal = true;
to->attinhcount = 0;
}
else
{
/* Expressional index */
Node *indexkey;
2003-08-04 02:43:34 +02:00
if (indexprs == NIL) /* shouldn't happen */
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexprs);
indexprs = lnext(indexprs);
/*
* Make the attribute's name "pg_expresssion_nnn" (maybe think
* of something better later)
*/
sprintf(NameStr(to->attname), "pg_expression_%d", i + 1);
/*
2003-08-04 02:43:34 +02:00
* Lookup the expression type in pg_type for the type length
* etc.
*/
keyType = exprType(indexkey);
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(keyType),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", keyType);
typeTup = (Form_pg_type) GETSTRUCT(tuple);
/*
* Assign some of the attributes values. Leave the rest as 0.
*/
to->attnum = i + 1;
to->atttypid = keyType;
to->attlen = typeTup->typlen;
to->attbyval = typeTup->typbyval;
to->attstorage = typeTup->typstorage;
to->attalign = typeTup->typalign;
to->attcacheoff = -1;
to->atttypmod = -1;
to->attislocal = true;
ReleaseSysCache(tuple);
}
/*
2001-03-22 05:01:46 +01:00
* We do not yet have the correct relation OID for the index, so
* just set it invalid for now. InitializeAttributeOids() will
* fix it later.
*/
to->attrelid = InvalidOid;
/*
* Check the opclass to see if it provides a keytype (overriding
* the attribute type).
*/
tuple = SearchSysCache(CLAOID,
ObjectIdGetDatum(classObjectId[i]),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for opclass %u",
classObjectId[i]);
keyType = ((Form_pg_opclass) GETSTRUCT(tuple))->opckeytype;
ReleaseSysCache(tuple);
if (OidIsValid(keyType) && keyType != to->atttypid)
{
/* index value and heap value have different types */
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(keyType),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", keyType);
typeTup = (Form_pg_type) GETSTRUCT(tuple);
to->atttypid = keyType;
to->atttypmod = -1;
to->attlen = typeTup->typlen;
to->attbyval = typeTup->typbyval;
to->attalign = typeTup->typalign;
to->attstorage = typeTup->typstorage;
ReleaseSysCache(tuple);
}
}
return indexTupDesc;
}
/* ----------------------------------------------------------------
* UpdateRelationRelation
* ----------------------------------------------------------------
*/
static void
UpdateRelationRelation(Relation indexRelation)
{
Relation pg_class;
HeapTuple tuple;
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
/* XXX Natts_pg_class_fixed is a hack - see pg_class.h */
tuple = heap_addheader(Natts_pg_class_fixed,
2002-09-04 22:31:48 +02:00
true,
CLASS_TUPLE_SIZE,
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
(void *) indexRelation->rd_rel);
/*
2002-09-04 22:31:48 +02:00
* the new tuple must have the oid already chosen for the index. sure
* would be embarrassing to do this sort of thing in polite company.
*/
HeapTupleSetOid(tuple, RelationGetRelid(indexRelation));
simple_heap_insert(pg_class, tuple);
/* update the system catalog indexes */
CatalogUpdateIndexes(pg_class, tuple);
heap_freetuple(tuple);
heap_close(pg_class, RowExclusiveLock);
}
/* ----------------------------------------------------------------
* InitializeAttributeOids
* ----------------------------------------------------------------
*/
static void
InitializeAttributeOids(Relation indexRelation,
int numatts,
Oid indexoid)
{
TupleDesc tupleDescriptor;
int i;
1998-09-01 05:29:17 +02:00
tupleDescriptor = RelationGetDescr(indexRelation);
for (i = 0; i < numatts; i += 1)
tupleDescriptor->attrs[i]->attrelid = indexoid;
}
/* ----------------------------------------------------------------
* AppendAttributeTuples
* ----------------------------------------------------------------
*/
static void
AppendAttributeTuples(Relation indexRelation, int numatts)
{
Relation pg_attribute;
CatalogIndexState indstate;
TupleDesc indexTupDesc;
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
HeapTuple new_tuple;
int i;
/*
* open the attribute relation and its indexes
*/
pg_attribute = heap_openr(AttributeRelationName, RowExclusiveLock);
indstate = CatalogOpenIndexes(pg_attribute);
/*
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
* insert data from new index's tupdesc into pg_attribute
*/
1998-09-01 05:29:17 +02:00
indexTupDesc = RelationGetDescr(indexRelation);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
for (i = 0; i < numatts; i++)
{
/*
* There used to be very grotty code here to set these fields, but
* I think it's unnecessary. They should be set already.
*/
Assert(indexTupDesc->attrs[i]->attnum == i + 1);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
Assert(indexTupDesc->attrs[i]->attcacheoff == -1);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
new_tuple = heap_addheader(Natts_pg_attribute,
2002-09-04 22:31:48 +02:00
false,
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
ATTRIBUTE_TUPLE_SIZE,
(void *) indexTupDesc->attrs[i]);
simple_heap_insert(pg_attribute, new_tuple);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
CatalogIndexInsert(indstate, new_tuple);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
heap_freetuple(new_tuple);
}
CatalogCloseIndexes(indstate);
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
heap_close(pg_attribute, RowExclusiveLock);
}
/* ----------------------------------------------------------------
* UpdateIndexRelation
* ----------------------------------------------------------------
*/
static void
UpdateIndexRelation(Oid indexoid,
Oid heapoid,
IndexInfo *indexInfo,
1998-09-01 05:29:17 +02:00
Oid *classOids,
1999-05-25 18:15:34 +02:00
bool primary)
{
int16 indkey[INDEX_MAX_KEYS];
Oid indclass[INDEX_MAX_KEYS];
Datum exprsDatum;
Datum predDatum;
Datum values[Natts_pg_index];
char nulls[Natts_pg_index];
Relation pg_index;
HeapTuple tuple;
int i;
/*
* Copy the index key and opclass info into zero-filled vectors
*/
MemSet(indkey, 0, sizeof(indkey));
MemSet(indclass, 0, sizeof(indclass));
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
indkey[i] = indexInfo->ii_KeyAttrNumbers[i];
indclass[i] = classOids[i];
}
/*
* Convert the index expressions (if any) to a text datum
*/
if (indexInfo->ii_Expressions != NIL)
{
char *exprsString;
exprsString = nodeToString(indexInfo->ii_Expressions);
exprsDatum = DirectFunctionCall1(textin,
CStringGetDatum(exprsString));
pfree(exprsString);
}
else
exprsDatum = (Datum) 0;
/*
* Convert the index predicate (if any) to a text datum
*/
if (indexInfo->ii_Predicate != NIL)
{
char *predString;
predString = nodeToString(indexInfo->ii_Predicate);
predDatum = DirectFunctionCall1(textin,
CStringGetDatum(predString));
pfree(predString);
}
else
predDatum = (Datum) 0;
/*
* open the system catalog index relation
*/
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
/*
* Build a pg_index tuple
*/
MemSet(nulls, ' ', sizeof(nulls));
values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid);
values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid);
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
values[Anum_pg_index_indexprs - 1] = exprsDatum;
if (exprsDatum == (Datum) 0)
nulls[Anum_pg_index_indexprs - 1] = 'n';
values[Anum_pg_index_indpred - 1] = predDatum;
if (predDatum == (Datum) 0)
nulls[Anum_pg_index_indpred - 1] = 'n';
tuple = heap_formtuple(RelationGetDescr(pg_index), values, nulls);
/*
* insert the tuple into the pg_index catalog
*/
simple_heap_insert(pg_index, tuple);
/* update the indexes on pg_index */
CatalogUpdateIndexes(pg_index, tuple);
/*
* close the relation and free the tuple
*/
heap_close(pg_index, RowExclusiveLock);
heap_freetuple(tuple);
}
/* ----------------------------------------------------------------
* index_create
*
* Returns OID of the created index.
* ----------------------------------------------------------------
*/
Oid
index_create(Oid heapRelationId,
const char *indexRelationName,
IndexInfo *indexInfo,
Oid accessMethodObjectId,
1998-09-01 05:29:17 +02:00
Oid *classObjectId,
bool primary,
bool isconstraint,
bool allow_system_table_mods)
{
Relation heapRelation;
Relation indexRelation;
TupleDesc indexTupDesc;
bool shared_relation;
Oid namespaceId;
Oid indexoid;
int i;
1999-05-25 18:15:34 +02:00
SetReindexProcessing(false);
/*
* Only SELECT ... FOR UPDATE are allowed while doing this
*/
heapRelation = heap_open(heapRelationId, ShareLock);
/*
2002-09-04 22:31:48 +02:00
* The index will be in the same namespace as its parent table, and is
* shared across databases if and only if the parent is.
*/
namespaceId = RelationGetNamespace(heapRelation);
shared_relation = heapRelation->rd_rel->relisshared;
/*
* check parameters
*/
if (indexInfo->ii_NumIndexAttrs < 1)
2001-08-10 17:49:39 +02:00
elog(ERROR, "must index at least one column");
1999-05-25 18:15:34 +02:00
if (!allow_system_table_mods &&
IsSystemRelation(heapRelation) &&
IsNormalProcessingMode())
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user-defined indexes on system catalogs are not supported")));
/*
* We cannot allow indexing a shared relation after initdb (because
* there's no way to make the entry in other databases' pg_class).
* Unfortunately we can't distinguish initdb from a manually started
2002-09-04 22:31:48 +02:00
* standalone backend. However, we can at least prevent this mistake
* under normal multi-user operation.
*/
if (shared_relation && IsUnderPostmaster)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2003-08-04 02:43:34 +02:00
errmsg("shared indexes cannot be created after initdb")));
if (get_relname_relid(indexRelationName, namespaceId))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists",
indexRelationName)));
/*
* construct tuple descriptor for index tuples
*/
indexTupDesc = ConstructTupleDescriptor(heapRelation,
indexInfo,
classObjectId);
2002-09-04 22:31:48 +02:00
/*
* 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,
namespaceId,
indexTupDesc,
shared_relation,
true,
allow_system_table_mods);
/* Fetch the relation OID assigned by heap_create */
indexoid = RelationGetRelid(indexRelation);
/*
* Obtain exclusive lock on it. Although no other backends can see it
* until we commit, this prevents deadlock-risk complaints from lock
* manager in cases such as CLUSTER.
*/
LockRelation(indexRelation, AccessExclusiveLock);
/*
* Fill in fields of the index's pg_class entry that are not set
* correctly by heap_create.
*
* XXX should have a cleaner way to create cataloged indexes
*/
indexRelation->rd_rel->relowner = GetUserId();
indexRelation->rd_rel->relam = accessMethodObjectId;
indexRelation->rd_rel->relkind = RELKIND_INDEX;
indexRelation->rd_rel->relhasoids = false;
/*
* store index's pg_class entry
*/
UpdateRelationRelation(indexRelation);
/*
* now update the object id's of all the attribute tuple forms in the
* index relation's tuple descriptor
*/
InitializeAttributeOids(indexRelation,
indexInfo->ii_NumIndexAttrs,
indexoid);
/*
* append ATTRIBUTE tuples for the index
*/
AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
/* ----------------
* update pg_index
* (append INDEX tuple)
*
* Note that this stows away a representation of "predicate".
* (Or, could define a rule to maintain the predicate) --Nels, Feb '92
* ----------------
*/
UpdateIndexRelation(indexoid, heapRelationId, indexInfo,
classObjectId, primary);
/*
* Register constraint and dependencies for the index.
*
* If the index is from a CONSTRAINT clause, construct a pg_constraint
2002-09-04 22:31:48 +02:00
* entry. The index is then linked to the constraint, which in turn
* is linked to the table. If it's not a CONSTRAINT, make the
* dependency directly on the table.
*
2002-09-04 22:31:48 +02:00
* We don't need a dependency on the namespace, because there'll be an
* indirect dependency via our parent table.
*
2002-09-04 22:31:48 +02:00
* During bootstrap we can't register any dependencies, and we don't try
* to make a constraint either.
*/
if (!IsBootstrapProcessingMode())
{
2002-09-04 22:31:48 +02:00
ObjectAddress myself,
referenced;
myself.classId = RelOid_pg_class;
myself.objectId = indexoid;
myself.objectSubId = 0;
if (isconstraint)
{
char constraintType;
Oid conOid;
if (primary)
constraintType = CONSTRAINT_PRIMARY;
else if (indexInfo->ii_Unique)
constraintType = CONSTRAINT_UNIQUE;
else
{
elog(ERROR, "constraint must be PRIMARY or UNIQUE");
2002-09-04 22:31:48 +02:00
constraintType = 0; /* keep compiler quiet */
}
/* Shouldn't have any expressions */
if (indexInfo->ii_Expressions)
elog(ERROR, "constraints can't have index expressions");
conOid = CreateConstraintEntry(indexRelationName,
namespaceId,
constraintType,
2002-09-04 22:31:48 +02:00
false, /* isDeferrable */
false, /* isDeferred */
heapRelationId,
indexInfo->ii_KeyAttrNumbers,
indexInfo->ii_NumIndexAttrs,
2002-09-04 22:31:48 +02:00
InvalidOid, /* no domain */
InvalidOid, /* no foreign key */
NULL,
0,
' ',
' ',
' ',
2003-08-04 02:43:34 +02:00
InvalidOid, /* no associated index */
2002-09-04 22:31:48 +02:00
NULL, /* no check constraint */
NULL,
NULL);
referenced.classId = get_system_catalog_relid(ConstraintRelationName);
referenced.objectId = conOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
else
{
/* Create auto dependencies on simply-referenced columns */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
if (indexInfo->ii_KeyAttrNumbers[i] != 0)
{
referenced.classId = RelOid_pg_class;
referenced.objectId = heapRelationId;
referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
}
/* Store dependency on operator classes */
referenced.classId = get_system_catalog_relid(OperatorClassRelationName);
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
referenced.objectId = classObjectId[i];
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Store dependencies on anything mentioned in index expressions */
if (indexInfo->ii_Expressions)
{
recordDependencyOnSingleRelExpr(&myself,
2003-08-04 02:43:34 +02:00
(Node *) indexInfo->ii_Expressions,
heapRelationId,
DEPENDENCY_NORMAL,
DEPENDENCY_AUTO);
}
/* Store dependencies on anything mentioned in predicate */
if (indexInfo->ii_Predicate)
{
recordDependencyOnSingleRelExpr(&myself,
2003-08-04 02:43:34 +02:00
(Node *) indexInfo->ii_Predicate,
heapRelationId,
DEPENDENCY_NORMAL,
DEPENDENCY_AUTO);
}
}
/*
* Fill in the index strategy structure with information from the
* catalogs. First we must advance the command counter so that we
* will see the newly-entered index catalog tuples.
*/
CommandCounterIncrement();
RelationInitIndexAccessInfo(indexRelation);
/*
* If this is bootstrap (initdb) time, then we don't actually fill in
* the index yet. We'll be creating more indexes and classes later,
* so we delay filling them in until just before we're done with
* bootstrapping. Otherwise, we call the routine that constructs the
* index.
*
* In normal processing mode, the heap and index relations are closed by
* index_build() --- but we continue to hold the ShareLock on the heap
* and the exclusive lock on the index that we acquired above, until
* end of transaction.
*/
if (IsBootstrapProcessingMode())
{
index_register(heapRelationId, indexoid, indexInfo);
/* XXX shouldn't we close the heap and index rels here? */
}
else
index_build(heapRelation, indexRelation, indexInfo);
return indexoid;
}
/*
* index_drop
*
* NOTE: this routine should now only be called through performDeletion(),
* else associated dependencies won't be cleaned up.
*/
void
index_drop(Oid indexId)
{
Oid heapId;
Relation userHeapRelation;
Relation userIndexRelation;
Relation indexRelation;
HeapTuple tuple;
int i;
1999-05-25 18:15:34 +02:00
Assert(OidIsValid(indexId));
/*
* To drop an index safely, we must grab exclusive lock on its parent
* table; otherwise there could be other backends using the index!
* Exclusive lock on the index alone is insufficient because another
* backend might be in the midst of devising a query plan that will
* use the index. The parser and planner take care to hold an
* appropriate lock on the parent table while working, but having them
* hold locks on all the indexes too seems overly complex. We do grab
* exclusive lock on the index too, just to be safe. Both locks must
* be held till end of transaction, else other backends will still see
* this index in pg_index.
*/
heapId = IndexGetRelation(indexId);
userHeapRelation = heap_open(heapId, AccessExclusiveLock);
userIndexRelation = index_open(indexId);
LockRelation(userIndexRelation, AccessExclusiveLock);
/*
* fix RELATION relation
*/
DeleteRelationTuple(indexId);
2002-09-04 22:31:48 +02:00
/*
* fix ATTRIBUTE relation
*/
DeleteAttributeTuples(indexId);
/*
* fix INDEX relation
*/
indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);
tuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u", indexId);
1998-08-21 00:07:46 +02:00
simple_heap_delete(indexRelation, &tuple->t_self);
ReleaseSysCache(tuple);
heap_close(indexRelation, RowExclusiveLock);
/*
* flush buffer cache and physically remove the file
*/
i = FlushRelationBuffers(userIndexRelation, (BlockNumber) 0);
if (i < 0)
elog(ERROR, "FlushRelationBuffers returned %d", i);
smgrunlink(DEFAULT_SMGR, userIndexRelation);
/*
2002-09-04 22:31:48 +02:00
* We are presently too lazy to attempt to compute the new correct
2003-08-04 02:43:34 +02:00
* value of relhasindex (the next VACUUM will fix it if necessary). So
* there is no need to update the pg_class tuple for the owning
2002-09-04 22:31:48 +02:00
* relation. But we must send out a shared-cache-inval notice on the
* owning relation to ensure other backends update their relcache
* lists of indexes.
*/
CacheInvalidateRelcache(heapId);
/*
* Close rels, but keep locks
*/
index_close(userIndexRelation);
heap_close(userHeapRelation, NoLock);
RelationForgetRelation(indexId);
}
/* ----------------------------------------------------------------
* index_build support
* ----------------------------------------------------------------
*/
/* ----------------
* BuildIndexInfo
* Construct an IndexInfo record for an open index
*
* IndexInfo stores the information about the index that's needed by
* FormIndexDatum, which is used for both index_build() and later insertion
2001-03-22 05:01:46 +01:00
* of individual index tuples. Normally we build an IndexInfo for an index
* just once per command, and then use it for (potentially) many tuples.
* ----------------
*/
IndexInfo *
BuildIndexInfo(Relation index)
{
IndexInfo *ii = makeNode(IndexInfo);
Form_pg_index indexStruct = index->rd_index;
int i;
int numKeys;
/* check the number of keys, and copy attr numbers into the IndexInfo */
numKeys = indexStruct->indnatts;
if (numKeys < 1 || numKeys > INDEX_MAX_KEYS)
elog(ERROR, "invalid indnatts %d for index %u",
numKeys, RelationGetRelid(index));
ii->ii_NumIndexAttrs = numKeys;
for (i = 0; i < numKeys; i++)
ii->ii_KeyAttrNumbers[i] = indexStruct->indkey[i];
/* fetch any expressions needed for expressional indexes */
ii->ii_Expressions = RelationGetIndexExpressions(index);
ii->ii_ExpressionsState = NIL;
/* fetch index predicate if any */
ii->ii_Predicate = RelationGetIndexPredicate(index);
ii->ii_PredicateState = NIL;
/* other info */
ii->ii_Unique = indexStruct->indisunique;
return ii;
}
/* ----------------
* FormIndexDatum
* Construct Datum[] and nullv[] arrays for a new index tuple.
*
* indexInfo Info about the index
* heapTuple Heap tuple for which we must prepare an index entry
* heapDescriptor tupledesc for heap tuple
* estate executor state for evaluating any index expressions
* datum Array of index Datums (output area)
* nullv Array of is-null indicators (output area)
*
* When there are no index expressions, estate may be NULL. Otherwise it
* must be supplied, *and* the ecxt_scantuple slot of its per-tuple expr
* context must point to the heap tuple passed in.
*
* For largely historical reasons, we don't actually call index_formtuple()
* here, we just prepare its input arrays datum[] and nullv[].
* ----------------
*/
void
FormIndexDatum(IndexInfo *indexInfo,
HeapTuple heapTuple,
TupleDesc heapDescriptor,
EState *estate,
Datum *datum,
char *nullv)
{
List *indexprs;
int i;
if (indexInfo->ii_Expressions != NIL &&
indexInfo->ii_ExpressionsState == NIL)
{
/* First time through, set up expression evaluation state */
indexInfo->ii_ExpressionsState = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
estate);
/* Check caller has set up context correctly */
Assert(GetPerTupleExprContext(estate)->ecxt_scantuple->val == heapTuple);
}
indexprs = indexInfo->ii_ExpressionsState;
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
int keycol = indexInfo->ii_KeyAttrNumbers[i];
Datum iDatum;
bool isNull;
1998-08-28 06:57:21 +02:00
if (keycol != 0)
{
/*
* Plain index column; get the value we need directly from the
* heap tuple.
*/
iDatum = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull);
}
else
{
/*
* Index expression --- need to evaluate it.
*/
if (indexprs == NIL)
elog(ERROR, "wrong number of index expressions");
iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexprs),
2003-08-04 02:43:34 +02:00
GetPerTupleExprContext(estate),
&isNull,
NULL);
indexprs = lnext(indexprs);
}
datum[i] = iDatum;
nullv[i] = (isNull) ? 'n' : ' ';
}
if (indexprs != NIL)
elog(ERROR, "wrong number of index expressions");
}
2000-02-18 10:30:20 +01:00
/* ---------------------------------------------
* Indexes of the relation active ?
*
* Caller must hold an adequate lock on the relation to ensure the
* answer won't be changing.
2000-02-18 10:30:20 +01:00
* ---------------------------------------------
*/
bool
IndexesAreActive(Relation heaprel)
2000-02-18 10:30:20 +01:00
{
bool isactive;
2000-02-18 10:30:20 +01:00
Relation indexRelation;
HeapScanDesc scan;
ScanKeyData entry;
2000-02-18 10:30:20 +01:00
if (heaprel->rd_rel->relkind != RELKIND_RELATION &&
heaprel->rd_rel->relkind != RELKIND_TOASTVALUE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("relation \"%s\" isn't an indexable relation",
RelationGetRelationName(heaprel))));
/* If pg_class.relhasindex is set, indexes are active */
isactive = heaprel->rd_rel->relhasindex;
2000-02-18 10:30:20 +01:00
if (isactive)
return isactive;
/* Otherwise, look to see if there are any indexes */
2000-02-18 10:30:20 +01:00
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0,
Anum_pg_index_indrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(heaprel)));
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
if (heap_getnext(scan, ForwardScanDirection) == NULL)
isactive = true; /* no indexes, so report "active" */
2000-02-18 10:30:20 +01:00
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
2000-02-18 10:30:20 +01:00
return isactive;
}
/* ----------------
* set relhasindex of relation's pg_class entry
*
* If isprimary is TRUE, we are defining a primary index, so also set
* relhaspkey to TRUE. Otherwise, leave relhaspkey alone.
*
* If reltoastidxid is not InvalidOid, also set reltoastidxid to that value.
* This is only used for TOAST relations.
*
* NOTE: an important side-effect of this operation is that an SI invalidation
* message is sent out to all backends --- including me --- causing relcache
* entries to be flushed or updated with the new hasindex data. This must
* happen even if we find that no change is needed in the pg_class row.
2000-02-18 10:30:20 +01:00
* ----------------
*/
void
setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
2000-02-18 10:30:20 +01:00
{
Relation pg_class;
HeapTuple tuple;
Form_pg_class classtuple;
bool dirty = false;
HeapScanDesc pg_class_scan = NULL;
2000-02-18 10:30:20 +01:00
/*
* Find the tuple to update in pg_class.
2000-02-18 10:30:20 +01:00
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
if (!IsIgnoringSystemIndexes() &&
(!IsReindexProcessing() || pg_class->rd_rel->relhasindex))
2000-02-18 10:30:20 +01:00
{
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
2000-02-18 10:30:20 +01:00
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(relid));
pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
2000-02-18 10:30:20 +01:00
}
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u", relid);
2000-02-18 10:30:20 +01:00
/*
* Update fields in the pg_class tuple.
2000-02-18 10:30:20 +01:00
*/
2000-12-08 07:17:58 +01:00
if (pg_class_scan)
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
classtuple = (Form_pg_class) GETSTRUCT(tuple);
if (classtuple->relhasindex != hasindex)
{
classtuple->relhasindex = hasindex;
dirty = true;
}
if (isprimary)
{
if (!classtuple->relhaspkey)
{
classtuple->relhaspkey = true;
dirty = true;
}
}
if (OidIsValid(reltoastidxid))
{
Assert(classtuple->relkind == RELKIND_TOASTVALUE);
if (classtuple->reltoastidxid != reltoastidxid)
{
classtuple->reltoastidxid = reltoastidxid;
dirty = true;
}
}
2000-12-08 07:17:58 +01:00
if (pg_class_scan)
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
2000-02-18 10:30:20 +01:00
if (pg_class_scan)
{
/* Write the modified tuple in-place */
2000-02-18 10:30:20 +01:00
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
/* Send out shared cache inval if necessary */
if (!IsBootstrapProcessingMode())
CacheInvalidateHeapTuple(pg_class, tuple);
2000-12-08 07:17:58 +01:00
BufferSync();
2000-02-18 10:30:20 +01:00
}
else if (dirty)
2000-02-18 10:30:20 +01:00
{
simple_heap_update(pg_class, &tuple->t_self, tuple);
2000-02-18 10:30:20 +01:00
/* Keep the catalog indexes up to date */
CatalogUpdateIndexes(pg_class, tuple);
}
else
{
/* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relid);
}
2000-02-18 10:30:20 +01:00
if (!pg_class_scan)
heap_freetuple(tuple);
else
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
2000-02-18 10:30:20 +01:00
}
/*
* setNewRelfilenode - assign a new relfilenode value to the relation
*
* Caller must already hold exclusive lock on the relation.
*/
2000-12-08 07:17:58 +01:00
void
setNewRelfilenode(Relation relation)
{
2001-03-22 05:01:46 +01:00
Oid newrelfilenode;
Relation pg_class;
HeapTuple tuple;
Form_pg_class rd_rel;
HeapScanDesc pg_class_scan = NULL;
bool in_place_upd;
2001-03-22 05:01:46 +01:00
RelationData workrel;
Assert(!IsSystemRelation(relation) || IsToastRelation(relation) ||
relation->rd_rel->relkind == RELKIND_INDEX);
2000-12-08 07:17:58 +01:00
/* Allocate a new relfilenode */
newrelfilenode = newoid();
/*
* Find the RELATION relation tuple for the given relation.
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
in_place_upd = IsIgnoringSystemIndexes();
if (!in_place_upd)
2000-12-08 07:17:58 +01:00
{
tuple = SearchSysCacheCopy(RELOID,
2003-08-04 02:43:34 +02:00
ObjectIdGetDatum(RelationGetRelid(relation)),
0, 0, 0);
2000-12-08 07:17:58 +01:00
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
2003-08-04 02:43:34 +02:00
ObjectIdGetDatum(RelationGetRelid(relation)));
pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
}
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u",
RelationGetRelid(relation));
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
/* schedule unlinking old relfilenode */
2000-12-08 07:17:58 +01:00
smgrunlink(DEFAULT_SMGR, relation);
2000-12-08 07:17:58 +01:00
/* create another storage file. Is it a little ugly ? */
memcpy((char *) &workrel, relation, sizeof(RelationData));
workrel.rd_fd = -1;
2000-12-08 07:17:58 +01:00
workrel.rd_node.relNode = newrelfilenode;
heap_storage_create(&workrel);
2001-01-18 08:29:04 +01:00
smgrclose(DEFAULT_SMGR, &workrel);
/* update the pg_class row */
if (in_place_upd)
2000-12-08 07:17:58 +01:00
{
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
rd_rel->relfilenode = newrelfilenode;
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
BufferSync();
2000-12-08 07:17:58 +01:00
/* Send out shared cache inval if necessary */
if (!IsBootstrapProcessingMode())
CacheInvalidateHeapTuple(pg_class, tuple);
}
else
{
rd_rel->relfilenode = newrelfilenode;
simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, tuple);
2000-12-08 07:17:58 +01:00
}
if (!pg_class_scan)
heap_freetuple(tuple);
else
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
/* Make sure the relfilenode change is visible */
2000-12-08 07:17:58 +01:00
CommandCounterIncrement();
}
2001-03-22 05:01:46 +01:00
2000-12-08 07:17:58 +01:00
/* ----------------
* UpdateStats
*
* Update pg_class' relpages and reltuples statistics for the given relation
* (which can be either a table or an index). Note that this is not used
* in the context of VACUUM.
* ----------------
*/
void
UpdateStats(Oid relid, double reltuples)
{
Relation whichRel;
Relation pg_class;
HeapTuple tuple;
BlockNumber relpages;
Form_pg_class rd_rel;
HeapScanDesc pg_class_scan = NULL;
2000-02-18 10:30:20 +01:00
bool in_place_upd;
/*
* This routine handles updates for both the heap and index relation
* statistics. In order to guarantee that we're able to *see* the
* index relation tuple, we bump the command counter id here. The
* index relation tuple was created in the current transaction.
*/
CommandCounterIncrement();
/*
* CommandCounterIncrement() flushes invalid cache entries, including
* those for the heap and index relations for which we're updating
* statistics. Now that the cache is flushed, it's safe to open the
* relation again. We need the relation open in order to figure out
* how many blocks it contains.
*/
/*
* Grabbing lock here is probably redundant ...
*/
whichRel = relation_open(relid, ShareLock);
/*
* Find the RELATION relation tuple for the given relation.
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
in_place_upd = (IsIgnoringSystemIndexes() || IsReindexProcessing());
2000-02-18 10:30:20 +01:00
if (!in_place_upd)
1998-08-20 17:16:59 +02:00
{
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
1998-08-20 17:16:59 +02:00
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(relid));
1998-08-20 17:16:59 +02:00
pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
1998-08-20 17:16:59 +02:00
}
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u", relid);
/*
* Figure values to insert.
*
* If we found zero tuples in the scan, do NOT believe it; instead put a
* bogus estimate into the statistics fields. Otherwise, the common
* pattern "CREATE TABLE; CREATE INDEX; insert data" leaves the table
* with zero size statistics until a VACUUM is done. The optimizer
* will generate very bad plans if the stats claim the table is empty
* when it is actually sizable. See also CREATE TABLE in heap.c.
*
* Note: this path is also taken during bootstrap, because bootstrap.c
* passes reltuples = 0 after loading a table. We have to estimate
* some number for reltuples based on the actual number of pages.
*/
relpages = RelationGetNumberOfBlocks(whichRel);
if (reltuples == 0)
{
if (relpages == 0)
{
/* Bogus defaults for a virgin table, same as heap.c */
reltuples = 1000;
relpages = 10;
}
else if (whichRel->rd_rel->relkind == RELKIND_INDEX && relpages <= 2)
{
/* Empty index, leave bogus defaults in place */
reltuples = 1000;
}
else
reltuples = ((double) relpages) * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
}
/*
2003-08-04 02:43:34 +02:00
* Update statistics in pg_class, if they changed. (Avoiding an
* unnecessary update is not just a tiny performance improvement; it
* also reduces the window wherein concurrent CREATE INDEX commands
* may conflict.)
*/
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
if (rd_rel->relpages != (int32) relpages ||
rd_rel->reltuples != (float4) reltuples)
{
if (in_place_upd)
{
/*
2003-08-04 02:43:34 +02:00
* At bootstrap time, we don't need to worry about concurrency
* or visibility of changes, so we cheat. Also cheat if
* REINDEX.
*/
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
rd_rel->relpages = (int32) relpages;
rd_rel->reltuples = (float4) reltuples;
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
if (!IsBootstrapProcessingMode())
CacheInvalidateHeapTuple(pg_class, tuple);
}
else
{
/* During normal processing, must work harder. */
rd_rel->relpages = (int32) relpages;
rd_rel->reltuples = (float4) reltuples;
simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, tuple);
}
}
2000-02-18 10:30:20 +01:00
if (!pg_class_scan)
heap_freetuple(tuple);
1998-08-20 17:16:59 +02:00
else
heap_endscan(pg_class_scan);
/*
* We shouldn't have to do this, but we do... Modify the reldesc in
* place with the new values so that the cache contains the latest
2003-08-04 02:43:34 +02:00
* copy. (XXX is this really still necessary? The relcache will get
* fixed at next CommandCounterIncrement, so why bother here?)
*/
whichRel->rd_rel->relpages = (int32) relpages;
whichRel->rd_rel->reltuples = (float4) reltuples;
heap_close(pg_class, RowExclusiveLock);
relation_close(whichRel, NoLock);
}
/*
* index_build - invoke access-method-specific index build procedure
*/
void
index_build(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
RegProcedure procedure;
/*
* sanity checks
*/
Assert(RelationIsValid(indexRelation));
Assert(PointerIsValid(indexRelation->rd_am));
procedure = indexRelation->rd_am->ambuild;
Assert(RegProcedureIsValid(procedure));
/*
* Call the access method's build procedure
*/
OidFunctionCall3(procedure,
PointerGetDatum(heapRelation),
PointerGetDatum(indexRelation),
PointerGetDatum(indexInfo));
}
/*
* IndexBuildHeapScan - scan the heap relation to find tuples to be indexed
*
* This is called back from an access-method-specific index build procedure
* after the AM has done whatever setup it needs. The parent heap relation
* is scanned to find tuples that should be entered into the index. Each
* such tuple is passed to the AM's callback routine, which does the right
* things to add it to the new index. After we return, the AM's index
* build procedure does whatever cleanup is needed; in particular, it should
* close the heap and index relations.
*
* The total count of heap tuples is returned. This is for updating pg_class
* statistics. (It's annoying not to be able to do that here, but we can't
* do it until after the relation is closed.) Note that the index AM itself
* must keep track of the number of index tuples; we don't do so here because
* the AM might reject some of the tuples for its own reasons, such as being
* unable to store NULLs.
*/
double
IndexBuildHeapScan(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo,
IndexBuildCallback callback,
void *callback_state)
{
HeapScanDesc scan;
HeapTuple heapTuple;
TupleDesc heapDescriptor;
Datum attdata[INDEX_MAX_KEYS];
char nulls[INDEX_MAX_KEYS];
double reltuples;
List *predicate;
TupleTable tupleTable;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
Snapshot snapshot;
TransactionId OldestXmin;
/*
* sanity checks
*/
Assert(OidIsValid(indexRelation->rd_rel->relam));
1998-09-01 05:29:17 +02:00
heapDescriptor = RelationGetDescr(heapRelation);
/*
2003-08-04 02:43:34 +02:00
* Need an EState for evaluation of index expressions and
* partial-index predicates.
*/
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
/*
* If this is a predicate (partial) index, we will need to evaluate
* the predicate using ExecQual, which requires the current tuple to
2003-08-04 02:43:34 +02:00
* be in a slot of a TupleTable. Likewise if there are any
* expressions.
*/
if (indexInfo->ii_Predicate != NIL || indexInfo->ii_Expressions != NIL)
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, heapDescriptor, false);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
}
1996-11-08 01:46:14 +01:00
else
{
tupleTable = NULL;
1996-11-08 01:46:14 +01:00
slot = NULL;
predicate = NIL;
1996-11-08 01:46:14 +01:00
}
/*
* Ok, begin our scan of the base relation. We use SnapshotAny
* because we must retrieve all tuples and do our own time qual
* checks.
*/
if (IsBootstrapProcessingMode())
{
snapshot = SnapshotNow;
OldestXmin = InvalidTransactionId;
}
else
{
snapshot = SnapshotAny;
OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared);
}
scan = heap_beginscan(heapRelation, /* relation */
snapshot, /* seeself */
0, /* number of keys */
(ScanKey) NULL); /* scan key */
reltuples = 0;
/*
* Scan all tuples in the base relation.
*/
while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
CHECK_FOR_INTERRUPTS();
if (snapshot == SnapshotAny)
{
/* do our own time qual check */
bool indexIt;
uint16 sv_infomask;
/*
* HeapTupleSatisfiesVacuum may update tuple's hint status
* bits. We could possibly get away with not locking the
* buffer here, since caller should hold ShareLock on the
* relation, but let's be conservative about it.
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
sv_infomask = heapTuple->t_data->t_infomask;
2001-03-22 05:01:46 +01:00
switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin))
{
case HEAPTUPLE_DEAD:
indexIt = false;
tupleIsAlive = false;
break;
case HEAPTUPLE_LIVE:
indexIt = true;
tupleIsAlive = true;
break;
case HEAPTUPLE_RECENTLY_DEAD:
/*
* If tuple is recently deleted then we must index it
* anyway to keep VACUUM from complaining.
*/
indexIt = true;
tupleIsAlive = false;
break;
case HEAPTUPLE_INSERT_IN_PROGRESS:
/*
* Since caller should hold ShareLock or better, we
* should not see any tuples inserted by open
* transactions --- unless it's our own transaction.
* (Consider INSERT followed by CREATE INDEX within a
* transaction.)
*/
if (!TransactionIdIsCurrentTransactionId(
2002-09-04 22:31:48 +02:00
HeapTupleHeaderGetXmin(heapTuple->t_data)))
elog(ERROR, "concurrent insert in progress");
indexIt = true;
tupleIsAlive = true;
break;
case HEAPTUPLE_DELETE_IN_PROGRESS:
/*
* Since caller should hold ShareLock or better, we
* should not see any tuples deleted by open
* transactions --- unless it's our own transaction.
* (Consider DELETE followed by CREATE INDEX within a
* transaction.)
*/
if (!TransactionIdIsCurrentTransactionId(
2002-09-04 22:31:48 +02:00
HeapTupleHeaderGetXmax(heapTuple->t_data)))
elog(ERROR, "concurrent delete in progress");
indexIt = true;
tupleIsAlive = false;
break;
default:
elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
indexIt = tupleIsAlive = false; /* keep compiler quiet */
break;
}
/* check for hint-bit update by HeapTupleSatisfiesVacuum */
if (sv_infomask != heapTuple->t_data->t_infomask)
SetBufferCommitInfoNeedsSave(scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
if (!indexIt)
continue;
}
else
{
/* heap_getnext did the time qual check */
tupleIsAlive = true;
}
reltuples += 1;
MemoryContextReset(econtext->ecxt_per_tuple_memory);
2003-05-29 02:54:42 +02:00
/* Set up for predicate or expression evaluation */
if (slot)
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/*
* In a partial index, discard tuples that don't satisfy the
* predicate. We can also discard recently-dead tuples, since
* VACUUM doesn't complain about tuple count mismatch for partial
* indexes.
*/
if (predicate != NIL)
{
if (!tupleIsAlive)
continue;
if (!ExecQual(predicate, econtext, false))
continue;
}
/*
* For the current heap tuple, extract all the attributes we use
* in this index, and note which are null. This also performs
* evaluation of any expressions needed.
*/
FormIndexDatum(indexInfo,
heapTuple,
heapDescriptor,
estate,
attdata,
nulls);
/*
* You'd think we should go ahead and build the index tuple here,
* but some index AMs want to do further processing on the data
* first. So pass the attdata and nulls arrays, instead.
*/
/* Call the AM's callback routine to process the tuple */
callback(indexRelation, heapTuple, attdata, nulls, tupleIsAlive,
callback_state);
}
heap_endscan(scan);
if (tupleTable)
ExecDropTupleTable(tupleTable, true);
FreeExecutorState(estate);
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
return reltuples;
}
/*
* IndexGetRelation: given an index's relation OID, get the OID of the
* relation it is an index on. Uses the system cache.
*/
static Oid
IndexGetRelation(Oid indexId)
{
HeapTuple tuple;
Form_pg_index index;
Oid result;
tuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u", indexId);
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
result = index->indrelid;
ReleaseSysCache(tuple);
return result;
}
/* ---------------------------------
* activate_index -- activate/deactivate the specified index.
* Note that currently PostgreSQL doesn't hold the
* status per index
* ---------------------------------
*/
static bool
activate_index(Oid indexId, bool activate, bool inplace)
{
if (!activate) /* Currently does nothing */
return true;
return reindex_index(indexId, false, inplace);
}
/* --------------------------------
2000-02-18 10:30:20 +01:00
* reindex_index - This routine is used to recreate an index
* --------------------------------
2000-02-18 10:30:20 +01:00
*/
bool
2000-12-08 07:17:58 +01:00
reindex_index(Oid indexId, bool force, bool inplace)
2000-02-18 10:30:20 +01:00
{
Relation iRel,
heapRelation;
IndexInfo *indexInfo;
Oid heapId;
2000-02-18 10:30:20 +01:00
bool old;
/*
* Open our index relation and get an exclusive lock on it.
*
2002-09-04 22:31:48 +02:00
* Note: doing this before opening the parent heap relation means there's
* a possibility for deadlock failure against another xact that is
* doing normal accesses to the heap and index. However, it's not
* real clear why you'd be needing to do REINDEX on a table that's in
* active use, so I'd rather have the protection of making sure the
* index is locked down.
*/
iRel = index_open(indexId);
LockRelation(iRel, AccessExclusiveLock);
2000-02-18 10:30:20 +01:00
old = SetReindexProcessing(true);
/* Get OID of index's parent table */
heapId = iRel->rd_index->indrelid;
2000-02-18 10:30:20 +01:00
/* Open the parent heap relation */
heapRelation = heap_open(heapId, AccessExclusiveLock);
2000-02-18 10:30:20 +01:00
/*
* If it's a shared index, we must do inplace processing (because we
* have no way to update relfilenode in other databases). Also, if
* it's a nailed-in-cache index, we must do inplace processing because
* the relcache can't cope with changing its relfilenode.
*
2003-08-04 02:43:34 +02:00
* In either of these cases, we are definitely processing a system index,
* so we'd better be ignoring system indexes.
*/
if (iRel->rd_rel->relisshared)
{
if (!IsIgnoringSystemIndexes())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the target relation %u is shared", indexId)));
inplace = true;
}
#ifndef ENABLE_REINDEX_NAILED_RELATIONS
if (iRel->rd_isnailed)
{
if (!IsIgnoringSystemIndexes())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the target relation %u is nailed", indexId)));
inplace = true;
}
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
/* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(iRel);
2000-02-18 10:30:20 +01:00
2000-12-08 07:17:58 +01:00
if (inplace)
{
/*
2001-03-22 05:01:46 +01:00
* Release any buffers associated with this index. If they're
* dirty, they're just dropped without bothering to flush to disk.
*/
2000-12-08 07:17:58 +01:00
DropRelationBuffers(iRel);
/* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, iRel, 0);
iRel->rd_nblocks = 0;
iRel->rd_targblock = InvalidBlockNumber;
2000-12-08 07:17:58 +01:00
}
else
{
/*
* We'll build a new physical relation for the index.
*/
setNewRelfilenode(iRel);
}
2000-02-18 10:30:20 +01:00
/* Initialize the index and rebuild */
index_build(heapRelation, iRel, indexInfo);
2000-02-18 10:30:20 +01:00
/*
* index_build will close both the heap and index relations (but not
2001-03-22 05:01:46 +01:00
* give up the locks we hold on them). So we're done.
2000-02-18 10:30:20 +01:00
*/
SetReindexProcessing(old);
2000-02-18 10:30:20 +01:00
return true;
}
/*
* ----------------------------
* activate_indexes_of_a_table
2000-02-18 10:30:20 +01:00
* activate/deactivate indexes of the specified table.
*
* Caller must already hold exclusive lock on the table.
* ----------------------------
2000-02-18 10:30:20 +01:00
*/
bool
activate_indexes_of_a_table(Relation heaprel, bool activate)
2000-02-18 10:30:20 +01:00
{
if (IndexesAreActive(heaprel))
2000-02-18 10:30:20 +01:00
{
if (!activate)
setRelhasindex(RelationGetRelid(heaprel), false, false,
InvalidOid);
2000-02-18 10:30:20 +01:00
else
return false;
}
else
{
if (activate)
reindex_relation(RelationGetRelid(heaprel), false);
2000-02-18 10:30:20 +01:00
else
return false;
}
return true;
}
/* --------------------------------
* reindex_relation - This routine is used to recreate indexes
2000-02-18 10:30:20 +01:00
* of a relation.
* --------------------------------
2000-02-18 10:30:20 +01:00
*/
bool
reindex_relation(Oid relid, bool force)
{
Relation indexRelation;
ScanKeyData entry;
HeapScanDesc scan;
2000-02-18 10:30:20 +01:00
HeapTuple indexTuple;
bool old,
reindexed;
bool deactivate_needed,
overwrite;
2001-03-22 05:01:46 +01:00
Relation rel;
overwrite = deactivate_needed = false;
2001-03-22 05:01:46 +01:00
/*
* Ensure to hold an exclusive lock throughout the transaction. The
* lock could be less intensive (in the non-overwrite path) but for
* now it's AccessExclusiveLock for simplicity.
*/
rel = heap_open(relid, AccessExclusiveLock);
2000-12-08 07:17:58 +01:00
/*
* ignore the indexes of the target system relation while processing
* reindex.
2001-03-22 05:01:46 +01:00
*/
if (!IsIgnoringSystemIndexes() &&
IsSystemRelation(rel) && !IsToastRelation(rel))
deactivate_needed = true;
2001-03-22 05:01:46 +01:00
/*
2001-03-22 05:01:46 +01:00
* Shared system indexes must be overwritten because it's impossible
* to update pg_class tuples of all databases.
*/
Clean up various to-do items associated with system indexes: pg_database now has unique indexes on oid and on datname. pg_shadow now has unique indexes on usename and on usesysid. pg_am now has unique index on oid. pg_opclass now has unique index on oid. pg_amproc now has unique index on amid+amopclaid+amprocnum. Remove pg_rewrite's unnecessary index on oid, delete unused RULEOID syscache. Remove index on pg_listener and associated syscache for performance reasons (caching rows that are certain to change before you need 'em again is rather pointless). Change pg_attrdef's nonunique index on adrelid into a unique index on adrelid+adnum. Fix various incorrect settings of pg_class.relisshared, make that the primary reference point for whether a relation is shared or not. IsSharedSystemRelationName() is now only consulted to initialize relisshared during initial creation of tables and indexes. In theory we might now support shared user relations, though it's not clear how one would get entries for them into pg_class &etc of multiple databases. Fix recently reported bug that pg_attribute rows created for an index all have the same OID. (Proof that non-unique OID doesn't matter unless it's actually used to do lookups ;-)) There's no need to treat pg_trigger, pg_attrdef, pg_relcheck as bootstrap relations. Convert them into plain system catalogs without hardwired entries in pg_class and friends. Unify global.bki and template1.bki into a single init script postgres.bki, since the alleged distinction between them was misleading and pointless. Not to mention that it didn't work for setting up indexes on shared system relations. Rationalize locking of pg_shadow, pg_group, pg_attrdef (no need to use AccessExclusiveLock where ExclusiveLock or even RowExclusiveLock will do). Also, hold locks until transaction commit where necessary.
2001-06-12 07:55:50 +02:00
if (rel->rd_rel->relisshared)
{
if (IsIgnoringSystemIndexes())
{
overwrite = true;
deactivate_needed = true;
}
else
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the target relation %u is shared", relid)));
}
2000-02-18 10:30:20 +01:00
old = SetReindexProcessing(true);
if (deactivate_needed)
{
if (IndexesAreActive(rel))
{
if (!force)
{
SetReindexProcessing(old);
heap_close(rel, NoLock);
return false;
}
activate_indexes_of_a_table(rel, false);
CommandCounterIncrement();
}
}
/*
* Continue to hold the lock.
*/
heap_close(rel, NoLock);
2000-02-18 10:30:20 +01:00
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid));
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
2000-02-18 10:30:20 +01:00
reindexed = false;
while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2000-02-18 10:30:20 +01:00
{
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
if (activate_index(index->indexrelid, true, overwrite))
2000-02-18 10:30:20 +01:00
reindexed = true;
else
{
reindexed = false;
break;
}
}
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
if (reindexed)
{
/*
* Ok,we could use the reindexed indexes of the target system
* relation now.
*/
if (deactivate_needed)
{
if (!overwrite && relid == RelOid_pg_class)
{
/*
* For pg_class, relhasindex should be set to true here in
* place.
*/
setRelhasindex(relid, true, false, InvalidOid);
CommandCounterIncrement();
2001-03-22 05:01:46 +01:00
/*
* However the following setRelhasindex() is needed to
* keep consistency with WAL.
*/
}
setRelhasindex(relid, true, false, InvalidOid);
}
}
2000-02-18 10:30:20 +01:00
SetReindexProcessing(old);
2000-12-08 07:17:58 +01:00
return reindexed;
2000-02-18 10:30:20 +01:00
}