postgresql/src/backend/catalog/index.c
Tom Lane 6d1efd76fb Fix handling of NULL constraint conditions: per SQL92 spec, a NULL result
from a constraint condition does not violate the constraint (cf. discussion
on pghackers 12/9/99).  Implemented by adding a parameter to ExecQual,
specifying whether to return TRUE or FALSE when the qual result is
really NULL in three-valued boolean logic.  Currently, ExecRelCheck is
the only caller that asks for TRUE, but if we find any other places that
have the wrong response to NULL, it'll be easy to fix them.
2000-01-19 23:55:03 +00:00

1828 lines
49 KiB
C

/*-------------------------------------------------------------------------
*
* index.c
* code to create and destroy POSTGRES index relations
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.103 2000/01/19 23:54:51 tgl Exp $
*
*
* INTERFACE ROUTINES
* index_create() - Create a cataloged index relation
* index_drop() - Removes index relation from catalogs
*
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/istrat.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/catname.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_index.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/smgr.h"
#include "utils/builtins.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))) / \
((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData))))
/* non-export function prototypes */
static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName,
bool istemp);
static TupleDesc BuildFuncTupleDesc(FuncIndexInfo *funcInfo);
static TupleDesc ConstructTupleDescriptor(Oid heapoid, Relation heapRelation,
List *attributeList, int numatts, AttrNumber *attNums);
static void ConstructIndexReldesc(Relation indexRelation, Oid amoid);
static Oid UpdateRelationRelation(Relation indexRelation, char *temp_relname);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
static void AppendAttributeTuples(Relation indexRelation, int numatts);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
FuncIndexInfo *funcInfo, int natts,
AttrNumber *attNums, Oid *classOids, Node *predicate,
List *attributeList, bool islossy, bool unique, bool primary);
static void DefaultBuild(Relation heapRelation, Relation indexRelation,
int numberOfAttributes, AttrNumber *attributeNumber,
IndexStrategy indexStrategy, uint16 parameterCount,
Datum *parameter, FuncIndexInfoPtr funcInfo, PredInfo *predInfo);
static Oid IndexGetRelation(Oid indexId);
/* ----------------------------------------------------------------
* 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 && get_temp_rel_by_username(indexRelationName) != NULL))
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(FuncIndexInfo *funcInfo)
{
HeapTuple tuple;
TupleDesc funcTupDesc;
Oid retType;
char *funcname;
int4 nargs;
Oid *argtypes;
/*
* Allocate and zero a tuple descriptor.
*/
funcTupDesc = CreateTemplateTupleDesc(1);
funcTupDesc->attrs[0] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
MemSet(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE);
/*
* Lookup the function for the return type.
*/
funcname = FIgetname(funcInfo);
nargs = FIgetnArgs(funcInfo);
argtypes = FIgetArglist(funcInfo);
tuple = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(funcname),
Int32GetDatum(nargs),
PointerGetDatum(argtypes),
0);
if (!HeapTupleIsValid(tuple))
func_error("BuildFuncTupleDesc", funcname, nargs, argtypes, NULL);
retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype;
/*
* Look up the return type in pg_type for the type length.
*/
tuple = SearchSysCacheTuple(TYPEOID,
ObjectIdGetDatum(retType),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Function %s return type does not exist", FIgetname(funcInfo));
/*
* 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]->attcacheoff = -1;
funcTupDesc->attrs[0]->atttypmod = -1;
funcTupDesc->attrs[0]->attstorage = 'p';
funcTupDesc->attrs[0]->attalign = ((Form_pg_type) GETSTRUCT(tuple))->typalign;
/*
* make the attributes name the same as the functions
*/
namestrcpy(&funcTupDesc->attrs[0]->attname, funcname);
return funcTupDesc;
}
/* ----------------------------------------------------------------
* ConstructTupleDescriptor
* ----------------------------------------------------------------
*/
static TupleDesc
ConstructTupleDescriptor(Oid heapoid,
Relation heapRelation,
List *attributeList,
int numatts,
AttrNumber *attNums)
{
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
IndexElem *IndexKey;
TypeName *IndexKeyType;
AttrNumber atnum; /* attributeNumber[attributeOffset] */
AttrNumber atind;
int natts; /* Form_pg_class->relnatts */
char *from; /* used to simplify memcpy below */
char *to; /* used to simplify memcpy below */
int i;
/* ----------------
* allocate the new tuple descriptor
* ----------------
*/
natts = RelationGetForm(heapRelation)->relnatts;
indexTupDesc = CreateTemplateTupleDesc(numatts);
/* ----------------
*
* ----------------
*/
/* ----------------
* for each attribute we are indexing, obtain its attribute
* tuple form from either the static table of system attribute
* tuple forms or the relation tuple descriptor
* ----------------
*/
for (i = 0; i < numatts; i += 1)
{
/* ----------------
* get the attribute number and make sure it's valid
* ----------------
*/
atnum = attNums[i];
if (atnum > natts)
elog(ERROR, "Cannot create index: attribute %d does not exist",
atnum);
if (attributeList)
{
IndexKey = (IndexElem *) lfirst(attributeList);
IndexKeyType = IndexKey->typename;
attributeList = lnext(attributeList);
}
else
IndexKeyType = NULL;
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...-12)
* so we convert atnum into a usable index 0...11 so we can
* use it to dereference the array sysatts[] which stores
* tuple descriptor information for system attributes.
* ----------------
*/
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]);
}
else
{
/* ----------------
* here we are indexing on a normal attribute (1...n)
* ----------------
*/
heapTupDesc = RelationGetDescr(heapRelation);
atind = AttrNumberGetAttrOffset(atnum);
from = (char *) (heapTupDesc->attrs[atind]);
}
/* ----------------
* now that we've determined the "from", let's copy
* the tuple desc data...
* ----------------
*/
to = (char *) (indexTupDesc->attrs[i]);
memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
((Form_pg_attribute) to)->attnum = i + 1;
((Form_pg_attribute) to)->attnotnull = false;
((Form_pg_attribute) to)->atthasdef = false;
((Form_pg_attribute) to)->attcacheoff = -1;
((Form_pg_attribute) to)->atttypmod = -1;
((Form_pg_attribute) to)->attalign = 'i';
/*
* if the keytype is defined, we need to change the tuple form's
* atttypid & attlen field to match that of the key's type
*/
if (IndexKeyType != NULL)
{
HeapTuple tup;
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(IndexKeyType->name),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "create index: type '%s' undefined",
IndexKeyType->name);
((Form_pg_attribute) to)->atttypid = tup->t_data->t_oid;
((Form_pg_attribute) to)->attbyval =
((Form_pg_type) GETSTRUCT(tup))->typbyval;
((Form_pg_attribute) to)->attlen =
((Form_pg_type) GETSTRUCT(tup))->typlen;
((Form_pg_attribute) to)->attstorage = 'p';
((Form_pg_attribute) to)->attalign =
((Form_pg_type) GETSTRUCT(tup))->typalign;
((Form_pg_attribute) to)->atttypmod = IndexKeyType->typmod;
}
/* ----------------
* now we have to drop in the proper relation descriptor
* into the copied tuple form's attrelid and we should be
* all set.
* ----------------
*/
((Form_pg_attribute) to)->attrelid = heapoid;
}
return indexTupDesc;
}
/* ----------------------------------------------------------------
* AccessMethodObjectIdGetForm
* Returns the formated access method tuple given its object identifier.
*
* XXX ADD INDEXING
*
* Note:
* Assumes object identifier is valid.
* ----------------------------------------------------------------
*/
Form_pg_am
AccessMethodObjectIdGetForm(Oid accessMethodObjectId)
{
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);
pg_am_tuple = heap_getnext(pg_am_scan, 0);
/* ----------------
* return NULL if not found
* ----------------
*/
if (!HeapTupleIsValid(pg_am_tuple))
{
heap_endscan(pg_am_scan);
heap_close(pg_am_desc, AccessShareLock);
return NULL;
}
/* ----------------
* if found am tuple, then copy the form and return the copy
* ----------------
*/
aform = (Form_pg_am) palloc(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)
{
extern GlobalMemory CacheCxt;
MemoryContext oldcxt;
/* ----------------
* here we make certain to allocate the access method
* tuple within the cache context lest it vanish when the
* context changes
* ----------------
*/
if (!CacheCxt)
CacheCxt = CreateGlobalMemory("Cache");
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid);
MemoryContextSwitchTo(oldcxt);
/* ----------------
* 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;
}
/* ----------------------------------------------------------------
* UpdateRelationRelation
* ----------------------------------------------------------------
*/
static Oid
UpdateRelationRelation(Relation indexRelation, char *temp_relname)
{
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,
sizeof(*indexRelation->rd_rel),
(char *) indexRelation->rd_rel);
/* ----------------
* the new tuple must have the same oid as the relcache entry for the
* index. sure would be embarassing 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);
/*
* 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 (!IsBootstrapProcessingMode())
{
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;
}
/* ----------------------------------------------------------------
* InitializeAttributeOids
* ----------------------------------------------------------------
*/
static void
InitializeAttributeOids(Relation indexRelation,
int numatts,
Oid indexoid)
{
TupleDesc tupleDescriptor;
int i;
tupleDescriptor = RelationGetDescr(indexRelation);
for (i = 0; i < numatts; i += 1)
tupleDescriptor->attrs[i]->attrelid = indexoid;
}
/* ----------------------------------------------------------------
* 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];
TupleDesc indexTupDesc;
int i;
/* ----------------
* open the attribute relation
* XXX ADD INDEXING
* ----------------
*/
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 (!IsBootstrapProcessingMode() && 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);
/* ----------------
* now we use the information in the index cur_tuple
* descriptor to form the remaining attribute tuples.
* ----------------
*/
indexTupDesc = RelationGetDescr(indexRelation);
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;
}
if (cur_tuple)
heap_freetuple(cur_tuple);
heap_close(pg_attribute, RowExclusiveLock);
if (hasind)
CatalogCloseIndices(Num_pg_attr_indices, idescs);
}
/* ----------------------------------------------------------------
* UpdateIndexRelation
* ----------------------------------------------------------------
*/
static void
UpdateIndexRelation(Oid indexoid,
Oid heapoid,
FuncIndexInfo *funcInfo,
int natts,
AttrNumber *attNums,
Oid *classOids,
Node *predicate,
List *attributeList,
bool islossy,
bool unique,
bool primary)
{
Form_pg_index indexForm;
IndexElem *IndexKey;
char *predString;
text *predText;
int predLen,
itupLen;
Relation pg_index;
HeapTuple tuple;
int i;
Relation idescs[Num_pg_index_indices];
/* ----------------
* allocate an Form_pg_index big enough to hold the
* index-predicate (if any) in string form
* ----------------
*/
if (predicate != NULL)
{
predString = nodeToString(predicate);
predText = (text *) fmgr(F_TEXTIN, predString);
pfree(predString);
}
else
predText = (text *) fmgr(F_TEXTIN, "");
predLen = VARSIZE(predText);
itupLen = predLen + sizeof(FormData_pg_index);
indexForm = (Form_pg_index) palloc(itupLen);
memset(indexForm, 0, sizeof(FormData_pg_index));
memmove((char *) &indexForm->indpred, (char *) predText, predLen);
/* ----------------
* store the oid information into the index tuple form
* ----------------
*/
indexForm->indrelid = heapoid;
indexForm->indexrelid = indexoid;
indexForm->indproc = (PointerIsValid(funcInfo)) ?
FIgetProcOid(funcInfo) : InvalidOid;
indexForm->indislossy = islossy;
indexForm->indisprimary = primary;
indexForm->indisunique = unique;
indexForm->indhaskeytype = 0;
while (attributeList != NIL)
{
IndexKey = (IndexElem *) lfirst(attributeList);
if (IndexKey->typename != NULL)
{
indexForm->indhaskeytype = 1;
break;
}
attributeList = lnext(attributeList);
}
MemSet((char *) &indexForm->indkey[0], 0, sizeof indexForm->indkey);
MemSet((char *) &indexForm->indclass[0], 0, sizeof indexForm->indclass);
/* ----------------
* copy index key and op class information
* ----------------
*/
for (i = 0; i < natts; i += 1)
{
indexForm->indkey[i] = attNums[i];
indexForm->indclass[i] = classOids[i];
}
/*
* If we have a functional index, add all attribute arguments
*/
if (PointerIsValid(funcInfo))
{
for (i = 1; i < FIgetnArgs(funcInfo); i++)
indexForm->indkey[i] = attNums[i];
}
indexForm->indisclustered = '\0'; /* XXX constant */
/* ----------------
* open the system catalog index relation
* ----------------
*/
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
/* ----------------
* form a tuple to insert into pg_index
* ----------------
*/
tuple = heap_addheader(Natts_pg_index,
itupLen,
(char *) indexForm);
/* ----------------
* insert the tuple into the pg_index
* XXX ADD INDEX TUPLES TOO
* ----------------
*/
heap_insert(pg_index, tuple);
/* ----------------
* insert the index tuple into the pg_index
* ----------------
*/
if (!IsBootstrapProcessingMode())
{
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);
}
/* ----------------
* close the relation and free the tuple
* ----------------
*/
heap_close(pg_index, RowExclusiveLock);
pfree(predText);
pfree(indexForm);
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 = (text *) fmgr(F_TEXTIN, predString);
pfree(predString);
}
else
predText = (text *) fmgr(F_TEXTIN, "");
/* open the index system catalog relation */
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexoid),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
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);
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;
extern GlobalMemory CacheCxt;
/* ----------------
* 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 (!CacheCxt)
CacheCxt = CreateGlobalMemory("Cache");
strategy = (IndexStrategy)
MemoryContextAlloc((MemoryContext) CacheCxt, strsize);
if (amsupport > 0)
{
strsize = numatts * (amsupport * sizeof(RegProcedure));
support = (RegProcedure *) MemoryContextAlloc((MemoryContext) CacheCxt,
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,
attrelid, accessMethodObjectId,
amstrategies, amsupport, numatts);
/* ----------------
* store the strategy information in the index reldesc
* ----------------
*/
RelationSetIndexSupport(indexRelation, strategy, support);
}
/* ----------------------------------------------------------------
* index_create
* ----------------------------------------------------------------
*/
void
index_create(char *heapRelationName,
char *indexRelationName,
FuncIndexInfo *funcInfo,
List *attributeList,
Oid accessMethodObjectId,
int numatts,
AttrNumber *attNums,
Oid *classObjectId,
uint16 parameterCount,
Datum *parameter,
Node *predicate,
bool islossy,
bool unique,
bool primary)
{
Relation heapRelation;
Relation indexRelation;
TupleDesc indexTupDesc;
Oid heapoid;
Oid indexoid;
PredInfo *predInfo;
bool istemp = (get_temp_rel_by_username(heapRelationName) != NULL);
char *temp_relname = NULL;
/* ----------------
* check parameters
* ----------------
*/
if (numatts < 1)
elog(ERROR, "must index at least one attribute");
/* ----------------
* get heap relation oid and open the heap relation
* XXX ADD INDEXING
* ----------------
*/
heapoid = GetHeapRelationOid(heapRelationName, indexRelationName, istemp);
/*
* Only SELECT ... FOR UPDATE are allowed while doing this
*/
heapRelation = heap_open(heapoid, ShareLock);
/* ----------------
* construct new tuple descriptor
* ----------------
*/
if (PointerIsValid(funcInfo))
indexTupDesc = BuildFuncTupleDesc(funcInfo);
else
indexTupDesc = ConstructTupleDescriptor(heapoid,
heapRelation,
attributeList,
numatts,
attNums);
/* save user relation name because heap_create changes it */
if (istemp)
{
temp_relname = pstrdup(indexRelationName); /* save original value */
indexRelationName = palloc(NAMEDATALEN);
strcpy(indexRelationName, temp_relname); /* heap_create will
* change this */
}
/* ----------------
* create the index relation
* ----------------
*/
indexRelation = heap_create(indexRelationName,
indexTupDesc, false, istemp, false);
/* ----------------
* construct the index relation descriptor
*
* XXX should have a proper way to create cataloged relations
* ----------------
*/
ConstructIndexReldesc(indexRelation, accessMethodObjectId);
/* ----------------
* add index to catalogs
* (append RELATION tuple)
* ----------------
*/
indexoid = UpdateRelationRelation(indexRelation, temp_relname);
/*
* We create the disk file for this relation here
*/
heap_storage_create(indexRelation);
/* ----------------
* Now get the index procedure (only relevant for functional indices).
* ----------------
*/
if (PointerIsValid(funcInfo))
{
HeapTuple proc_tup;
proc_tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(FIgetname(funcInfo)),
Int32GetDatum(FIgetnArgs(funcInfo)),
PointerGetDatum(FIgetArglist(funcInfo)),
0);
if (!HeapTupleIsValid(proc_tup))
{
func_error("index_create", FIgetname(funcInfo),
FIgetnArgs(funcInfo), FIgetArglist(funcInfo), NULL);
}
FIgetProcOid(funcInfo) = proc_tup->t_data->t_oid;
}
/* ----------------
* now update the object id's of all the attribute
* tuple forms in the index relation's tuple descriptor
* ----------------
*/
InitializeAttributeOids(indexRelation, numatts, indexoid);
/* ----------------
* append ATTRIBUTE tuples
* ----------------
*/
AppendAttributeTuples(indexRelation, numatts);
/* ----------------
* update pg_index
* (append INDEX tuple)
*
* Note that this stows away a representation of "predicate".
* (Or, could define a rule to maintain the predicate) --Nels, Feb '92
* ----------------
*/
UpdateIndexRelation(indexoid, heapoid, funcInfo,
numatts, attNums, classObjectId, predicate,
attributeList, islossy, unique, primary);
predInfo = (PredInfo *) palloc(sizeof(PredInfo));
predInfo->pred = predicate;
predInfo->oldPred = NULL;
/* ----------------
* initialize the index strategy
* ----------------
*/
InitIndexStrategy(numatts, indexRelation, accessMethodObjectId);
/*
* 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,
* so we delay filling them in until just before we're done with
* bootstrapping. Otherwise, we call the routine that constructs the
* index.
*
* In normal processing mode, the heap and index relations are closed
* by index_build() --- but we continue to hold the ShareLock on the
* heap that we acquired above, until end of transaction.
*/
if (IsBootstrapProcessingMode())
{
index_register(heapRelationName, indexRelationName, numatts, attNums,
parameterCount, parameter, funcInfo, predInfo);
/* XXX shouldn't we close the heap and index rels here? */
}
else
{
index_build(heapRelation, indexRelation, numatts, attNums,
parameterCount, parameter, funcInfo, predInfo);
}
}
/* ----------------------------------------------------------------
*
* index_drop
*
* ----------------------------------------------------------------
*/
void
index_drop(Oid indexId)
{
Relation userHeapRelation;
Relation userIndexRelation;
Relation indexRelation;
Relation relationRelation;
Relation attributeRelation;
HeapTuple tuple;
int16 attnum;
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.
* ----------------
*/
userHeapRelation = heap_open(IndexGetRelation(indexId),
AccessExclusiveLock);
userIndexRelation = index_open(indexId);
LockRelation(userIndexRelation, AccessExclusiveLock);
/* ----------------
* DROP INDEX within a transaction block is dangerous, because
* if the transaction is later rolled back there will be no way to
* undo the unlink of the relation's physical file. For now, allow it
* but emit a warning message.
* Someday we might want to consider postponing the physical unlink
* until transaction commit, but that's a lot of work...
* The only case that actually works right is for relations created
* in the current transaction, since the post-abort state would be that
* they don't exist anyway. So, no warning in that case.
* ----------------
*/
if (IsTransactionBlock() && ! userIndexRelation->rd_myxactonly)
elog(NOTICE, "Caution: DROP INDEX cannot be rolled back, so don't abort now");
/* ----------------
* fix DESCRIPTION relation
* ----------------
*/
DeleteComments(indexId);
/* ----------------
* fix RELATION relation
* ----------------
*/
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(indexId),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
heap_delete(relationRelation, &tuple->t_self, NULL);
heap_freetuple(tuple);
heap_close(relationRelation, RowExclusiveLock);
/* ----------------
* fix ATTRIBUTE relation
* ----------------
*/
attributeRelation = heap_openr(AttributeRelationName, RowExclusiveLock);
attnum = 1; /* indexes start at 1 */
while (HeapTupleIsValid(tuple = SearchSysCacheTupleCopy(ATTNUM,
ObjectIdGetDatum(indexId),
Int16GetDatum(attnum),
0, 0)))
{
heap_delete(attributeRelation, &tuple->t_self, NULL);
heap_freetuple(tuple);
attnum++;
}
heap_close(attributeRelation, RowExclusiveLock);
/* ----------------
* fix INDEX relation
* ----------------
*/
indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);
tuple = SearchSysCacheTupleCopy(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
heap_delete(indexRelation, &tuple->t_self, NULL);
heap_freetuple(tuple);
heap_close(indexRelation, RowExclusiveLock);
/*
* flush buffer cache and physically remove the file
*/
ReleaseRelationBuffers(userIndexRelation);
if (smgrunlink(DEFAULT_SMGR, userIndexRelation) != SM_SUCCESS)
elog(ERROR, "index_drop: unlink: %m");
/*
* Close rels, but keep locks
*/
index_close(userIndexRelation);
heap_close(userHeapRelation, NoLock);
RelationForgetRelation(indexId);
/* does something only if it is a temp index */
remove_temp_relation(indexId);
}
/* ----------------------------------------------------------------
* index_build support
* ----------------------------------------------------------------
*/
/* ----------------
* FormIndexDatum
* ----------------
*/
void
FormIndexDatum(int numberOfAttributes,
AttrNumber *attributeNumber,
HeapTuple heapTuple,
TupleDesc heapDescriptor,
Datum *datum,
char *nullv,
FuncIndexInfoPtr fInfo)
{
AttrNumber i;
int offset;
bool isNull;
/* ----------------
* for each attribute we need from the heap tuple,
* get the attribute and stick it into the datum and
* null arrays.
* ----------------
*/
for (i = 1; i <= numberOfAttributes; i++)
{
offset = AttrNumberGetAttrOffset(i);
datum[offset] = PointerGetDatum(GetIndexValue(heapTuple,
heapDescriptor,
offset,
attributeNumber,
fInfo,
&isNull));
nullv[offset] = (isNull) ? 'n' : ' ';
}
}
/* ----------------
* UpdateStats
* ----------------
*/
void
UpdateStats(Oid relid, long reltuples, bool hasindex)
{
Relation whichRel;
Relation pg_class;
HeapTuple tuple;
HeapTuple newtup;
long 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;
/* ----------------
* This routine handles updates for both the heap and index relation
* statistics. In order to guarantee that we're able to *see* the index
* relation tuple, we bump the command counter id here. The index
* relation tuple was created in the current transaction.
* ----------------
*/
CommandCounterIncrement();
/* ----------------
* CommandCounterIncrement() flushes invalid cache entries, including
* those for the heap and index relations for which we're updating
* statistics. Now that the cache is flushed, it's safe to open the
* relation again. We need the relation open in order to figure out
* how many blocks it contains.
* ----------------
*/
/*
* Can't use heap_open here since we don't know if it's an index...
*/
whichRel = RelationIdGetRelation(relid);
if (!RelationIsValid(whichRel))
elog(ERROR, "UpdateStats: cannot open relation id %u", relid);
LockRelation(whichRel, ShareLock);
/* ----------------
* Find the RELATION relation tuple for the given relation.
* ----------------
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
if (!RelationIsValid(pg_class))
elog(ERROR, "UpdateStats: could not open RELATION relation");
if (!IsBootstrapProcessingMode())
{
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(relid));
pg_class_scan = heap_beginscan(pg_class, 0, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, 0);
}
if (!HeapTupleIsValid(tuple))
{
if (IsBootstrapProcessingMode())
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
elog(ERROR, "UpdateStats: cannot scan RELATION relation");
}
/* ----------------
* Figure values to insert.
*
* If we found zero tuples in the scan, do NOT believe it; instead put
* a bogus estimate into the statistics fields. Otherwise, the common
* pattern "CREATE TABLE; CREATE INDEX; insert data" leaves the table
* with zero size statistics until a VACUUM is done. The optimizer will
* generate very bad plans if the stats claim the table is empty when
* it is actually sizable. See also CREATE TABLE in heap.c.
* ----------------
*/
relpages = RelationGetNumberOfBlocks(whichRel);
if (reltuples == 0)
{
if (relpages == 0)
{
/* Bogus defaults for a virgin table, same as heap.c */
reltuples = 1000;
relpages = 10;
}
else if (whichRel->rd_rel->relkind == RELKIND_INDEX && relpages <= 2)
{
/* Empty index, leave bogus defaults in place */
reltuples = 1000;
}
else
reltuples = relpages * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
}
/*
* We shouldn't have to do this, but we do... Modify the reldesc in
* place with the new values so that the cache contains the latest
* copy.
*/
whichRel->rd_rel->relhasindex = hasindex;
whichRel->rd_rel->relpages = relpages;
whichRel->rd_rel->reltuples = reltuples;
/* ----------------
* Update statistics in pg_class.
* ----------------
*/
if (IsBootstrapProcessingMode())
{
/*
* At bootstrap time, we don't need to worry about concurrency or
* visibility of changes, so we cheat.
*/
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
rd_rel->relpages = relpages;
rd_rel->reltuples = reltuples;
rd_rel->relhasindex = hasindex;
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
}
else
{
/* During normal processing, must work harder. */
for (i = 0; i < Natts_pg_class; i++)
{
nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' ';
replace[i] = ' ';
values[i] = (Datum) NULL;
}
replace[Anum_pg_class_relpages - 1] = 'r';
values[Anum_pg_class_relpages - 1] = (Datum) relpages;
replace[Anum_pg_class_reltuples - 1] = 'r';
values[Anum_pg_class_reltuples - 1] = (Datum) reltuples;
replace[Anum_pg_class_relhasindex - 1] = 'r';
values[Anum_pg_class_relhasindex - 1] = CharGetDatum(hasindex);
newtup = heap_modifytuple(tuple, pg_class, values, nulls, replace);
heap_update(pg_class, &tuple->t_self, newtup, NULL);
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);
heap_freetuple(newtup);
}
if (!IsBootstrapProcessingMode())
heap_freetuple(tuple);
else
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, ShareLock);
}
/* -------------------------
* FillDummyExprContext
* Sets up dummy ExprContext and TupleTableSlot objects for use
* with ExecQual.
*
* NOTE: buffer is passed for historical reasons; it should
* almost certainly always be InvalidBuffer.
* -------------------------
*/
void
FillDummyExprContext(ExprContext *econtext,
TupleTableSlot *slot,
TupleDesc tupdesc,
Buffer buffer)
{
econtext->ecxt_scantuple = slot;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
econtext->ecxt_param_list_info = NULL;
econtext->ecxt_range_table = NULL;
slot->ttc_tupleDescriptor = tupdesc;
slot->ttc_buffer = buffer;
slot->ttc_shouldFree = false;
}
/* ----------------
* DefaultBuild
* ----------------
*/
static void
DefaultBuild(Relation heapRelation,
Relation indexRelation,
int numberOfAttributes,
AttrNumber *attributeNumber,
IndexStrategy indexStrategy, /* not used */
uint16 parameterCount, /* not used */
Datum *parameter, /* not used */
FuncIndexInfoPtr funcInfo,
PredInfo *predInfo)
{
HeapScanDesc scan;
HeapTuple heapTuple;
IndexTuple indexTuple;
TupleDesc heapDescriptor;
TupleDesc indexDescriptor;
Datum *datum;
char *nullv;
long reltuples,
indtuples;
#ifndef OMIT_PARTIAL_INDEX
ExprContext *econtext;
TupleTable tupleTable;
TupleTableSlot *slot;
#endif
Node *predicate;
Node *oldPred;
InsertIndexResult insertResult;
/* ----------------
* more & better checking is needed
* ----------------
*/
Assert(OidIsValid(indexRelation->rd_rel->relam)); /* XXX */
/* ----------------
* get the tuple descriptors from the relations so we know
* how to form the index tuples..
* ----------------
*/
heapDescriptor = RelationGetDescr(heapRelation);
indexDescriptor = RelationGetDescr(indexRelation);
/* ----------------
* datum and null are arrays in which we collect the index attributes
* when forming a new index tuple.
* ----------------
*/
datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
nullv = (char *) palloc(numberOfAttributes * sizeof *nullv);
/*
* If this is a predicate (partial) index, we will need to evaluate
* the predicate using ExecQual, which requires the current tuple to
* be in a slot of a TupleTable. In addition, ExecQual must have an
* ExprContext referring to that slot. Here, we initialize dummy
* TupleTable and ExprContext objects for this purpose. --Nels, Feb
* '92
*/
predicate = predInfo->pred;
oldPred = predInfo->oldPred;
#ifndef OMIT_PARTIAL_INDEX
if (predicate != NULL || oldPred != NULL)
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
econtext = makeNode(ExprContext);
FillDummyExprContext(econtext, slot, heapDescriptor, InvalidBuffer);
}
else
{
econtext = NULL;
tupleTable = 0;
slot = NULL;
}
#endif /* OMIT_PARTIAL_INDEX */
/* ----------------
* Ok, begin our scan of the base relation.
* ----------------
*/
scan = heap_beginscan(heapRelation, /* relation */
0, /* start at end */
SnapshotNow, /* seeself */
0, /* number of keys */
(ScanKey) NULL); /* scan key */
reltuples = indtuples = 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.
* ----------------
*/
while (HeapTupleIsValid(heapTuple = heap_getnext(scan, 0)))
{
reltuples++;
#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)
{
/* SetSlotContents(slot, heapTuple); */
slot->val = heapTuple;
if (ExecQual((List *) oldPred, econtext, false))
{
indtuples++;
continue;
}
}
/*
* Skip this tuple if it doesn't satisfy the partial-index
* predicate
*/
if (predicate != NULL)
{
/* SetSlotContents(slot, heapTuple); */
slot->val = heapTuple;
if (! ExecQual((List *) 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.
* ----------------
*/
FormIndexDatum(numberOfAttributes, /* num attributes */
attributeNumber, /* array of att nums to extract */
heapTuple, /* tuple from base relation */
heapDescriptor, /* heap tuple's descriptor */
datum, /* return: array of attributes */
nullv, /* return: array of char's */
funcInfo);
indexTuple = index_formtuple(indexDescriptor,
datum,
nullv);
indexTuple->t_tid = heapTuple->t_self;
insertResult = index_insert(indexRelation, datum, nullv,
&(heapTuple->t_self), heapRelation);
if (insertResult)
pfree(insertResult);
pfree(indexTuple);
}
heap_endscan(scan);
#ifndef OMIT_PARTIAL_INDEX
if (predicate != NULL || oldPred != NULL)
{
/* parameter was 'false', almost certainly wrong --- tgl 9/21/99 */
ExecDropTupleTable(tupleTable, true);
}
#endif /* OMIT_PARTIAL_INDEX */
pfree(nullv);
pfree(datum);
/*
* 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, true);
UpdateStats(irelid, indtuples, false);
if (oldPred != NULL)
{
if (indtuples == reltuples)
predicate = NULL;
UpdateIndexPredicate(irelid, oldPred, predicate);
}
}
}
/* ----------------
* index_build
* ----------------
*/
void
index_build(Relation heapRelation,
Relation indexRelation,
int numberOfAttributes,
AttrNumber *attributeNumber,
uint16 parameterCount,
Datum *parameter,
FuncIndexInfo *funcInfo,
PredInfo *predInfo)
{
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..
* ----------------
*/
if (RegProcedureIsValid(procedure))
fmgr(procedure,
heapRelation,
indexRelation,
numberOfAttributes,
attributeNumber,
RelationGetIndexStrategy(indexRelation),
parameterCount,
parameter,
funcInfo,
predInfo);
else
DefaultBuild(heapRelation,
indexRelation,
numberOfAttributes,
attributeNumber,
RelationGetIndexStrategy(indexRelation),
parameterCount,
parameter,
funcInfo,
predInfo);
}
/*
* IndexGetRelation: given an index's relation OID, get the OID of the
* relation it is an index on. Uses the system cache.
*/
static Oid
IndexGetRelation(Oid indexId)
{
HeapTuple tuple;
Form_pg_index index;
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "IndexGetRelation: can't find index id %u",
indexId);
}
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
return index->indrelid;
}
/*
* IndexIsUnique: given an index's relation OID, see if it
* is unique using the system cache.
*/
bool
IndexIsUnique(Oid indexId)
{
HeapTuple tuple;
Form_pg_index index;
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "IndexIsUnique: can't find index id %u",
indexId);
}
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
return index->indisunique;
}
/*
* IndexIsUniqueNoCache: same as above function, but don't use the
* system cache. if we are called from btbuild, the transaction
* that is adding the entry to pg_index has not been committed yet.
* the system cache functions will do a heap scan, but only with
* NowTimeQual, not SelfTimeQual, so it won't find tuples added
* by the current transaction (which is good, because if the transaction
* is aborted, you don't want the tuples sitting around in the cache).
* so anyway, we have to do our own scan with SelfTimeQual.
* this is only called when a new index is created, so it's OK
* if it's slow.
*/
bool
IndexIsUniqueNoCache(Oid indexId)
{
Relation pg_index;
ScanKeyData skey[1];
HeapScanDesc scandesc;
HeapTuple tuple;
Form_pg_index index;
bool isunique;
pg_index = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], (bits16) 0x0,
Anum_pg_index_indexrelid,
(RegProcedure) F_OIDEQ,
ObjectIdGetDatum(indexId));
scandesc = heap_beginscan(pg_index, 0, SnapshotSelf, 1, skey);
/* NO CACHE */
tuple = heap_getnext(scandesc, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "IndexIsUniqueNoCache: can't find index id %u", indexId);
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
isunique = index->indisunique;
heap_endscan(scandesc);
heap_close(pg_index, AccessShareLock);
return isunique;
}