* index.c
* code to create and destroy POSTGRES index relations
*
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2002, 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.152 2001/05/30 20:52:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.196 2002/09/04 20:31:14 momjian Exp $
*
*
* INTERFACE ROUTINES
#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"
#include "catalog/pg_index.h"
+#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "commands/comment.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
#include "optimizer/prep.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"
-#include "utils/temprel.h"
+
/*
* macros used in guessing how many tuples are on a page.
((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData))))
/* non-export function prototypes */
-static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName,
- bool istemp);
-static TupleDesc BuildFuncTupleDesc(Oid funcOid);
+static TupleDesc BuildFuncTupleDesc(Oid funcOid,
+ Oid *classObjectId);
static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
- int numatts, AttrNumber *attNums);
-static void ConstructIndexReldesc(Relation indexRelation, Oid amoid);
-static Oid UpdateRelationRelation(Relation indexRelation, char *temp_relname);
+ int numatts, AttrNumber *attNums,
+ 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 islossy, bool primary);
-static void DefaultBuild(Relation heapRelation, Relation indexRelation,
- IndexInfo *indexInfo, Node *oldPred,
- IndexStrategy indexStrategy);
+ bool primary);
static Oid IndexGetRelation(Oid indexId);
static bool activate_index(Oid indexId, bool activate, bool inplace);
return reindexing;
}
-/* ----------------------------------------------------------------
- * GetHeapRelationOid
- * ----------------------------------------------------------------
- */
-static Oid
-GetHeapRelationOid(char *heapRelationName, char *indexRelationName, bool istemp)
-{
- Oid indoid;
- Oid heapoid;
-
-
- indoid = RelnameFindRelid(indexRelationName);
-
- if ((!istemp && OidIsValid(indoid)) ||
- (istemp && is_temp_rel_name(indexRelationName)))
- elog(ERROR, "Cannot create index: '%s' already exists",
- indexRelationName);
-
- heapoid = RelnameFindRelid(heapRelationName);
-
- if (!OidIsValid(heapoid))
- elog(ERROR, "Cannot create index on '%s': relation does not exist",
- heapRelationName);
-
- return heapoid;
-}
-
static TupleDesc
-BuildFuncTupleDesc(Oid funcOid)
+BuildFuncTupleDesc(Oid funcOid,
+ Oid *classObjectId)
{
TupleDesc funcTupDesc;
HeapTuple tuple;
+ Oid keyType;
Oid retType;
+ Form_pg_type typeTup;
/*
* Allocate and zero a tuple descriptor for a one-column tuple.
*/
- funcTupDesc = CreateTemplateTupleDesc(1);
+ funcTupDesc = CreateTemplateTupleDesc(1, false);
funcTupDesc->attrs[0] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
MemSet(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE);
ReleaseSysCache(tuple);
/*
- * Lookup the return type in pg_type for the type length etc.
+ * Check the opclass to see if it provides a keytype (overriding the
+ * function result type).
+ */
+ tuple = SearchSysCache(CLAOID,
+ ObjectIdGetDatum(classObjectId[0]),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "Opclass %u does not exist", classObjectId[0]);
+ keyType = ((Form_pg_opclass) GETSTRUCT(tuple))->opckeytype;
+ ReleaseSysCache(tuple);
+
+ if (!OidIsValid(keyType))
+ keyType = retType;
+
+ /*
+ * Lookup the key type in pg_type for the type length etc.
*/
tuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(retType),
+ ObjectIdGetDatum(keyType),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
- elog(ERROR, "Type %u does not exist", retType);
+ elog(ERROR, "Type %u does not exist", keyType);
+ typeTup = (Form_pg_type) GETSTRUCT(tuple);
/*
* Assign some of the attributes values. Leave the rest as 0.
*/
- funcTupDesc->attrs[0]->attlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
- funcTupDesc->attrs[0]->atttypid = retType;
funcTupDesc->attrs[0]->attnum = 1;
- funcTupDesc->attrs[0]->attbyval = ((Form_pg_type) GETSTRUCT(tuple))->typbyval;
+ funcTupDesc->attrs[0]->atttypid = keyType;
+ funcTupDesc->attrs[0]->attlen = typeTup->typlen;
+ funcTupDesc->attrs[0]->attbyval = typeTup->typbyval;
funcTupDesc->attrs[0]->attcacheoff = -1;
funcTupDesc->attrs[0]->atttypmod = -1;
- funcTupDesc->attrs[0]->attstorage = ((Form_pg_type) GETSTRUCT(tuple))->typstorage;
- funcTupDesc->attrs[0]->attalign = ((Form_pg_type) GETSTRUCT(tuple))->typalign;
+ funcTupDesc->attrs[0]->attstorage = typeTup->typstorage;
+ funcTupDesc->attrs[0]->attalign = typeTup->typalign;
ReleaseSysCache(tuple);
static TupleDesc
ConstructTupleDescriptor(Relation heapRelation,
int numatts,
- AttrNumber *attNums)
+ AttrNumber *attNums,
+ Oid *classObjectId)
{
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
* allocate the new tuple descriptor
*/
- indexTupDesc = CreateTemplateTupleDesc(numatts);
+ indexTupDesc = CreateTemplateTupleDesc(numatts, false);
/* ----------------
* for each attribute we are indexing, obtain its attribute
AttrNumber atnum; /* attributeNumber[attributeOffset] */
Form_pg_attribute from;
Form_pg_attribute to;
+ HeapTuple tuple;
+ Oid keyType;
/*
* get the attribute number and make sure it's valid; determine
/*
* here we are indexing on a system attribute (-1...-n)
*/
- from = SystemAttributeDefinition(atnum);
+ from = SystemAttributeDefinition(atnum,
+ heapRelation->rd_rel->relhasoids);
}
else
{
* here we are indexing on a normal attribute (1...n)
*/
if (atnum > natts)
- elog(ERROR, "Cannot create index: attribute %d does not exist",
+ elog(ERROR, "cannot create index: column %d does not exist",
atnum);
from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)];
to->attcacheoff = -1;
to->attnotnull = false;
to->atthasdef = false;
+ to->attisinherited = false;
/*
* We do not yet have the correct relation OID for the index, so
* fix it later.
*/
to->attrelid = InvalidOid;
- }
- return indexTupDesc;
-}
-
-/* ----------------------------------------------------------------
- * AccessMethodObjectIdGetForm
- * Returns an access method tuple given its object identifier,
- * or NULL if no such AM tuple can be found.
- *
- * Scanning is done using CurrentMemoryContext as working storage,
- * but the returned tuple will be allocated in resultCxt (which is
- * typically CacheMemoryContext).
- *
- * There was a note here about adding indexing, but I don't see a need
- * for it. There are so few tuples in pg_am that an indexscan would
- * surely be slower.
- * ----------------------------------------------------------------
- */
-Form_pg_am
-AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
- MemoryContext resultCxt)
-{
- Relation pg_am_desc;
- HeapScanDesc pg_am_scan;
- HeapTuple pg_am_tuple;
- ScanKeyData key;
- Form_pg_am aform;
-
- /*
- * form a scan key for the pg_am relation
- */
- ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
- F_OIDEQ,
- ObjectIdGetDatum(accessMethodObjectId));
-
- /*
- * fetch the desired access method tuple
- */
- pg_am_desc = heap_openr(AccessMethodRelationName, AccessShareLock);
- pg_am_scan = heap_beginscan(pg_am_desc, 0, SnapshotNow, 1, &key);
+ /*
+ * 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, "Opclass %u does not exist", classObjectId[i]);
+ keyType = ((Form_pg_opclass) GETSTRUCT(tuple))->opckeytype;
+ ReleaseSysCache(tuple);
- pg_am_tuple = heap_getnext(pg_am_scan, 0);
+ if (OidIsValid(keyType) && keyType != to->atttypid)
+ {
+ /* index value and heap value have different types */
+ Form_pg_type typeTup;
- /*
- * return NULL if not found
- */
- if (!HeapTupleIsValid(pg_am_tuple))
- {
- heap_endscan(pg_am_scan);
- heap_close(pg_am_desc, AccessShareLock);
- return NULL;
+ tuple = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(keyType),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "Type %u does not exist", 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);
+ }
}
- /*
- * if found AM tuple, then copy it into resultCxt and return the copy
- */
- aform = (Form_pg_am) MemoryContextAlloc(resultCxt, sizeof *aform);
- memcpy(aform, GETSTRUCT(pg_am_tuple), sizeof *aform);
-
- heap_endscan(pg_am_scan);
- heap_close(pg_am_desc, AccessShareLock);
-
- return aform;
-}
-
-/* ----------------------------------------------------------------
- * ConstructIndexReldesc
- * ----------------------------------------------------------------
- */
-static void
-ConstructIndexReldesc(Relation indexRelation, Oid amoid)
-{
- indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid,
- CacheMemoryContext);
-
- /*
- * XXX missing the initialization of some other fields
- */
-
- indexRelation->rd_rel->relowner = GetUserId();
-
- indexRelation->rd_rel->relam = amoid;
- indexRelation->rd_rel->reltuples = 1; /* XXX */
- indexRelation->rd_rel->relkind = RELKIND_INDEX;
+ return indexTupDesc;
}
/* ----------------------------------------------------------------
* UpdateRelationRelation
* ----------------------------------------------------------------
*/
-static Oid
-UpdateRelationRelation(Relation indexRelation, char *temp_relname)
+static void
+UpdateRelationRelation(Relation indexRelation)
{
Relation pg_class;
HeapTuple tuple;
- Oid tupleOid;
- Relation idescs[Num_pg_class_indices];
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,
+ true,
CLASS_TUPLE_SIZE,
- (char *) indexRelation->rd_rel);
+ (void *) indexRelation->rd_rel);
/*
- * the new tuple must have the same oid as the relcache entry for the
- * index. sure would be embarrassing to do this sort of thing in
- * polite company.
+ * 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.
*/
- tuple->t_data->t_oid = RelationGetRelid(indexRelation);
- heap_insert(pg_class, tuple);
+ HeapTupleSetOid(tuple, RelationGetRelid(indexRelation));
+ simple_heap_insert(pg_class, tuple);
- if (temp_relname)
- create_temp_relation(temp_relname, tuple);
+ /* update the system catalog indexes */
+ CatalogUpdateIndexes(pg_class, tuple);
- /*
- * During normal processing, we need to make sure that the system
- * catalog indices are correct. Bootstrap (initdb) time doesn't
- * require this, because we make sure that the indices are correct
- * just before exiting.
- */
-
- if (!IsIgnoringSystemIndexes())
- {
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
- }
-
- tupleOid = tuple->t_data->t_oid;
heap_freetuple(tuple);
heap_close(pg_class, RowExclusiveLock);
-
- return tupleOid;
}
/* ----------------------------------------------------------------
/* ----------------------------------------------------------------
* AppendAttributeTuples
- *
- * XXX For now, only change the ATTNUM attribute value
* ----------------------------------------------------------------
*/
static void
AppendAttributeTuples(Relation indexRelation, int numatts)
{
Relation pg_attribute;
- HeapTuple init_tuple,
- cur_tuple = NULL,
- new_tuple;
- bool hasind;
- Relation idescs[Num_pg_attr_indices];
- Datum value[Natts_pg_attribute];
- char nullv[Natts_pg_attribute];
- char replace[Natts_pg_attribute];
+ CatalogIndexState indstate;
TupleDesc indexTupDesc;
+ HeapTuple new_tuple;
int i;
/*
- * open the attribute relation
+ * open the attribute relation and its indexes
*/
pg_attribute = heap_openr(AttributeRelationName, RowExclusiveLock);
- /*
- * initialize *null, *replace and *value
- */
- MemSet(nullv, ' ', Natts_pg_attribute);
- MemSet(replace, ' ', Natts_pg_attribute);
-
- /* ----------
- * create the first attribute tuple.
- * XXX For now, only change the ATTNUM attribute value
- * ----------
- */
- replace[Anum_pg_attribute_attnum - 1] = 'r';
- replace[Anum_pg_attribute_attcacheoff - 1] = 'r';
-
- value[Anum_pg_attribute_attnum - 1] = Int16GetDatum(1);
- value[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1);
-
- init_tuple = heap_addheader(Natts_pg_attribute,
- ATTRIBUTE_TUPLE_SIZE,
- (char *) (indexRelation->rd_att->attrs[0]));
-
- hasind = false;
- if (!IsIgnoringSystemIndexes() && pg_attribute->rd_rel->relhasindex)
- {
- hasind = true;
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- }
-
- /*
- * insert the first attribute tuple.
- */
- cur_tuple = heap_modifytuple(init_tuple,
- pg_attribute,
- value,
- nullv,
- replace);
- heap_freetuple(init_tuple);
-
- heap_insert(pg_attribute, cur_tuple);
- if (hasind)
- CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, cur_tuple);
+ indstate = CatalogOpenIndexes(pg_attribute);
/*
- * now we use the information in the index cur_tuple descriptor to
- * form the remaining attribute tuples.
+ * insert data from new index's tupdesc into pg_attribute
*/
indexTupDesc = RelationGetDescr(indexRelation);
- for (i = 1; i < numatts; i += 1)
+ for (i = 0; i < numatts; i++)
{
-
/*
- * process the remaining attributes...
+ * There used to be very grotty code here to set these fields, but
+ * I think it's unnecessary. They should be set already.
*/
- memmove(GETSTRUCT(cur_tuple),
- (char *) indexTupDesc->attrs[i],
- ATTRIBUTE_TUPLE_SIZE);
+ Assert(indexTupDesc->attrs[i]->attnum == i + 1);
+ Assert(indexTupDesc->attrs[i]->attcacheoff == -1);
- value[Anum_pg_attribute_attnum - 1] = Int16GetDatum(i + 1);
+ new_tuple = heap_addheader(Natts_pg_attribute,
+ false,
+ ATTRIBUTE_TUPLE_SIZE,
+ (void *) indexTupDesc->attrs[i]);
- new_tuple = heap_modifytuple(cur_tuple,
- pg_attribute,
- value,
- nullv,
- replace);
- heap_freetuple(cur_tuple);
+ simple_heap_insert(pg_attribute, new_tuple);
- heap_insert(pg_attribute, new_tuple);
- if (hasind)
- CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, new_tuple);
+ CatalogIndexInsert(indstate, new_tuple);
- /*
- * ModifyHeapTuple returns a new copy of a cur_tuple so we free
- * the original and use the copy..
- */
- cur_tuple = new_tuple;
+ heap_freetuple(new_tuple);
}
- if (cur_tuple)
- heap_freetuple(cur_tuple);
+ CatalogCloseIndexes(indstate);
+
heap_close(pg_attribute, RowExclusiveLock);
- if (hasind)
- CatalogCloseIndices(Num_pg_attr_indices, idescs);
}
/* ----------------------------------------------------------------
Oid heapoid,
IndexInfo *indexInfo,
Oid *classOids,
- bool islossy,
bool primary)
{
Form_pg_index indexForm;
Relation pg_index;
HeapTuple tuple;
int i;
- Relation idescs[Num_pg_index_indices];
/*
* allocate a Form_pg_index big enough to hold the index-predicate (if
* any) in string form
*/
- if (indexInfo->ii_Predicate != NULL)
+ if (indexInfo->ii_Predicate != NIL)
{
predString = nodeToString(indexInfo->ii_Predicate);
predText = DatumGetTextP(DirectFunctionCall1(textin,
indexForm->indexrelid = indexoid;
indexForm->indrelid = heapoid;
indexForm->indproc = indexInfo->ii_FuncOid;
- indexForm->indisclustered = false; /* not used */
- indexForm->indislossy = islossy;
- indexForm->indhaskeytype = true; /* used by GIST */
+ indexForm->indisclustered = false; /* not clustered, yet */
indexForm->indisunique = indexInfo->ii_Unique;
indexForm->indisprimary = primary;
memcpy((char *) &indexForm->indpred, (char *) predText, predLen);
* form a tuple to insert into pg_index
*/
tuple = heap_addheader(Natts_pg_index,
+ false,
itupLen,
- (char *) indexForm);
+ (void *) indexForm);
/*
- * insert the tuple into the pg_index
+ * insert the tuple into the pg_index catalog
*/
- heap_insert(pg_index, tuple);
+ simple_heap_insert(pg_index, tuple);
- /*
- * add index tuples for it
- */
- if (!IsIgnoringSystemIndexes())
- {
- CatalogOpenIndices(Num_pg_index_indices, Name_pg_index_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_index_indices, pg_index, tuple);
- CatalogCloseIndices(Num_pg_index_indices, idescs);
- }
+ /* update the indexes on pg_index */
+ CatalogUpdateIndexes(pg_index, tuple);
/*
* close the relation and free the tuple
heap_freetuple(tuple);
}
-/* ----------------------------------------------------------------
- * UpdateIndexPredicate
- * ----------------------------------------------------------------
- */
-void
-UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate)
-{
- Node *newPred;
- char *predString;
- text *predText;
- Relation pg_index;
- HeapTuple tuple;
- HeapTuple newtup;
- int i;
- Datum values[Natts_pg_index];
- char nulls[Natts_pg_index];
- char replace[Natts_pg_index];
-
- /*
- * Construct newPred as a CNF expression equivalent to the OR of the
- * original partial-index predicate ("oldPred") and the extension
- * predicate ("predicate").
- *
- * This should really try to process the result to change things like
- * "a>2 OR a>1" to simply "a>1", but for now all it does is make sure
- * that if the extension predicate is NULL (i.e., it is being extended
- * to be a complete index), then newPred will be NULL - in effect,
- * changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93
- */
- newPred = NULL;
- if (predicate != NULL)
- {
- newPred = (Node *) make_orclause(lcons(make_andclause((List *) predicate),
- lcons(make_andclause((List *) oldPred),
- NIL)));
- newPred = (Node *) cnfify((Expr *) newPred, true);
- }
-
- /* translate the index-predicate to string form */
- if (newPred != NULL)
- {
- predString = nodeToString(newPred);
- predText = DatumGetTextP(DirectFunctionCall1(textin,
- CStringGetDatum(predString)));
- pfree(predString);
- }
- else
- predText = DatumGetTextP(DirectFunctionCall1(textin,
- CStringGetDatum("")));
-
- /* open the index system catalog relation */
- pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
-
- tuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(indexoid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "UpdateIndexPredicate: cache lookup failed for index %u",
- indexoid);
-
- for (i = 0; i < Natts_pg_index; i++)
- {
- nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' ';
- replace[i] = ' ';
- values[i] = (Datum) NULL;
- }
-
- replace[Anum_pg_index_indpred - 1] = 'r';
- values[Anum_pg_index_indpred - 1] = (Datum) predText;
-
- newtup = heap_modifytuple(tuple, pg_index, values, nulls, replace);
-
- simple_heap_update(pg_index, &newtup->t_self, newtup);
-
- heap_freetuple(newtup);
- ReleaseSysCache(tuple);
-
- heap_close(pg_index, RowExclusiveLock);
- pfree(predText);
-}
-
-/* ----------------------------------------------------------------
- * InitIndexStrategy
- * ----------------------------------------------------------------
- */
-void
-InitIndexStrategy(int numatts,
- Relation indexRelation,
- Oid accessMethodObjectId)
-{
- IndexStrategy strategy;
- RegProcedure *support;
- uint16 amstrategies;
- uint16 amsupport;
- Oid attrelid;
- Size strsize;
-
- /*
- * get information from the index relation descriptor
- */
- attrelid = indexRelation->rd_att->attrs[0]->attrelid;
- amstrategies = indexRelation->rd_am->amstrategies;
- amsupport = indexRelation->rd_am->amsupport;
-
- /*
- * get the size of the strategy
- */
- strsize = AttributeNumberGetIndexStrategySize(numatts, amstrategies);
-
- /*
- * allocate the new index strategy structure
- *
- * the index strategy has to be allocated in the same context as the
- * relation descriptor cache or else it will be lost at the end of the
- * transaction.
- */
- if (!CacheMemoryContext)
- CreateCacheMemoryContext();
-
- strategy = (IndexStrategy) MemoryContextAlloc(CacheMemoryContext,
- strsize);
-
- if (amsupport > 0)
- {
- strsize = numatts * (amsupport * sizeof(RegProcedure));
- support = (RegProcedure *) MemoryContextAlloc(CacheMemoryContext,
- strsize);
- }
- else
- support = (RegProcedure *) NULL;
-
- /*
- * 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();
-
- IndexSupportInitialize(strategy, support,
- &indexRelation->rd_uniqueindex,
- attrelid, accessMethodObjectId,
- amstrategies, amsupport, numatts);
-
- /*
- * store the strategy information in the index reldesc
- */
- RelationSetIndexSupport(indexRelation, strategy, support);
-}
-
/* ----------------------------------------------------------------
* index_create
+ *
+ * Returns OID of the created index.
* ----------------------------------------------------------------
*/
-void
-index_create(char *heapRelationName,
- char *indexRelationName,
+Oid
+index_create(Oid heapRelationId,
+ const char *indexRelationName,
IndexInfo *indexInfo,
Oid accessMethodObjectId,
Oid *classObjectId,
- bool islossy,
bool primary,
+ bool isconstraint,
bool allow_system_table_mods)
{
Relation heapRelation;
Relation indexRelation;
TupleDesc indexTupDesc;
- Oid heapoid;
+ bool shared_relation;
+ Oid namespaceId;
Oid indexoid;
- bool istemp = is_temp_rel_name(heapRelationName);
- char *temp_relname = NULL;
+ int i;
SetReindexProcessing(false);
/*
+ * Only SELECT ... FOR UPDATE are allowed while doing this
+ */
+ heapRelation = heap_open(heapRelationId, ShareLock);
+
+ /*
+ * 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 ||
indexInfo->ii_NumKeyAttrs < 1)
- elog(ERROR, "must index at least one attribute");
+ elog(ERROR, "must index at least one column");
- if (heapRelationName && !allow_system_table_mods &&
- IsSystemRelationName(heapRelationName) && IsNormalProcessingMode())
+ if (!allow_system_table_mods &&
+ IsSystemRelation(heapRelation) &&
+ IsNormalProcessingMode())
elog(ERROR, "User-defined indexes on system catalogs are not supported");
/*
- * get heap relation oid and open the heap relation
+ * 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
+ * standalone backend. However, we can at least prevent this mistake
+ * under normal multi-user operation.
*/
- heapoid = GetHeapRelationOid(heapRelationName, indexRelationName, istemp);
+ if (shared_relation && IsUnderPostmaster)
+ elog(ERROR, "Shared indexes cannot be created after initdb");
- /*
- * Only SELECT ... FOR UPDATE are allowed while doing this
- */
- heapRelation = heap_open(heapoid, ShareLock);
+ if (get_relname_relid(indexRelationName, namespaceId))
+ elog(ERROR, "relation named \"%s\" already exists",
+ indexRelationName);
/*
- * construct new tuple descriptor
+ * construct tuple descriptor for index tuples
*/
if (OidIsValid(indexInfo->ii_FuncOid))
- indexTupDesc = BuildFuncTupleDesc(indexInfo->ii_FuncOid);
+ indexTupDesc = BuildFuncTupleDesc(indexInfo->ii_FuncOid,
+ classObjectId);
else
indexTupDesc = ConstructTupleDescriptor(heapRelation,
indexInfo->ii_NumKeyAttrs,
- indexInfo->ii_KeyAttrNumbers);
+ indexInfo->ii_KeyAttrNumbers,
+ classObjectId);
- if (istemp)
- {
- /* save user relation name because heap_create changes it */
- temp_relname = pstrdup(indexRelationName); /* save original value */
- indexRelationName = palloc(NAMEDATALEN);
- strcpy(indexRelationName, temp_relname); /* heap_create will
- * change this */
- }
+ indexTupDesc->tdhasoid = false;
/*
- * create the index relation
+ * 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, indexTupDesc,
- istemp, false, allow_system_table_mods);
+ 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
LockRelation(indexRelation, AccessExclusiveLock);
/*
- * construct the index relation descriptor
+ * Fill in fields of the index's pg_class entry that are not set
+ * correctly by heap_create.
*
- * XXX should have a proper way to create cataloged relations
+ * XXX should have a cleaner way to create cataloged indexes
*/
- ConstructIndexReldesc(indexRelation, accessMethodObjectId);
-
- /* ----------------
- * add index to catalogs
- * (append RELATION tuple)
- * ----------------
- */
- indexoid = UpdateRelationRelation(indexRelation, temp_relname);
+ indexRelation->rd_rel->relowner = GetUserId();
+ indexRelation->rd_rel->relam = accessMethodObjectId;
+ indexRelation->rd_rel->relkind = RELKIND_INDEX;
+ indexRelation->rd_rel->relhasoids = false;
/*
- * We create the disk file for this relation here
+ * store index's pg_class entry
*/
- heap_storage_create(indexRelation);
+ UpdateRelationRelation(indexRelation);
/*
* now update the object id's of all the attribute tuple forms in the
* (Or, could define a rule to maintain the predicate) --Nels, Feb '92
* ----------------
*/
- UpdateIndexRelation(indexoid, heapoid, indexInfo,
- classObjectId, islossy, primary);
+ 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
+ * 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.
+ *
+ * We don't need a dependency on the namespace, because there'll be an
+ * indirect dependency via our parent table.
+ *
+ * During bootstrap we can't register any dependencies, and we don't try
+ * to make a constraint either.
+ */
+ if (!IsBootstrapProcessingMode())
+ {
+ 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, "index_create: constraint must be PRIMARY or UNIQUE");
+ constraintType = 0; /* keep compiler quiet */
+ }
+
+ conOid = CreateConstraintEntry(indexRelationName,
+ namespaceId,
+ constraintType,
+ false, /* isDeferrable */
+ false, /* isDeferred */
+ heapRelationId,
+ indexInfo->ii_KeyAttrNumbers,
+ indexInfo->ii_NumKeyAttrs,
+ InvalidOid, /* no domain */
+ InvalidOid, /* no foreign key */
+ NULL,
+ 0,
+ ' ',
+ ' ',
+ ' ',
+ 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
+ {
+ for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++)
+ {
+ 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 the dependency on the function (if appropriate) */
+ if (OidIsValid(indexInfo->ii_FuncOid))
+ {
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = indexInfo->ii_FuncOid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ }
/*
- * initialize the index strategy
+ * 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.
*/
- InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
- indexRelation,
- accessMethodObjectId);
+ CommandCounterIncrement();
+
+ RelationInitIndexAccessInfo(indexRelation);
/*
* If this is bootstrap (initdb) time, then we don't actually fill in
- * the index yet. We'll be creating more indices and classes later,
+ * 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.
*/
if (IsBootstrapProcessingMode())
{
- index_register(heapRelationName, indexRelationName, indexInfo);
+ index_register(heapRelationId, indexoid, indexInfo);
/* XXX shouldn't we close the heap and index rels here? */
}
else
- index_build(heapRelation, indexRelation, indexInfo, NULL);
+ 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)
Relation userHeapRelation;
Relation userIndexRelation;
Relation indexRelation;
- Relation relationRelation;
- Relation attributeRelation;
HeapTuple tuple;
- int16 attnum;
int i;
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 the index
- * access routines are a little slipshod about obtaining adequate
- * locking (see ExecOpenIndices()). 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.
+ * 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);
LockRelation(userIndexRelation, AccessExclusiveLock);
/*
- * Note: unlike heap_drop_with_catalog, we do not need to prevent
- * deletion of system indexes here; that's checked for upstream. If we
- * did check it here, deletion of TOAST tables would fail...
- */
-
- /*
- * fix DESCRIPTION relation
- */
- DeleteComments(indexId);
-
- /*
* fix RELATION relation
*/
- relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
-
- /* Remove the pg_class tuple for the index itself */
- tuple = SearchSysCacheCopy(RELOID,
- ObjectIdGetDatum(indexId),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "index_drop: cache lookup failed for index %u",
- indexId);
-
- simple_heap_delete(relationRelation, &tuple->t_self);
- heap_freetuple(tuple);
-
- /*
- * Update the pg_class tuple for the owning relation. We are
- * presently too lazy to attempt to compute the new correct value of
- * relhasindex (the next VACUUM will fix it if necessary). But we
- * must send out a shared-cache-inval notice on the owning relation to
- * ensure other backends update their relcache lists of indexes. So,
- * unconditionally do setRelhasindex(true).
- */
- setRelhasindex(heapId, true);
-
- heap_close(relationRelation, RowExclusiveLock);
+ DeleteRelationTuple(indexId);
/*
* fix ATTRIBUTE relation
*/
- attributeRelation = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- attnum = 1; /* indexes start at 1 */
-
- while (HeapTupleIsValid(tuple = SearchSysCacheCopy(ATTNUM,
- ObjectIdGetDatum(indexId),
- Int16GetDatum(attnum),
- 0, 0)))
- {
- simple_heap_delete(attributeRelation, &tuple->t_self);
- heap_freetuple(tuple);
- attnum++;
- }
- heap_close(attributeRelation, RowExclusiveLock);
+ DeleteAttributeTuples(indexId);
/*
* fix INDEX relation
*/
indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);
- tuple = SearchSysCacheCopy(INDEXRELID,
- ObjectIdGetDatum(indexId),
- 0, 0, 0);
+ tuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexId),
+ 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "index_drop: cache lookup failed for index %u",
indexId);
simple_heap_delete(indexRelation, &tuple->t_self);
- heap_freetuple(tuple);
+
+ ReleaseSysCache(tuple);
heap_close(indexRelation, RowExclusiveLock);
/*
smgrunlink(DEFAULT_SMGR, userIndexRelation);
/*
+ * We are presently too lazy to attempt to compute the new correct
+ * 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
+ * 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);
-
- /* if it's a temp index, clear the temp mapping table entry */
- remove_temp_rel_by_relid(indexId);
}
/* ----------------------------------------------------------------
* just once per command, and then use it for (potentially) many tuples.
* ----------------
*/
-IndexInfo *
-BuildIndexInfo(HeapTuple indexTuple)
+IndexInfo *
+BuildIndexInfo(Form_pg_index indexStruct)
{
- Form_pg_index indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
IndexInfo *ii = makeNode(IndexInfo);
int i;
int numKeys;
* Handle functional index.
*
* If we have a functional index then the number of attributes defined in
- * the index must be 1 (the function's single return value).
- * Otherwise it's same as number of keys.
+ * the index must be 1 (the function's single return value). Otherwise
+ * it's same as number of keys.
*/
ii->ii_FuncOid = indexStruct->indproc;
/*
* If partial index, convert predicate into expression nodetree
*/
- if (VARSIZE(&indexStruct->indpred) != 0)
+ if (VARSIZE(&indexStruct->indpred) > VARHDRSZ)
{
char *predString;
pfree(predString);
}
else
- ii->ii_Predicate = NULL;
+ ii->ii_Predicate = NIL;
/* Other info */
ii->ii_Unique = indexStruct->indisunique;
if (OidIsValid(indexInfo->ii_FuncOid))
{
-
/*
* Functional index --- compute the single index attribute
*/
}
else
{
-
/*
* Plain index --- for each attribute we need from the heap tuple,
* get the attribute and stick it into the datum and nullv arrays.
ItemPointerData tidsave;
ItemPointerCopy(&(rtup->t_self), &tidsave);
- test = heap_mark4update(relationRelation, rtup, buffer);
+ test = heap_mark4update(relationRelation, rtup, buffer,
+ GetCurrentCommandId());
switch (test)
{
case HeapTupleSelfUpdated:
}
break;
}
- RelationInvalidateHeapTuple(relationRelation, rtup);
+ CacheInvalidateHeapTuple(relationRelation, rtup);
if (confirmCommitted)
{
HeapTupleHeader th = rtup->t_data;
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid));
- scan = heap_beginscan(indexRelation, false, SnapshotNow,
- 1, &entry);
- if (!heap_getnext(scan, 0))
- isactive = true;
+ scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
+ if (heap_getnext(scan, ForwardScanDirection) == NULL)
+ isactive = true; /* no indexes, so report "active" */
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
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.
- * Therefore, we execute the update even if relhasindex has the right value
- * already. Possible future improvement: skip the disk update and just send
- * an SI message in that case.
+ * 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.
* ----------------
*/
void
-setRelhasindex(Oid relid, bool hasindex)
+setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
{
Relation pg_class;
HeapTuple tuple;
+ Form_pg_class classtuple;
+ bool dirty = false;
HeapScanDesc pg_class_scan = NULL;
/*
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
- if (!IsIgnoringSystemIndexes() && (!IsReindexProcessing() || pg_class->rd_rel->relhasindex))
+ if (!IsIgnoringSystemIndexes() &&
+ (!IsReindexProcessing() || pg_class->rd_rel->relhasindex))
{
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(relid),
F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan(pg_class, 0, SnapshotNow, 1, key);
- tuple = heap_getnext(pg_class_scan, 0);
+ pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
+ tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
}
if (!HeapTupleIsValid(tuple))
}
/*
- * Update hasindex in pg_class.
+ * Update fields in the pg_class tuple.
*/
if (pg_class_scan)
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
- ((Form_pg_class) GETSTRUCT(tuple))->relhasindex = hasindex;
+
+ 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;
+ }
+ }
+
if (pg_class_scan)
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
/* Send out shared cache inval if necessary */
if (!IsBootstrapProcessingMode())
- RelationInvalidateHeapTuple(pg_class, tuple);
+ CacheInvalidateHeapTuple(pg_class, tuple);
BufferSync();
}
- else
+ else if (dirty)
{
simple_heap_update(pg_class, &tuple->t_self, tuple);
- /* Keep the catalog indices up to date */
- if (!IsIgnoringSystemIndexes())
- {
- Relation idescs[Num_pg_class_indices];
-
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
- idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
- }
+ /* Keep the catalog indexes up to date */
+ CatalogUpdateIndexes(pg_class, tuple);
+ }
+ else
+ {
+ /* no need to change tuple, but force relcache rebuild anyway */
+ CacheInvalidateRelcache(relid);
}
if (!pg_class_scan)
void
setNewRelfilenode(Relation relation)
{
- Relation pg_class,
- idescs[Num_pg_class_indices];
+ Relation pg_class;
Oid newrelfilenode;
bool in_place_update = false;
HeapTupleData lockTupleData;
Buffer buffer;
RelationData workrel;
- Assert(!IsSystemRelationName(NameStr(relation->rd_rel->relname)) || relation->rd_rel->relkind == RELKIND_INDEX);
+ Assert(!IsSystemRelation(relation) || IsToastRelation(relation) ||
+ relation->rd_rel->relkind == RELKIND_INDEX);
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
/* Fetch and lock the classTuple associated with this relation */
}
/* schedule unlinking old relfilenode */
smgrunlink(DEFAULT_SMGR, relation);
- /* cleanup pg_internal.init if necessary */
- if (relation->rd_isnailed)
- unlink(RELCACHE_INIT_FILENAME);
/* create another storage file. Is it a little ugly ? */
memcpy((char *) &workrel, relation, sizeof(RelationData));
workrel.rd_node.relNode = newrelfilenode;
classTuple = &lockTupleData;
/* Send out shared cache inval if necessary */
if (!IsBootstrapProcessingMode())
- RelationInvalidateHeapTuple(pg_class, classTuple);
+ CacheInvalidateHeapTuple(pg_class, classTuple);
/* Update the buffer in-place */
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
((Form_pg_class) GETSTRUCT(classTuple))->relfilenode = newrelfilenode;
WriteBuffer(buffer);
BufferSync();
}
- /* Keep the catalog indices up to date */
- if (!in_place_update && pg_class->rd_rel->relhasindex)
- {
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
- idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, classTuple);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
- }
+ /* Keep the catalog indexes up to date */
+ if (!in_place_update)
+ CatalogUpdateIndexes(pg_class, classTuple);
+
heap_close(pg_class, NoLock);
if (!in_place_update)
heap_freetuple(classTuple);
Relation pg_class;
HeapTuple tuple;
HeapTuple newtup;
- long relpages;
+ BlockNumber relpages;
int i;
Form_pg_class rd_rel;
- Relation idescs[Num_pg_class_indices];
Datum values[Natts_pg_class];
char nulls[Natts_pg_class];
char replace[Natts_pg_class];
*/
/*
- * Can't use heap_open here since we don't know if it's an index...
+ * Grabbing lock here is probably redundant ...
*/
- whichRel = RelationIdGetRelation(relid);
-
- if (!RelationIsValid(whichRel))
- elog(ERROR, "UpdateStats: cannot open relation id %u", relid);
-
- /* Grab lock to be held till end of xact (probably redundant...) */
- LockRelation(whichRel, ShareLock);
+ whichRel = relation_open(relid, ShareLock);
/*
* Find the RELATION relation tuple for the given relation.
F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan(pg_class, 0, SnapshotNow, 1, key);
- tuple = heap_getnext(pg_class_scan, 0);
+ pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
+ tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
}
if (!HeapTupleIsValid(tuple))
* 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.
+ * 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);
reltuples = 1000;
}
else
- reltuples = relpages * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
+ reltuples = ((double) relpages) * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
}
/*
* place with the new values so that the cache contains the latest
* copy.
*/
- whichRel->rd_rel->relpages = relpages;
+ whichRel->rd_rel->relpages = (int32) relpages;
whichRel->rd_rel->reltuples = reltuples;
/*
*/
if (in_place_upd)
{
-
/*
* At bootstrap time, we don't need to worry about concurrency or
* visibility of changes, so we cheat. Also cheat if REINDEX.
*/
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
- rd_rel->relpages = relpages;
+ rd_rel->relpages = (int32) relpages;
rd_rel->reltuples = reltuples;
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
if (!IsBootstrapProcessingMode())
- RelationInvalidateHeapTuple(pg_class, tuple);
+ CacheInvalidateHeapTuple(pg_class, tuple);
}
else
{
}
replace[Anum_pg_class_relpages - 1] = 'r';
- values[Anum_pg_class_relpages - 1] = Int32GetDatum(relpages);
+ values[Anum_pg_class_relpages - 1] = Int32GetDatum((int32) relpages);
replace[Anum_pg_class_reltuples - 1] = 'r';
values[Anum_pg_class_reltuples - 1] = Float4GetDatum((float4) reltuples);
newtup = heap_modifytuple(tuple, pg_class, values, nulls, replace);
simple_heap_update(pg_class, &tuple->t_self, newtup);
- if (!IsIgnoringSystemIndexes())
- {
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, newtup);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
- }
+ CatalogUpdateIndexes(pg_class, newtup);
heap_freetuple(newtup);
}
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
- /* Cheating a little bit since we didn't open it with heap_open... */
- heap_close(whichRel, NoLock);
+ relation_close(whichRel, NoLock);
}
-/* ----------------
- * DefaultBuild
+/*
+ * 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
*
- * NB: this routine is dead code, and likely always has been, because
- * there are no access methods that don't supply their own ambuild procedure.
+ * 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.
*
- * Anyone want to wager whether it would actually work if executed?
- * ----------------
+ * 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.
*/
-static void
-DefaultBuild(Relation heapRelation,
- Relation indexRelation,
- IndexInfo *indexInfo,
- Node *oldPred,
- IndexStrategy indexStrategy) /* not used */
+double
+IndexBuildHeapScan(Relation heapRelation,
+ Relation indexRelation,
+ IndexInfo *indexInfo,
+ IndexBuildCallback callback,
+ void *callback_state)
{
HeapScanDesc scan;
HeapTuple heapTuple;
TupleDesc heapDescriptor;
- Datum datum[INDEX_MAX_KEYS];
- char nullv[INDEX_MAX_KEYS];
- double reltuples,
- indtuples;
- Node *predicate = indexInfo->ii_Predicate;
-
-#ifndef OMIT_PARTIAL_INDEX
+ Datum attdata[INDEX_MAX_KEYS];
+ char nulls[INDEX_MAX_KEYS];
+ double reltuples;
+ List *predicate = indexInfo->ii_Predicate;
TupleTable tupleTable;
TupleTableSlot *slot;
-
-#endif
ExprContext *econtext;
- InsertIndexResult insertResult;
+ Snapshot snapshot;
+ TransactionId OldestXmin;
/*
- * more & better checking is needed
+ * sanity checks
*/
- Assert(OidIsValid(indexRelation->rd_rel->relam)); /* XXX */
+ Assert(OidIsValid(indexRelation->rd_rel->relam));
heapDescriptor = RelationGetDescr(heapRelation);
* We construct the ExprContext anyway since we need a per-tuple
* temporary memory context for function evaluation -- tgl July 00
*/
-#ifndef OMIT_PARTIAL_INDEX
- if (predicate != NULL || oldPred != NULL)
+ if (predicate != NIL)
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
slot = NULL;
}
econtext = MakeExprContext(slot, TransactionCommandContext);
-#else
- econtext = MakeExprContext(NULL, TransactionCommandContext);
-#endif /* OMIT_PARTIAL_INDEX */
/*
- * Ok, begin our scan of the base relation.
+ * 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 */
- 0, /* start at end */
- SnapshotNow, /* seeself */
+ snapshot, /* seeself */
0, /* number of keys */
(ScanKey) NULL); /* scan key */
- reltuples = indtuples = 0.0;
+ reltuples = 0;
/*
- * for each tuple in the base relation, we create an index tuple and
- * add it to the index relation. We keep a running count of the
- * number of tuples so that we can update pg_class with correct
- * statistics when we're done building the index.
+ * Scan all tuples in the base relation.
*/
- while (HeapTupleIsValid(heapTuple = heap_getnext(scan, 0)))
+ while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- MemoryContextReset(econtext->ecxt_per_tuple_memory);
-
- reltuples += 1.0;
+ bool tupleIsAlive;
-#ifndef OMIT_PARTIAL_INDEX
+ CHECK_FOR_INTERRUPTS();
- /*
- * If oldPred != NULL, this is an EXTEND INDEX command, so skip
- * this tuple if it was already in the existing partial index
- */
- if (oldPred != NULL)
+ if (snapshot == SnapshotAny)
{
- slot->val = heapTuple;
- if (ExecQual((List *) oldPred, econtext, false))
+ /* 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;
+
+ switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin))
{
- indtuples += 1.0;
- continue;
+ 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(
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ elog(ERROR, "IndexBuildHeapScan: 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(
+ HeapTupleHeaderGetXmax(heapTuple->t_data)))
+ elog(ERROR, "IndexBuildHeapScan: 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);
+
/*
- * Skip this tuple if it doesn't satisfy the partial-index
- * predicate
+ * 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 != NULL)
+ if (predicate != NIL)
{
- slot->val = heapTuple;
- if (!ExecQual((List *) predicate, econtext, false))
+ if (!tupleIsAlive)
+ continue;
+ ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
+ if (!ExecQual(predicate, econtext, false))
continue;
}
-#endif /* OMIT_PARTIAL_INDEX */
-
- indtuples += 1.0;
/*
- * FormIndexDatum fills in its datum and null parameters with
- * attribute information taken from the given heap tuple.
+ * For the current heap tuple, extract all the attributes we use
+ * in this index, and note which are null. This also performs
+ * evaluation of the function, if this is a functional index.
*/
FormIndexDatum(indexInfo,
heapTuple,
heapDescriptor,
econtext->ecxt_per_tuple_memory,
- datum,
- nullv);
+ attdata,
+ nulls);
- insertResult = index_insert(indexRelation, datum, nullv,
- &(heapTuple->t_self), heapRelation);
+ /*
+ * 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.
+ */
- if (insertResult)
- pfree(insertResult);
+ /* Call the AM's callback routine to process the tuple */
+ callback(indexRelation, heapTuple, attdata, nulls, tupleIsAlive,
+ callback_state);
}
heap_endscan(scan);
-#ifndef OMIT_PARTIAL_INDEX
- if (predicate != NULL || oldPred != NULL)
+ if (predicate != NIL)
ExecDropTupleTable(tupleTable, true);
-#endif /* OMIT_PARTIAL_INDEX */
FreeExprContext(econtext);
- /*
- * Since we just counted the tuples in the heap, we update its stats
- * in pg_class to guarantee that the planner takes advantage of the
- * index we just created. But, only update statistics during normal
- * index definitions, not for indices on system catalogs created
- * during bootstrap processing. We must close the relations before
- * updating statistics to guarantee that the relcache entries are
- * flushed when we increment the command counter in UpdateStats(). But
- * we do not release any locks on the relations; those will be held
- * until end of transaction.
- */
- if (IsNormalProcessingMode())
- {
- Oid hrelid = RelationGetRelid(heapRelation);
- Oid irelid = RelationGetRelid(indexRelation);
-
- heap_close(heapRelation, NoLock);
- index_close(indexRelation);
- UpdateStats(hrelid, reltuples);
- UpdateStats(irelid, indtuples);
- if (oldPred != NULL)
- {
- if (indtuples == reltuples)
- predicate = NULL;
- UpdateIndexPredicate(irelid, oldPred, predicate);
- }
- }
+ return reltuples;
}
-/* ----------------
- * index_build
- * ----------------
- */
-void
-index_build(Relation heapRelation,
- Relation indexRelation,
- IndexInfo *indexInfo,
- Node *oldPred)
-{
- RegProcedure procedure;
-
- /*
- * sanity checks
- */
- Assert(RelationIsValid(indexRelation));
- Assert(PointerIsValid(indexRelation->rd_am));
-
- procedure = indexRelation->rd_am->ambuild;
-
- /*
- * use the access method build procedure if supplied, else default.
- */
- if (RegProcedureIsValid(procedure))
- OidFunctionCall5(procedure,
- PointerGetDatum(heapRelation),
- PointerGetDatum(indexRelation),
- PointerGetDatum(indexInfo),
- PointerGetDatum(oldPred),
- PointerGetDatum(RelationGetIndexStrategy(indexRelation)));
- else
- DefaultBuild(heapRelation,
- indexRelation,
- indexInfo,
- oldPred,
- RelationGetIndexStrategy(indexRelation));
-}
/*
* IndexGetRelation: given an index's relation OID, get the OID of the
reindex_index(Oid indexId, bool force, bool inplace)
{
Relation iRel,
- indexRelation,
heapRelation;
- ScanKeyData entry;
- HeapScanDesc scan;
- HeapTuple indexTuple,
- classTuple;
IndexInfo *indexInfo;
- Oid heapId,
- accessMethodId;
+ Oid heapId;
bool old;
/*
* REINDEX within a transaction block is dangerous, because if the
* transaction is later rolled back we have no way to undo truncation
* of the index's physical file. Disallow it.
+ *
+ * XXX if we're not doing an inplace rebuild, wouldn't this be okay?
*/
if (IsTransactionBlock())
- elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
+ elog(ERROR, "REINDEX cannot run inside a transaction block");
- old = SetReindexProcessing(true);
+ /*
+ * Open our index relation and get an exclusive lock on it.
+ *
+ * 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);
+ if (iRel == NULL)
+ elog(ERROR, "reindex_index: can't open index relation");
+ LockRelation(iRel, AccessExclusiveLock);
- /* Scan pg_index to find the index's pg_index entry */
- indexRelation = heap_openr(IndexRelationName, AccessShareLock);
- ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indexrelid, F_OIDEQ,
- ObjectIdGetDatum(indexId));
- scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry);
- indexTuple = heap_getnext(scan, 0);
- if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "reindex_index: index %u not found in pg_index", indexId);
+ old = SetReindexProcessing(true);
/* Get OID of index's parent table */
- heapId = ((Form_pg_index) GETSTRUCT(indexTuple))->indrelid;
+ heapId = iRel->rd_index->indrelid;
/* Fetch info needed for index_build */
- indexInfo = BuildIndexInfo(indexTuple);
-
- /* Complete the scan and close pg_index */
- heap_endscan(scan);
- heap_close(indexRelation, AccessShareLock);
-
- /* Fetch the classTuple associated with this index */
- classTuple = SearchSysCache(RELOID,
- ObjectIdGetDatum(indexId),
- 0, 0, 0);
- if (!HeapTupleIsValid(classTuple))
- elog(ERROR, "reindex_index: index %u not found in pg_class", indexId);
- accessMethodId = ((Form_pg_class) GETSTRUCT(classTuple))->relam;
- ReleaseSysCache(classTuple);
+ indexInfo = BuildIndexInfo(iRel->rd_index);
- /* Open our index relation */
- heapRelation = heap_open(heapId, ExclusiveLock);
+ /* Open the parent heap relation */
+ heapRelation = heap_open(heapId, AccessExclusiveLock);
if (heapRelation == NULL)
elog(ERROR, "reindex_index: can't open heap relation");
- iRel = index_open(indexId);
- if (iRel == NULL)
- elog(ERROR, "reindex_index: can't open index relation");
- if (!inplace)
- {
- inplace = IsSharedSystemRelationName(NameStr(iRel->rd_rel->relname));
- if (!inplace)
- setNewRelfilenode(iRel);
- }
- /* Obtain exclusive lock on it, just to be sure */
- LockRelation(iRel, AccessExclusiveLock);
+ /*
+ * Force inplace processing if it's a shared index. Necessary because
+ * we have no way to update relfilenode in other databases.
+ */
+ if (iRel->rd_rel->relisshared)
+ inplace = true;
if (inplace)
{
-
/*
* Release any buffers associated with this index. If they're
* dirty, they're just dropped without bothering to flush to disk.
/* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, iRel, 0);
iRel->rd_nblocks = 0;
+ iRel->rd_targblock = InvalidBlockNumber;
+ }
+ else
+ {
+ /*
+ * We'll build a new physical relation for the index.
+ */
+ setNewRelfilenode(iRel);
}
/* Initialize the index and rebuild */
- InitIndexStrategy(indexInfo->ii_NumIndexAttrs, iRel, accessMethodId);
- index_build(heapRelation, iRel, indexInfo, NULL);
+ index_build(heapRelation, iRel, indexInfo);
/*
* index_build will close both the heap and index relations (but not
if (IndexesAreActive(relid, true))
{
if (!activate)
- setRelhasindex(relid, false);
+ setRelhasindex(relid, false, false, InvalidOid);
else
return false;
}
HeapTuple indexTuple;
bool old,
reindexed;
-
bool deactivate_needed,
overwrite,
upd_pg_class_inplace;
-
Relation rel;
overwrite = upd_pg_class_inplace = deactivate_needed = false;
upd_pg_class_inplace = true;
/*
+ * Ensure to hold an exclusive lock throughout the transaction. The
+ * lock could be less intensive (in the non-overwrite path) but now
+ * it's AccessExclusiveLock for simplicity.
+ */
+ rel = heap_open(relid, AccessExclusiveLock);
+
+ /*
* ignore the indexes of the target system relation while processing
* reindex.
*/
- rel = RelationIdGetRelation(relid);
- if (!IsIgnoringSystemIndexes() && IsSystemRelationName(NameStr(rel->rd_rel->relname)))
+ if (!IsIgnoringSystemIndexes() &&
+ IsSystemRelation(rel) && !IsToastRelation(rel))
deactivate_needed = true;
#ifndef ENABLE_REINDEX_NAILED_RELATIONS
else
elog(ERROR, "the target relation %u is nailed", relid);
}
-#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
+#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
/*
* Shared system indexes must be overwritten because it's impossible
* to update pg_class tuples of all databases.
*/
- if (IsSharedSystemRelationName(NameStr(rel->rd_rel->relname)))
+ if (rel->rd_rel->relisshared)
{
if (IsIgnoringSystemIndexes())
{
else
elog(ERROR, "the target relation %u is shared", relid);
}
- RelationClose(rel);
+
+ /*
+ * Continue to hold the lock.
+ */
+ heap_close(rel, NoLock);
old = SetReindexProcessing(true);
if (deactivate_needed)
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid));
- scan = heap_beginscan(indexRelation, false, SnapshotNow,
- 1, &entry);
+ scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
reindexed = false;
- while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0)))
+ while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
heap_close(indexRelation, AccessShareLock);
if (reindexed)
{
-
/*
* Ok,we could use the reindexed indexes of the target system
* relation now.
{
if (!overwrite && relid == RelOid_pg_class)
{
-
/*
* For pg_class, relhasindex should be set to true here in
* place.
*/
- setRelhasindex(relid, true);
+ setRelhasindex(relid, true, false, InvalidOid);
CommandCounterIncrement();
/*
* keep consistency with WAL.
*/
}
- setRelhasindex(relid, true);
+ setRelhasindex(relid, true, false, InvalidOid);
}
}
SetReindexProcessing(old);