postgresql/src/backend/catalog/index.c

2212 lines
60 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* index.c
* code to create and destroy POSTGRES index relations
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.125 2000/07/12 02:36:55 tgl Exp $
*
*
* INTERFACE ROUTINES
* index_create() - Create a cataloged index relation
* index_drop() - Removes index relation from catalogs
*
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
1999-07-16 07:00:38 +02:00
#include "access/genam.h"
#include "access/heapam.h"
#include "access/istrat.h"
#include "bootstrap/bootstrap.h"
#include "catalog/catname.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
2000-06-15 05:33:12 +02:00
#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/catcache.h"
#include "utils/fmgroids.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "utils/temprel.h"
2000-02-18 10:30:20 +01:00
#include "utils/inval.h"
1996-11-05 12:57:55 +01:00
/*
* macros used in guessing how many tuples are on a page.
*/
1999-07-07 11:27:28 +02:00
#define AVG_ATTR_SIZE 8
#define NTUPLES_PER_PAGE(natts) \
((BLCKSZ - MAXALIGN(sizeof (PageHeaderData))) / \
((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData))))
/* non-export function prototypes */
1999-05-25 18:15:34 +02:00
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,
FuncIndexInfoPtr funcInfo, PredInfo *predInfo,
bool unique, IndexStrategy indexStrategy);
static Oid IndexGetRelation(Oid indexId);
static bool activate_index(Oid indexId, bool activate);
static bool reindexing = false;
extern bool
SetReindexProcessing(bool reindexmode)
2000-02-18 10:30:20 +01:00
{
bool old = reindexing;
2000-02-18 10:30:20 +01:00
reindexing = reindexmode;
return old;
}
extern bool
IsReindexProcessing(void)
2000-02-18 10:30:20 +01:00
{
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;
1999-05-25 18:15:34 +02:00
indoid = RelnameFindRelid(indexRelationName);
if ((!istemp && OidIsValid(indoid)) ||
(istemp && get_temp_rel_by_username(indexRelationName) != NULL))
1998-01-06 20:42:33 +01:00
elog(ERROR, "Cannot create index: '%s' already exists",
indexRelationName);
heapoid = RelnameFindRelid(heapRelationName);
if (!OidIsValid(heapoid))
1998-01-06 20:42:33 +01:00
elog(ERROR, "Cannot create index on '%s': relation does not exist",
heapRelationName);
1999-05-25 18:15:34 +02:00
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);
1998-09-01 05:29:17 +02:00
funcTupDesc->attrs[0] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
1997-09-18 22:22:58 +02:00
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))
1998-05-10 01:43:45 +02:00
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))
1998-01-06 20:42:33 +01:00
elog(ERROR, "Function %s return type does not exist", FIgetname(funcInfo));
/*
* Assign some of the attributes values. Leave the rest as 0.
*/
1998-09-01 05:29:17 +02:00
funcTupDesc->attrs[0]->attlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
funcTupDesc->attrs[0]->atttypid = retType;
funcTupDesc->attrs[0]->attnum = 1;
1998-09-01 05:29:17 +02:00
funcTupDesc->attrs[0]->attbyval = ((Form_pg_type) GETSTRUCT(tuple))->typbyval;
1998-02-07 22:41:52 +01:00
funcTupDesc->attrs[0]->attcacheoff = -1;
funcTupDesc->attrs[0]->atttypmod = -1;
funcTupDesc->attrs[0]->attstorage = 'p';
1998-09-01 05:29:17 +02:00
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);
1998-09-01 05:29:17 +02:00
return funcTupDesc;
}
/* ----------------------------------------------------------------
* ConstructTupleDescriptor
* ----------------------------------------------------------------
*/
static TupleDesc
ConstructTupleDescriptor(Oid heapoid,
Relation heapRelation,
List *attributeList,
int numatts,
1998-09-01 05:29:17 +02:00
AttrNumber *attNums)
{
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
IndexElem *IndexKey;
TypeName *IndexKeyType;
AttrNumber atnum; /* attributeNumber[attributeOffset] */
AttrNumber atind;
1998-09-01 05:29:17 +02:00
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
* ----------------
*/
1998-09-01 05:29:17 +02:00
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)
1998-01-06 20:42:33 +01:00
elog(ERROR, "Cannot create index: attribute %d does not exist",
atnum);
if (attributeList)
{
IndexKey = (IndexElem *) lfirst(attributeList);
1998-08-26 07:22:58 +02:00
IndexKeyType = IndexKey->typename;
attributeList = lnext(attributeList);
}
else
IndexKeyType = NULL;
1998-09-01 05:29:17 +02:00
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)
1998-01-06 20:42:33 +01:00
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)
* ----------------
*/
1998-09-01 05:29:17 +02:00
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);
1998-09-01 05:29:17 +02:00
((Form_pg_attribute) to)->attnum = i + 1;
1998-09-01 05:29:17 +02:00
((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))
1998-01-06 20:42:33 +01:00
elog(ERROR, "create index: type '%s' undefined",
IndexKeyType->name);
1998-11-27 20:52:36 +01:00
((Form_pg_attribute) to)->atttypid = tup->t_data->t_oid;
1998-09-01 05:29:17 +02:00
((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';
1998-09-01 05:29:17 +02:00
((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.
* ----------------
*/
1998-09-01 05:29:17 +02:00
((Form_pg_attribute) to)->attrelid = heapoid;
}
return indexTupDesc;
}
/* ----------------------------------------------------------------
1999-05-25 18:15:34 +02:00
* AccessMethodObjectIdGetForm
* Returns an access method tuple given its object identifier,
* or NULL if no such AM tuple can be found.
*
* Scanning is done using CurrentMemoryContext as working storage,
* but the returned tuple will be allocated in resultCxt (which is
* typically CacheMemoryContext).
*
* There was a note here about adding indexing, but I don't see a need
* for it. There are so few tuples in pg_am that an indexscan would
* surely be slower.
* ----------------------------------------------------------------
*/
Form_pg_am
AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
MemoryContext resultCxt)
{
Relation pg_am_desc;
HeapScanDesc pg_am_scan;
HeapTuple pg_am_tuple;
ScanKeyData key;
Form_pg_am aform;
/* ----------------
* form a scan key for the pg_am relation
* ----------------
*/
ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(accessMethodObjectId));
/* ----------------
* fetch the desired access method tuple
* ----------------
*/
pg_am_desc = heap_openr(AccessMethodRelationName, AccessShareLock);
1998-07-27 21:38:40 +02:00
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);
1998-09-01 05:29:17 +02:00
return NULL;
}
/* ----------------
* 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;
}
/* ----------------------------------------------------------------
* 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,
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.
* ----------------
*/
1998-11-27 20:52:36 +01:00
tuple->t_data->t_oid = RelationGetRelid(indexRelation);
heap_insert(pg_class, tuple);
if (temp_relname)
create_temp_relation(temp_relname, tuple);
1999-05-25 18:15:34 +02:00
/*
* 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.
*/
2000-02-18 10:30:20 +01:00
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);
}
1998-11-27 20:52:36 +01:00
tupleOid = tuple->t_data->t_oid;
heap_freetuple(tuple);
heap_close(pg_class, RowExclusiveLock);
1998-09-01 05:29:17 +02:00
return tupleOid;
}
/* ----------------------------------------------------------------
* InitializeAttributeOids
* ----------------------------------------------------------------
*/
static void
InitializeAttributeOids(Relation indexRelation,
int numatts,
Oid indexoid)
{
TupleDesc tupleDescriptor;
int i;
1998-09-01 05:29:17 +02:00
tupleDescriptor = RelationGetDescr(indexRelation);
for (i = 0; i < numatts; i += 1)
tupleDescriptor->attrs[i]->attrelid = indexoid;
}
/* ----------------------------------------------------------------
* AppendAttributeTuples
*
* 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);
/* ----------------
1998-09-01 05:29:17 +02:00
* initialize *null, *replace and *value
* ----------------
*/
1997-09-18 22:22:58 +02:00
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,
1999-05-25 18:15:34 +02:00
ATTRIBUTE_TUPLE_SIZE,
(char *) (indexRelation->rd_att->attrs[0]));
hasind = false;
2000-02-18 10:30:20 +01:00
if (!IsIgnoringSystemIndexes() && pg_attribute->rd_rel->relhasindex)
{
hasind = true;
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
}
/* ----------------
* insert the first attribute tuple.
* ----------------
*/
cur_tuple = heap_modifytuple(init_tuple,
pg_attribute,
value,
nullv,
replace);
heap_freetuple(init_tuple);
heap_insert(pg_attribute, cur_tuple);
if (hasind)
CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, cur_tuple);
/* ----------------
* now we use the information in the index cur_tuple
* descriptor to form the remaining attribute tuples.
* ----------------
*/
1998-09-01 05:29:17 +02:00
indexTupDesc = RelationGetDescr(indexRelation);
for (i = 1; i < numatts; i += 1)
{
/* ----------------
* process the remaining attributes...
* ----------------
*/
memmove(GETSTRUCT(cur_tuple),
(char *) indexTupDesc->attrs[i],
1999-05-25 18:15:34 +02:00
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,
1998-09-01 05:29:17 +02:00
AttrNumber *attNums,
Oid *classOids,
Node *predicate,
List *attributeList,
bool islossy,
bool unique,
1999-05-25 18:15:34 +02:00
bool primary)
{
1998-09-01 05:29:17 +02:00
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];
/* ----------------
1998-09-01 05:29:17 +02:00
* allocate an Form_pg_index big enough to hold the
* index-predicate (if any) in string form
* ----------------
*/
if (predicate != NULL)
{
predString = nodeToString(predicate);
predText = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(predString)));
pfree(predString);
}
else
predText = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum("")));
predLen = VARSIZE(predText);
itupLen = predLen + sizeof(FormData_pg_index);
1998-09-01 05:29:17 +02:00
indexForm = (Form_pg_index) palloc(itupLen);
1999-05-25 18:15:34 +02:00
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);
1998-08-26 07:22:58 +02:00
if (IndexKey->typename != NULL)
{
indexForm->indhaskeytype = 1;
break;
}
attributeList = lnext(attributeList);
}
1997-09-18 22:22:58 +02:00
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
* ----------------
*/
2000-02-18 10:30:20 +01:00
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);
}
/* ----------------
* 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 = 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 = 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);
1999-11-24 01:44:37 +01:00
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;
/* ----------------
* 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,
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,
1998-09-01 05:29:17 +02:00
AttrNumber *attNums,
Oid *classObjectId,
Node *predicate,
bool islossy,
bool unique,
bool primary,
bool allow_system_table_mods)
{
Relation heapRelation;
Relation indexRelation;
TupleDesc indexTupDesc;
Oid heapoid;
Oid indexoid;
PredInfo *predInfo;
bool istemp = (get_temp_rel_by_username(heapRelationName) != NULL);
1999-05-25 18:15:34 +02:00
char *temp_relname = NULL;
/* ----------------
* check parameters
* ----------------
*/
2000-02-18 10:30:20 +01:00
SetReindexProcessing(false);
if (numatts < 1)
1998-01-06 20:42:33 +01:00
elog(ERROR, "must index at least one attribute");
1999-05-25 18:15:34 +02:00
/* ----------------
* get heap relation oid and open the heap relation
* ----------------
*/
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);
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
* ----------------
*/
indexRelation = heap_create(indexRelationName, indexTupDesc,
istemp, false, allow_system_table_mods);
/* ----------------
* 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 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,
funcInfo, predInfo, unique);
/* XXX shouldn't we close the heap and index rels here? */
}
else
{
index_build(heapRelation, indexRelation, numatts, attNums,
funcInfo, predInfo, unique);
}
}
/* ----------------------------------------------------------------
*
* index_drop
*
* ----------------------------------------------------------------
*/
void
index_drop(Oid indexId)
{
Oid heapId;
Relation userHeapRelation;
Relation userIndexRelation;
Relation indexRelation;
Relation relationRelation;
1998-08-21 00:07:46 +02:00
Relation attributeRelation;
HeapTuple tuple;
1999-05-25 18:15:34 +02:00
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.
* ----------------
*/
heapId = IndexGetRelation(indexId);
userHeapRelation = heap_open(heapId, 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);
/* Remove the pg_class tuple for the index itself */
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(indexId),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
heap_delete(relationRelation, &tuple->t_self, NULL);
heap_freetuple(tuple);
/*
* Find the pg_class tuple for the owning relation. We do not attempt
* to clear relhasindex, since we are too lazy to test whether any other
* indexes remain (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.
*/
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(heapId),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
ImmediateInvalidateSharedHeapTuple(relationRelation, tuple);
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));
1998-08-21 00:07:46 +02:00
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,
1998-09-01 05:29:17 +02:00
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));
1998-08-28 06:57:21 +02:00
nullv[offset] = (isNull) ? 'n' : ' ';
}
}
2000-02-18 10:30:20 +01:00
/* --------------------------------------------
* Lock class info for update
* --------------------------------------------
*/
static
bool
LockClassinfoForUpdate(Oid relid, HeapTuple rtup, Buffer *buffer, bool confirmCommitted)
2000-02-18 10:30:20 +01:00
{
HeapTuple classTuple;
Form_pg_class pgcform;
2000-02-18 10:30:20 +01:00
bool test;
Relation relationRelation;
classTuple = SearchSysCacheTuple(RELOID, PointerGetDatum(relid),
0, 0, 0);
2000-02-18 10:30:20 +01:00
if (!HeapTupleIsValid(classTuple))
return false;
rtup->t_self = classTuple->t_self;
pgcform = (Form_pg_class) GETSTRUCT(classTuple);
relationRelation = heap_openr(RelationRelationName, RowShareLock);
test = heap_mark4update(relationRelation, rtup, buffer);
switch (test)
{
case HeapTupleSelfUpdated:
case HeapTupleMayBeUpdated:
break;
default:
elog(ERROR, "LockStatsForUpdate couldn't lock relid %u", relid);
return false;
}
RelationInvalidateHeapTuple(relationRelation, rtup);
if (confirmCommitted)
{
HeapTupleHeader th = rtup->t_data;
2000-02-18 10:30:20 +01:00
if (!(th->t_infomask & HEAP_XMIN_COMMITTED))
elog(ERROR, "The tuple isn't committed");
if (th->t_infomask & HEAP_XMAX_COMMITTED)
if (!(th->t_infomask & HEAP_MARKED_FOR_UPDATE))
elog(ERROR, "The tuple is already deleted");
}
heap_close(relationRelation, NoLock);
return true;
}
/* ---------------------------------------------
* Indexes of the relation active ?
* ---------------------------------------------
*/
bool
IndexesAreActive(Oid relid, bool confirmCommitted)
2000-02-18 10:30:20 +01:00
{
HeapTupleData tuple;
2000-02-18 10:30:20 +01:00
Relation indexRelation;
Buffer buffer;
HeapScanDesc scan;
ScanKeyData entry;
2000-02-18 10:30:20 +01:00
bool isactive;
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)
elog(ERROR, "relation %u isn't an indexable relation", relid);
2000-02-18 10:30:20 +01:00
isactive = ((Form_pg_class) GETSTRUCT(&tuple))->relhasindex;
ReleaseBuffer(buffer);
if (isactive)
return isactive;
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid));
2000-02-18 10:30:20 +01:00
scan = heap_beginscan(indexRelation, false, SnapshotNow,
1, &entry);
2000-02-18 10:30:20 +01:00
if (!heap_getnext(scan, 0))
isactive = true;
heap_endscan(scan);
heap_close(indexRelation, NoLock);
return isactive;
}
/* ----------------
* set relhasindex of pg_class in place
* ----------------
*/
void
setRelhasindexInplace(Oid relid, bool hasindex, bool immediate)
{
Relation whichRel;
Relation pg_class;
HeapTuple tuple;
Form_pg_class rd_rel;
HeapScanDesc pg_class_scan = NULL;
2000-02-18 10:30:20 +01:00
/* ----------------
* This routine handles updates for only the heap relation
* hasindex. In order to guarantee that we're able to *see* the index
* relation tuple, we bump the command counter id here.
* ----------------
*/
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.
* ----------------
*/
whichRel = heap_open(relid, ShareLock);
if (!RelationIsValid(whichRel))
elog(ERROR, "setRelhasindexInplace: cannot open relation id %u", relid);
/* ----------------
* Find the RELATION relation tuple for the given relation.
* ----------------
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
if (!RelationIsValid(pg_class))
elog(ERROR, "setRelhasindexInplace: could not open RELATION relation");
if (!IsIgnoringSystemIndexes())
{
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(relid), 0, 0, 0);
2000-02-18 10:30:20 +01:00
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(relid));
pg_class_scan = heap_beginscan(pg_class, 0, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, 0);
}
if (!HeapTupleIsValid(tuple))
{
if (pg_class_scan)
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
elog(ERROR, "setRelhasindexInplace: cannot scan RELATION relation");
}
2000-02-18 10:30:20 +01:00
/*
* Confirm that target tuple is locked by this transaction in case of
* immedaite updation.
2000-02-18 10:30:20 +01:00
*/
if (immediate)
{
HeapTupleHeader th = tuple->t_data;
2000-02-18 10:30:20 +01:00
if (!(th->t_infomask & HEAP_XMIN_COMMITTED))
elog(ERROR, "Immediate hasindex updation can be done only for committed tuples %x", th->t_infomask);
if (th->t_infomask & HEAP_XMAX_INVALID)
elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask);
if (th->t_infomask & HEAP_XMAX_COMMITTED)
elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask);
if (!(th->t_infomask & HEAP_MARKED_FOR_UPDATE))
elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask);
if (!(TransactionIdIsCurrentTransactionId(th->t_xmax)))
elog(ERROR, "The updating tuple is already locked by another backend");
}
/*
* 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;
/* ----------------
* Update hasindex in pg_class.
* ----------------
*/
if (pg_class_scan)
{
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
rd_rel->relhasindex = hasindex;
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
}
else
{
HeapTupleData htup;
2000-02-18 10:30:20 +01:00
Buffer buffer;
htup.t_self = tuple->t_self;
heap_fetch(pg_class, SnapshotNow, &htup, &buffer);
rd_rel = (Form_pg_class) GETSTRUCT(&htup);
rd_rel->relhasindex = hasindex;
WriteBuffer(buffer);
}
/*
* Send out a shared-cache-inval message so other backends notice the
* update and fix their syscaches/relcaches.
*/
if (!IsBootstrapProcessingMode())
ImmediateInvalidateSharedHeapTuple(pg_class, tuple);
2000-02-18 10:30:20 +01:00
if (!pg_class_scan)
heap_freetuple(tuple);
else
heap_endscan(pg_class_scan);
heap_close(pg_class, NoLock);
heap_close(whichRel, NoLock);
}
/* ----------------
* UpdateStats
* ----------------
*/
void
2000-02-18 10:30:20 +01:00
UpdateStats(Oid relid, long reltuples, bool inplace)
{
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;
2000-02-18 10:30:20 +01:00
bool in_place_upd;
/* ----------------
* This routine handles updates for both the heap and index relation
* statistics. In order to guarantee that we're able to *see* the index
* relation tuple, we bump the command counter id here. The index
* relation tuple was created in the current transaction.
* ----------------
*/
CommandCounterIncrement();
/* ----------------
* CommandCounterIncrement() flushes invalid cache entries, including
* those for the heap and index relations for which we're updating
* statistics. Now that the cache is flushed, it's safe to open the
* relation again. We need the relation open in order to figure out
* how many blocks it contains.
* ----------------
*/
/*
* 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))
1998-01-06 20:42:33 +01:00
elog(ERROR, "UpdateStats: could not open RELATION relation");
2000-02-18 10:30:20 +01:00
in_place_upd = (inplace || IsBootstrapProcessingMode());
if (!in_place_upd)
1998-08-20 17:16:59 +02:00
{
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
}
else
{
ScanKeyData key[1];
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(relid));
1998-08-20 17:16:59 +02:00
pg_class_scan = heap_beginscan(pg_class, 0, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, 0);
}
if (!HeapTupleIsValid(tuple))
{
2000-02-18 10:30:20 +01:00
if (pg_class_scan)
1998-08-20 17:16:59 +02:00
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
1998-01-06 20:42:33 +01:00
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->relpages = relpages;
whichRel->rd_rel->reltuples = reltuples;
/* ----------------
* Update statistics in pg_class.
* ----------------
*/
2000-02-18 10:30:20 +01:00
if (in_place_upd)
{
/*
* At bootstrap time, we don't need to worry about concurrency or
* visibility of changes, so we cheat.
*/
2000-02-18 10:30:20 +01:00
if (!IsBootstrapProcessingMode())
ImmediateInvalidateSharedHeapTuple(pg_class, tuple);
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
rd_rel->relpages = relpages;
rd_rel->reltuples = reltuples;
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;
newtup = heap_modifytuple(tuple, pg_class, values, nulls, replace);
1999-11-24 01:44:37 +01:00
heap_update(pg_class, &tuple->t_self, newtup, NULL);
2000-02-18 10:30:20 +01:00
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);
}
heap_freetuple(newtup);
}
2000-02-18 10:30:20 +01:00
if (!pg_class_scan)
heap_freetuple(tuple);
1998-08-20 17:16:59 +02:00
else
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
/* Cheating a little bit since we didn't open it with heap_open... */
heap_close(whichRel, ShareLock);
}
/* ----------------
* DefaultBuild
*
* 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.
*
* Anyone want to wager whether it would actually work if executed?
* ----------------
*/
static void
DefaultBuild(Relation heapRelation,
Relation indexRelation,
int numberOfAttributes,
1998-09-01 05:29:17 +02:00
AttrNumber *attributeNumber,
FuncIndexInfoPtr funcInfo,
PredInfo *predInfo,
bool unique, /* not used */
IndexStrategy indexStrategy) /* not used */
{
HeapScanDesc scan;
HeapTuple heapTuple;
IndexTuple indexTuple;
TupleDesc heapDescriptor;
TupleDesc indexDescriptor;
Datum *datum;
char *nullv;
long reltuples,
indtuples;
1996-11-05 12:57:55 +01:00
#ifndef OMIT_PARTIAL_INDEX
ExprContext *econtext;
TupleTable tupleTable;
TupleTableSlot *slot;
1996-11-05 12:57:55 +01:00
#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..
* ----------------
*/
1998-09-01 05:29:17 +02:00
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);
ExecSetSlotDescriptor(slot, heapDescriptor);
econtext = MakeExprContext(slot, TransactionCommandContext);
}
1996-11-08 01:46:14 +01:00
else
{
tupleTable = NULL;
1996-11-08 01:46:14 +01:00
slot = NULL;
econtext = NULL;
1996-11-08 01:46:14 +01:00
}
#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);
1998-11-27 20:52:36 +01:00
indexTuple->t_tid = heapTuple->t_self;
insertResult = index_insert(indexRelation, datum, nullv,
1998-11-27 20:52:36 +01:00
&(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);
FreeExprContext(econtext);
}
#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);
2000-02-18 10:30:20 +01:00
bool inplace = IsReindexProcessing();
heap_close(heapRelation, NoLock);
index_close(indexRelation);
2000-02-18 10:30:20 +01:00
UpdateStats(hrelid, reltuples, inplace);
UpdateStats(irelid, indtuples, inplace);
if (oldPred != NULL)
{
if (indtuples == reltuples)
predicate = NULL;
2000-02-18 10:30:20 +01:00
if (!inplace)
UpdateIndexPredicate(irelid, oldPred, predicate);
}
}
}
/* ----------------
* index_build
* ----------------
*/
void
index_build(Relation heapRelation,
Relation indexRelation,
int numberOfAttributes,
1998-09-01 05:29:17 +02:00
AttrNumber *attributeNumber,
FuncIndexInfo *funcInfo,
PredInfo *predInfo,
bool unique)
{
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))
OidFunctionCall8(procedure,
PointerGetDatum(heapRelation),
PointerGetDatum(indexRelation),
Int32GetDatum(numberOfAttributes),
PointerGetDatum(attributeNumber),
PointerGetDatum(funcInfo),
PointerGetDatum(predInfo),
BoolGetDatum(unique),
PointerGetDatum(RelationGetIndexStrategy(indexRelation)));
else
DefaultBuild(heapRelation,
indexRelation,
numberOfAttributes,
attributeNumber,
funcInfo,
predInfo,
unique,
RelationGetIndexStrategy(indexRelation));
}
/*
* 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;
1998-09-01 05:29:17 +02:00
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);
}
1998-09-01 05:29:17 +02:00
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
return index->indisunique;
}
2000-02-18 10:30:20 +01:00
/* ---------------------------------
* activate_index -- activate/deactivate the specified index.
* Note that currelntly PostgreSQL doesn't hold the
* status per index
* ---------------------------------
*/
static bool
2000-02-18 10:30:20 +01:00
activate_index(Oid indexId, bool activate)
{
if (!activate) /* Currently does nothing */
2000-02-18 10:30:20 +01:00
return true;
return reindex_index(indexId, false);
}
2000-02-18 10:30:20 +01:00
/* --------------------------------
* reindex_index - This routine is used to recreate an index
* --------------------------------
*/
bool
reindex_index(Oid indexId, bool force)
{
Relation iRel,
indexRelation,
heapRelation;
ScanKeyData entry;
HeapScanDesc scan;
HeapTuple indexTuple,
procTuple,
classTuple;
Form_pg_index index;
Oid heapId,
procId,
accessMethodId;
Node *oldPred = NULL;
PredInfo *predInfo;
AttrNumber *attributeNumberA;
FuncIndexInfo fInfo,
*funcInfo = NULL;
int i,
numberOfAttributes;
bool unique;
char *predString;
2000-02-18 10:30:20 +01:00
bool old;
old = SetReindexProcessing(true);
/* Scan pg_index to find indexes on heapRelation */
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 %d tuple is invalid", indexId);
/*
* For the index, fetch index attributes so we can apply index_build
*/
index = (Form_pg_index) GETSTRUCT(indexTuple);
heapId = index->indrelid;
procId = index->indproc;
unique = index->indisunique;
2000-02-18 10:30:20 +01:00
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
if (index->indkey[i] == InvalidAttrNumber)
break;
}
numberOfAttributes = i;
/* If a valid where predicate, compute predicate Node */
if (VARSIZE(&index->indpred) != 0)
{
predString = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(&index->indpred)));
2000-02-18 10:30:20 +01:00
oldPred = stringToNode(predString);
pfree(predString);
}
predInfo = (PredInfo *) palloc(sizeof(PredInfo));
predInfo->pred = (Node *) oldPred;
predInfo->oldPred = NULL;
2000-02-18 10:30:20 +01:00
/* Assign Index keys to attributes array */
attributeNumberA = (AttrNumber *) palloc(numberOfAttributes * sizeof(AttrNumber));
for (i = 0; i < numberOfAttributes; i++)
attributeNumberA[i] = index->indkey[i];
/* If this is a procedural index, initialize our FuncIndexInfo */
if (procId != InvalidOid)
{
funcInfo = &fInfo;
FIsetnArgs(funcInfo, numberOfAttributes);
procTuple = SearchSysCacheTuple(PROCOID, ObjectIdGetDatum(procId),
0, 0, 0);
if (!HeapTupleIsValid(procTuple))
elog(ERROR, "RelationTruncateIndexes: index procedure not found");
namecpy(&(funcInfo->funcName),
&(((Form_pg_proc) GETSTRUCT(procTuple))->proname));
FIsetProcOid(funcInfo, procTuple->t_data->t_oid);
}
/* Fetch the classTuple associated with this index */
classTuple = SearchSysCacheTupleCopy(RELOID, ObjectIdGetDatum(indexId), 0, 0, 0);
if (!HeapTupleIsValid(classTuple))
elog(ERROR, "RelationTruncateIndexes: index access method not found");
accessMethodId = ((Form_pg_class) GETSTRUCT(classTuple))->relam;
/* Open our index relation */
iRel = index_open(indexId);
if (iRel == NULL)
elog(ERROR, "reindex_index: can't open index relation");
heapRelation = heap_open(heapId, ExclusiveLock);
if (heapRelation == NULL)
elog(ERROR, "reindex_index: can't open heap relation");
/* Obtain exclusive lock on it, just to be sure */
LockRelation(iRel, AccessExclusiveLock);
/*
* Release any buffers associated with this index. If they're dirty,
2000-02-18 10:30:20 +01:00
* they're just dropped without bothering to flush to disk.
*/
ReleaseRelationBuffers(iRel);
/* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, iRel, 0);
iRel->rd_nblocks = 0;
/* Initialize the index and rebuild */
InitIndexStrategy(numberOfAttributes, iRel, accessMethodId);
index_build(heapRelation, iRel, numberOfAttributes,
attributeNumberA, funcInfo, predInfo, unique);
2000-02-18 10:30:20 +01:00
/*
* index_build will close both the heap and index relations (but not
* give up the locks we hold on them). That's fine for the index, but
* we need to open the heap again. We need no new lock, since this
* backend still has the exclusive lock grabbed by heap_truncate.
2000-02-18 10:30:20 +01:00
*/
iRel = index_open(indexId);
Assert(iRel != NULL);
/* Complete the scan and close pg_index */
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
SetReindexProcessing(old);
return true;
}
/*
* ----------------------------
* activate_indexes_of_a_table
2000-02-18 10:30:20 +01:00
* activate/deactivate indexes of the specified table.
* ----------------------------
*/
bool
activate_indexes_of_a_table(Oid relid, bool activate)
{
if (IndexesAreActive(relid, true))
{
if (!activate)
setRelhasindexInplace(relid, false, true);
else
return false;
}
else
{
if (activate)
reindex_relation(relid, false);
else
return false;
}
return true;
}
2000-02-18 10:30:20 +01:00
/* --------------------------------
* reindex_relation - This routine is used to recreate indexes
* of a relation.
* --------------------------------
*/
bool
reindex_relation(Oid relid, bool force)
{
Relation indexRelation;
ScanKeyData entry;
HeapScanDesc scan;
2000-02-18 10:30:20 +01:00
HeapTuple indexTuple;
bool old,
reindexed;
2000-02-18 10:30:20 +01:00
old = SetReindexProcessing(true);
if (IndexesAreActive(relid, true))
{
if (!force)
{
SetReindexProcessing(old);
return false;
}
activate_indexes_of_a_table(relid, false);
}
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid));
2000-02-18 10:30:20 +01:00
scan = heap_beginscan(indexRelation, false, SnapshotNow,
1, &entry);
2000-02-18 10:30:20 +01:00
reindexed = false;
while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0)))
{
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
2000-02-18 10:30:20 +01:00
if (activate_index(index->indexrelid, true))
reindexed = true;
else
{
reindexed = false;
break;
}
}
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
if (reindexed)
setRelhasindexInplace(relid, true, false);
SetReindexProcessing(old);
return reindexed;
2000-02-18 10:30:20 +01:00
}