* index.c
* code to create and destroy POSTGRES index relations
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * 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.134 2001/01/18 04:01:42 inoue Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.192 2002/08/30 19:23:18 tgl 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.
*/
#define AVG_ATTR_SIZE 8
#define NTUPLES_PER_PAGE(natts) \
- ((BLCKSZ - MAXALIGN(sizeof (PageHeaderData))) / \
+ ((BLCKSZ - MAXALIGN(sizeof(PageHeaderData))) / \
((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 ConstructTupleDescriptor(Oid heapoid, Relation heapRelation,
- int numatts, AttrNumber *attNums);
-static void ConstructIndexReldesc(Relation indexRelation, Oid amoid);
-static Oid UpdateRelationRelation(Relation indexRelation, char *temp_relname);
+static TupleDesc BuildFuncTupleDesc(Oid funcOid,
+ Oid *classObjectId);
+static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
+ 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);
+ IndexInfo *indexInfo,
+ Oid *classOids,
+ bool primary);
static Oid IndexGetRelation(Oid indexId);
static bool activate_index(Oid indexId, bool activate, bool inplace);
return reindexing;
}
-/* ----------------------------------------------------------------
- * sysatts is a structure containing attribute tuple forms
- * for system attributes (numbered -1, -2, ...). This really
- * should be generated or eliminated or moved elsewhere. -cim 1/19/91
- *
- * typedef struct FormData_pg_attribute {
- * Oid attrelid;
- * NameData attname;
- * Oid atttypid;
- * uint32 attnvals;
- * int16 attlen;
- * AttrNumber attnum;
- * uint32 attnelems;
- * int32 attcacheoff;
- * int32 atttypmod;
- * bool attbyval;
- * bool attisset;
- * char attalign;
- * bool attnotnull;
- * bool atthasdef;
- * } FormData_pg_attribute;
- *
- * ----------------------------------------------------------------
- */
-static FormData_pg_attribute sysatts[] = {
- {0, {"ctid"}, TIDOID, 0, 6, -1, 0, -1, -1, '\0', 'p', '\0', 'i', '\0', '\0'},
- {0, {"oid"}, OIDOID, 0, 4, -2, 0, -1, -1, '\001', 'p', '\0', 'i', '\0', '\0'},
- {0, {"xmin"}, XIDOID, 0, 4, -3, 0, -1, -1, '\001', 'p', '\0', 'i', '\0', '\0'},
- {0, {"cmin"}, CIDOID, 0, 4, -4, 0, -1, -1, '\001', 'p', '\0', 'i', '\0', '\0'},
- {0, {"xmax"}, XIDOID, 0, 4, -5, 0, -1, -1, '\001', 'p', '\0', 'i', '\0', '\0'},
- {0, {"cmax"}, CIDOID, 0, 4, -6, 0, -1, -1, '\001', 'p', '\0', 'i', '\0', '\0'},
-};
-
-/* ----------------------------------------------------------------
- * 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.
+ * Allocate and zero a tuple descriptor for a one-column tuple.
*/
- funcTupDesc = CreateTemplateTupleDesc(1);
+ funcTupDesc = CreateTemplateTupleDesc(1, UNDEFOID);
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 = 'p';
- 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(Oid heapoid,
- Relation heapRelation,
+ConstructTupleDescriptor(Relation heapRelation,
int numatts,
- AttrNumber *attNums)
+ AttrNumber *attNums,
+ Oid *classObjectId)
{
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
heapTupDesc = RelationGetDescr(heapRelation);
natts = RelationGetForm(heapRelation)->relnatts;
- /* ----------------
- * allocate the new tuple descriptor
- * ----------------
+ /*
+ * allocate the new tuple descriptor
*/
- indexTupDesc = CreateTemplateTupleDesc(numatts);
+ indexTupDesc = CreateTemplateTupleDesc(numatts, WITHOUTOID);
/* ----------------
* for each attribute we are indexing, obtain its attribute
for (i = 0; i < numatts; i++)
{
AttrNumber atnum; /* attributeNumber[attributeOffset] */
- AttrNumber atind;
- char *from; /* used to simplify memcpy below */
- char *to; /* used to simplify memcpy below */
+ Form_pg_attribute from;
+ Form_pg_attribute to;
+ HeapTuple tuple;
+ Oid keyType;
- /* ----------------
- * get the attribute number and make sure it's valid
- * ----------------
+ /*
+ * get the attribute number and make sure it's valid; determine
+ * which attribute descriptor to copy
*/
atnum = attNums[i];
- if (atnum > natts)
- elog(ERROR, "Cannot create index: attribute %d does not exist",
- atnum);
-
- indexTupDesc->attrs[i] =
- (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
- /* ----------------
- * determine which tuple descriptor to copy
- * ----------------
- */
if (!AttrNumberIsForUserDefinedAttr(atnum))
{
- /* ----------------
- * here we are indexing on a system attribute (-1...-n)
- * so we convert atnum into a usable index 0...n-1 so we can
- * use it to dereference the array sysatts[] which stores
- * tuple descriptor information for system attributes.
- * ----------------
+ /*
+ * here we are indexing on a system attribute (-1...-n)
*/
- if (atnum <= FirstLowInvalidHeapAttributeNumber || atnum >= 0)
- elog(ERROR, "Cannot create index on system attribute: attribute number out of range (%d)", atnum);
- atind = (-atnum) - 1;
-
- from = (char *) (&sysatts[atind]);
+ from = SystemAttributeDefinition(atnum,
+ heapRelation->rd_rel->relhasoids);
}
else
{
- /* ----------------
- * here we are indexing on a normal attribute (1...n)
- * ----------------
+ /*
+ * here we are indexing on a normal attribute (1...n)
*/
- atind = AttrNumberGetAttrOffset(atnum);
+ if (atnum > natts)
+ elog(ERROR, "cannot create index: column %d does not exist",
+ atnum);
- from = (char *) (heapTupDesc->attrs[atind]);
+ from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)];
}
- /* ----------------
- * now that we've determined the "from", let's copy
- * the tuple desc data...
- * ----------------
+ /*
+ * now that we've determined the "from", let's copy the tuple desc
+ * data...
*/
- to = (char *) (indexTupDesc->attrs[i]);
+ indexTupDesc->attrs[i] = to =
+ (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
/*
- * Fix the stuff that should not be the same as the underlying attr
- */
- ((Form_pg_attribute) to)->attnum = i + 1;
-
- ((Form_pg_attribute) to)->attdispersion = 0.0;
- ((Form_pg_attribute) to)->attnotnull = false;
- ((Form_pg_attribute) to)->atthasdef = false;
- ((Form_pg_attribute) to)->attcacheoff = -1;
-
- /* ----------------
- * now we have to drop in the proper relation descriptor
- * into the copied tuple form's attrelid and we should be
- * all set.
- * ----------------
+ * Fix the stuff that should not be the same as the underlying
+ * attr
*/
- ((Form_pg_attribute) to)->attrelid = heapoid;
- }
-
- return indexTupDesc;
-}
+ to->attnum = i + 1;
-/* ----------------------------------------------------------------
- * 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;
+ to->attstattarget = 0;
+ to->attcacheoff = -1;
+ to->attnotnull = false;
+ to->atthasdef = false;
+ to->attisinherited = false;
- /* ----------------
- * form a scan key for the pg_am relation
- * ----------------
- */
- ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
- F_OIDEQ,
- ObjectIdGetDatum(accessMethodObjectId));
+ /*
+ * 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;
- /* ----------------
- * 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);
-
- /* ----------------
- * 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.
- * ----------------
- */
- tuple->t_data->t_oid = RelationGetRelid(indexRelation);
- heap_insert(pg_class, tuple);
-
- if (temp_relname)
- create_temp_relation(temp_relname, tuple);
+ (void *) indexRelation->rd_rel);
/*
- * 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.
+ * 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.
*/
+ AssertTupleDescHasOid(pg_class->rd_att);
+ HeapTupleSetOid(tuple, RelationGetRelid(indexRelation));
+ simple_heap_insert(pg_class, tuple);
- 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);
- }
+ /* update the system catalog indexes */
+ CatalogUpdateIndexes(pg_class, tuple);
- 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);
+ indstate = CatalogOpenIndexes(pg_attribute);
- /* ----------------
- * create the first attribute tuple.
- * XXX For now, only change the ATTNUM attribute value
- * ----------------
+ /*
+ * insert data from new index's tupdesc into pg_attribute
*/
- 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]));
+ indexTupDesc = RelationGetDescr(indexRelation);
- hasind = false;
- if (!IsIgnoringSystemIndexes() && pg_attribute->rd_rel->relhasindex)
+ for (i = 0; i < numatts; i++)
{
- hasind = true;
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- }
+ /*
+ * 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);
+ Assert(indexTupDesc->attrs[i]->attcacheoff == -1);
- /* ----------------
- * insert the first attribute tuple.
- * ----------------
- */
- cur_tuple = heap_modifytuple(init_tuple,
- pg_attribute,
- value,
- nullv,
- replace);
- heap_freetuple(init_tuple);
+ new_tuple = heap_addheader(Natts_pg_attribute,
+ false,
+ ATTRIBUTE_TUPLE_SIZE,
+ (void *) indexTupDesc->attrs[i]);
- heap_insert(pg_attribute, cur_tuple);
- if (hasind)
- CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, cur_tuple);
+ simple_heap_insert(pg_attribute, new_tuple);
- /* ----------------
- * now we use the information in the index cur_tuple
- * descriptor to form the remaining attribute tuples.
- * ----------------
- */
- indexTupDesc = RelationGetDescr(indexRelation);
+ CatalogIndexInsert(indstate, new_tuple);
- for (i = 1; i < numatts; i += 1)
- {
- /* ----------------
- * process the remaining attributes...
- * ----------------
- */
- memmove(GETSTRUCT(cur_tuple),
- (char *) indexTupDesc->attrs[i],
- ATTRIBUTE_TUPLE_SIZE);
-
- value[Anum_pg_attribute_attnum - 1] = Int16GetDatum(i + 1);
-
- new_tuple = heap_modifytuple(cur_tuple,
- pg_attribute,
- value,
- nullv,
- replace);
- heap_freetuple(cur_tuple);
-
- heap_insert(pg_attribute, new_tuple);
- if (hasind)
- CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, 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
- * ----------------
+ /*
+ * 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,
- CStringGetDatum(predString)));
+ CStringGetDatum(predString)));
pfree(predString);
}
else
predText = DatumGetTextP(DirectFunctionCall1(textin,
- CStringGetDatum("")));
+ CStringGetDatum("")));
predLen = VARSIZE(predText);
itupLen = predLen + sizeof(FormData_pg_index);
indexForm = (Form_pg_index) palloc(itupLen);
MemSet(indexForm, 0, sizeof(FormData_pg_index));
- /* ----------------
- * store information into the index tuple form
- * ----------------
+ /*
+ * store information into the index tuple form
*/
indexForm->indexrelid = indexoid;
indexForm->indrelid = heapoid;
indexForm->indproc = indexInfo->ii_FuncOid;
- indexForm->indisclustered = false;
- indexForm->indislossy = islossy;
- indexForm->indhaskeytype = true; /* not actually used anymore */
+ indexForm->indisclustered = false; /* not used */
indexForm->indisunique = indexInfo->ii_Unique;
indexForm->indisprimary = primary;
memcpy((char *) &indexForm->indpred, (char *) predText, predLen);
- /* ----------------
- * copy index key and op class information
+ /*
+ * copy index key and op class information
*
- * We zeroed the extra slots (if any) above --- that's essential.
- * ----------------
+ * We zeroed the extra slots (if any) above --- that's essential.
*/
for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++)
indexForm->indkey[i] = indexInfo->ii_KeyAttrNumbers[i];
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
indexForm->indclass[i] = classOids[i];
- /* ----------------
- * open the system catalog index relation
- * ----------------
+ /*
+ * open the system catalog index relation
*/
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
- /* ----------------
- * form a tuple to insert into pg_index
- * ----------------
+ /*
+ * 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
- * ----------------
+ /*
+ * close the relation and free the tuple
*/
heap_close(pg_index, RowExclusiveLock);
pfree(predText);
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);
-
- heap_update(pg_index, &newtup->t_self, newtup, NULL);
-
- 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);
- /* ----------------
- * check parameters
- * ----------------
+ /*
+ * 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");
- /* ----------------
- * get heap relation oid and open the heap relation
- * ----------------
- */
- heapoid = GetHeapRelationOid(heapRelationName, indexRelationName, istemp);
+ if (!allow_system_table_mods &&
+ IsSystemRelation(heapRelation) &&
+ IsNormalProcessingMode())
+ elog(ERROR, "User-defined indexes on system catalogs are not supported");
/*
- * Only SELECT ... FOR UPDATE are allowed while doing this
+ * 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.
*/
- heapRelation = heap_open(heapoid, ShareLock);
+ if (shared_relation && IsUnderPostmaster)
+ elog(ERROR, "Shared indexes cannot be created after initdb");
- /* ----------------
- * construct new tuple descriptor
- * ----------------
+ if (get_relname_relid(indexRelationName, namespaceId))
+ elog(ERROR, "relation named \"%s\" already exists",
+ indexRelationName);
+
+ /*
+ * 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(heapoid,
- heapRelation,
+ 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 */
- }
-
- /* ----------------
- * create the index relation
- * ----------------
+ indexTupDesc->tdhasoid = WITHOUTOID;
+ /*
+ * 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; /* WITHOUTOID! */
/*
- * 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 index relation's tuple descriptor
- * ----------------
+ /*
+ * 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
- * ----------------
+ /*
+ * append ATTRIBUTE tuples for the index
*/
AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
* (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);
- /* ----------------
- * initialize the index strategy
- * ----------------
+ /*
+ * 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.
*/
- InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
- indexRelation,
- accessMethodObjectId);
+ 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);
+ }
+ }
+
+ /*
+ * 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 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.
- * ----------------
+ /*
+ * 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);
-
- /* ----------------
- * 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);
-
- heap_delete(relationRelation, &tuple->t_self, NULL);
- heap_freetuple(tuple);
+ userIndexRelation = index_open(indexId);
+ LockRelation(userIndexRelation, AccessExclusiveLock);
/*
- * 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).
+ * fix RELATION relation
*/
- 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)))
- {
- heap_delete(attributeRelation, &tuple->t_self, NULL);
- 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);
- heap_delete(indexRelation, &tuple->t_self, NULL);
- heap_freetuple(tuple);
+ simple_heap_delete(indexRelation, &tuple->t_self);
+
+ 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);
}
/* ----------------------------------------------------------------
*
* IndexInfo stores the information about the index that's needed by
* FormIndexDatum, which is used for both index_build() and later insertion
- * of individual index tuples. Normally we build an IndexInfo for an index
+ * 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(HeapTuple indexTuple)
+BuildIndexInfo(Form_pg_index indexStruct)
{
- Form_pg_index indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
IndexInfo *ii = makeNode(IndexInfo);
int i;
int numKeys;
- /* ----------------
- * count the number of keys, and copy them into the IndexInfo
- * ----------------
+ /*
+ * count the number of keys, and copy them into the IndexInfo
*/
numKeys = 0;
for (i = 0; i < INDEX_MAX_KEYS &&
}
ii->ii_NumKeyAttrs = numKeys;
- /* ----------------
- * Handle functional index.
+ /*
+ * 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.
- * ----------------
+ * 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.
*/
ii->ii_FuncOid = indexStruct->indproc;
{
ii->ii_NumIndexAttrs = 1;
/* Do a lookup on the function, too */
- fmgr_info(indexStruct->indproc, & ii->ii_FuncInfo);
+ fmgr_info(indexStruct->indproc, &ii->ii_FuncInfo);
}
else
ii->ii_NumIndexAttrs = numKeys;
- /* ----------------
- * If partial index, convert predicate into expression nodetree
- * ----------------
+ /*
+ * If partial index, convert predicate into expression nodetree
*/
- if (VARSIZE(&indexStruct->indpred) != 0)
+ if (VARSIZE(&indexStruct->indpred) > VARHDRSZ)
{
char *predString;
predString = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(&indexStruct->indpred)));
+ PointerGetDatum(&indexStruct->indpred)));
ii->ii_Predicate = stringToNode(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
- * ----------------
+ /*
+ * Functional index --- compute the single index attribute
*/
- FunctionCallInfoData fcinfo;
- bool anynull = false;
+ FunctionCallInfoData fcinfo;
+ bool anynull = false;
MemSet(&fcinfo, 0, sizeof(fcinfo));
fcinfo.flinfo = &indexInfo->ii_FuncInfo;
}
else
{
- /* ----------------
- * Plain index --- for each attribute we need from the heap tuple,
- * get the attribute and stick it into the datum and nullv arrays.
- * ----------------
+ /*
+ * Plain index --- for each attribute we need from the heap tuple,
+ * get the attribute and stick it into the datum and nullv arrays.
*/
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
Relation relationRelation;
/*
- * NOTE: get and hold RowExclusiveLock on pg_class, because caller will
- * probably modify the rel's pg_class tuple later on.
+ * NOTE: get and hold RowExclusiveLock on pg_class, because caller
+ * will probably modify the rel's pg_class tuple later on.
*/
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
classTuple = SearchSysCache(RELOID, PointerGetDatum(relid),
while (1)
{
- ItemPointerData tidsave;
+ 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;
if (!LockClassinfoForUpdate(relid, &tuple, &buffer, confirmCommitted))
elog(ERROR, "IndexesAreActive couldn't lock %u", relid);
if (((Form_pg_class) GETSTRUCT(&tuple))->relkind != RELKIND_RELATION &&
- ((Form_pg_class) GETSTRUCT(&tuple))->relkind != RELKIND_TOASTVALUE)
+ ((Form_pg_class) GETSTRUCT(&tuple))->relkind != RELKIND_TOASTVALUE)
elog(ERROR, "relation %u isn't an indexable relation", relid);
isactive = ((Form_pg_class) GETSTRUCT(&tuple))->relhasindex;
ReleaseBuffer(buffer);
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);
-#ifdef OLD_FILE_NAMING
- if (!IsIgnoringSystemIndexes())
-#else
- if (!IsIgnoringSystemIndexes() && (!IsReindexProcessing() || pg_class->rd_rel->relhasindex))
-#endif /* OLD_FILE_NAMING */
+ 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))
relid);
}
- /* ----------------
- * 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)
{
- heap_update(pg_class, &tuple->t_self, tuple, NULL);
-
- /* Keep the catalog indices up to date */
- if (!IsIgnoringSystemIndexes())
- {
- Relation idescs[Num_pg_class_indices];
+ simple_heap_update(pg_class, &tuple->t_self, tuple);
- 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)
heap_close(pg_class, RowExclusiveLock);
}
-#ifndef OLD_FILE_NAMING
void
setNewRelfilenode(Relation relation)
{
- Relation pg_class, idescs[Num_pg_class_indices];
- Oid newrelfilenode;
+ Relation pg_class;
+ Oid newrelfilenode;
bool in_place_update = false;
- HeapTupleData lockTupleData;
- HeapTuple classTuple = NULL;
+ HeapTupleData lockTupleData;
+ HeapTuple classTuple = NULL;
Buffer buffer;
- RelationData workrel;
-
- Assert(!IsSystemRelationName(NameStr(relation->rd_rel->relname)) || relation->rd_rel->relkind == RELKIND_INDEX);
+ RelationData workrel;
+
+ 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 */
+ /* Fetch and lock the classTuple associated with this relation */
if (!LockClassinfoForUpdate(relation->rd_id, &lockTupleData, &buffer, true))
elog(ERROR, "setNewRelfilenode impossible to lock class tuple");
if (IsIgnoringSystemIndexes())
classTuple = heap_copytuple(&lockTupleData);
ReleaseBuffer(buffer);
((Form_pg_class) GETSTRUCT(classTuple))->relfilenode = newrelfilenode;
- heap_update(pg_class, &classTuple->t_self, classTuple, NULL);
+ simple_heap_update(pg_class, &classTuple->t_self, classTuple);
}
- /* unlink old relfilenode */
- DropRelationBuffers(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;
heap_storage_create(&workrel);
+ smgrclose(DEFAULT_SMGR, &workrel);
/* update pg_class tuple with new relfilenode in place */
if (in_place_update)
{
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);
- heap_freetuple(classTuple);
- }
+ /* 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);
/* Make sure the relfilenode change */
CommandCounterIncrement();
}
-#endif /* OLD_FILE_NAMING */
+
/* ----------------
* 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, long reltuples)
+UpdateStats(Oid relid, double reltuples)
{
Relation whichRel;
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];
HeapScanDesc pg_class_scan = NULL;
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.
- * ----------------
+ * 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.
- * ----------------
*/
/*
- * 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);
+ whichRel = relation_open(relid, ShareLock);
- /* Grab lock to be held till end of xact (probably redundant...) */
- LockRelation(whichRel, ShareLock);
-
- /* ----------------
+ /*
* Find the RELATION relation tuple for the given relation.
- * ----------------
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
-#ifdef OLD_FILE_NAMING
- in_place_upd = (IsReindexProcessing() || IsBootstrapProcessingMode());
-#else
in_place_upd = (IsIgnoringSystemIndexes() || IsReindexProcessing());
-#endif /* OLD_FILE_NAMING */
if (!in_place_upd)
{
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))
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
+ * 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.
- * ----------------
+ * 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);
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;
- /* ----------------
- * Update statistics in pg_class.
- * ----------------
+ /*
+ * Update statistics in pg_class.
*/
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.
+ * 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
{
for (i = 0; i < Natts_pg_class; i++)
{
- nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' ';
+ nulls[i] = ' ';
replace[i] = ' ';
values[i] = (Datum) NULL;
}
replace[Anum_pg_class_relpages - 1] = 'r';
- values[Anum_pg_class_relpages - 1] = (Datum) relpages;
+ values[Anum_pg_class_relpages - 1] = Int32GetDatum((int32) relpages);
replace[Anum_pg_class_reltuples - 1] = 'r';
- values[Anum_pg_class_reltuples - 1] = (Datum) reltuples;
+ values[Anum_pg_class_reltuples - 1] = Float4GetDatum((float4) reltuples);
newtup = heap_modifytuple(tuple, pg_class, values, nulls, replace);
- heap_update(pg_class, &tuple->t_self, newtup, NULL);
- 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);
- }
+ simple_heap_update(pg_class, &tuple->t_self, newtup);
+ 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];
- long 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);
- ExecSetSlotDescriptor(slot, heapDescriptor);
+ ExecSetSlotDescriptor(slot, heapDescriptor, false);
}
else
{
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;
+ 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);
+ bool tupleIsAlive;
- reltuples++;
+ CHECK_FOR_INTERRUPTS();
-#ifndef OMIT_PARTIAL_INDEX
- /*
- * 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++;
- 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++;
- /* ----------------
- * 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.
- * ----------------
+ /*
+ * 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 */
+ /* Open the parent heap relation */
heapRelation = heap_open(heapId, ExclusiveLock);
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");
-#ifndef OLD_FILE_NAMING
- if (!inplace)
- setNewRelfilenode(iRel);
-#endif /* OLD_FILE_NAMING */
- /* 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.
- */
+ * Release any buffers associated with this index. If they're
+ * dirty, they're just dropped without bothering to flush to disk.
+ */
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;
+ }
+ 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
- * give up the locks we hold on them). So we're done.
+ * give up the locks we hold on them). So we're done.
*/
SetReindexProcessing(old);
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;
- bool deactivate_needed, overwrite, upd_pg_class_inplace;
-#ifdef OLD_FILE_NAMING
- overwrite = upd_pg_class_inplace = deactivate_needed = true;
-#else
- Relation rel;
- overwrite = upd_pg_class_inplace = deactivate_needed = false;
/*
- * avoid heap_update() pg_class tuples while processing
- * reindex for pg_class.
- */
+ * avoid heap_update() pg_class tuples while processing reindex for
+ * pg_class.
+ */
if (IsIgnoringSystemIndexes())
upd_pg_class_inplace = true;
+
+ /*
+ * Ensure to hold an exclusive lock throughout the transaction. The
+ * lock could be less intensive 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
- /*
- * nailed relations are never updated.
- * We couldn't keep the consistency between the relation
- * descriptors and pg_class tuples.
- */
+#ifndef ENABLE_REINDEX_NAILED_RELATIONS
+
+ /*
+ * nailed relations are never updated. We couldn't keep the
+ * consistency between the relation descriptors and pg_class tuples.
+ */
if (rel->rd_isnailed)
{
if (IsIgnoringSystemIndexes())
else
elog(ERROR, "the target relation %u is nailed", relid);
}
-#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
- RelationClose(rel);
-#endif /* OLD_FILE_NAMING */
+#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
+
+ /*
+ * Shared system indexes must be overwritten because it's impossible
+ * to update pg_class tuples of all databases.
+ */
+ if (rel->rd_rel->relisshared)
+ {
+ if (IsIgnoringSystemIndexes())
+ {
+ overwrite = true;
+ deactivate_needed = true;
+ }
+ else
+ elog(ERROR, "the target relation %u is shared", relid);
+ }
+
+ /*
+ * 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_endscan(scan);
heap_close(indexRelation, AccessShareLock);
if (reindexed)
- /*
- * Ok,we could use the reindexed indexes of the target
- * system relation now.
- */
- {
+ {
+ /*
+ * 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.
+ /*
+ * For pg_class, relhasindex should be set to true here in
+ * place.
*/
- setRelhasindex(relid, true);
+ setRelhasindex(relid, true, false, InvalidOid);
CommandCounterIncrement();
- /*
- * However the following setRelhasindex()
- * is needed to keep consistency with WAL.
+
+ /*
+ * However the following setRelhasindex() is needed to
+ * keep consistency with WAL.
*/
}
- setRelhasindex(relid, true);
+ setRelhasindex(relid, true, false, InvalidOid);
}
}
SetReindexProcessing(old);