1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
2000-01-12 06:04:42 +01:00
|
|
|
* indexcmds.c
|
2001-07-17 23:53:01 +02:00
|
|
|
* POSTGRES define and remove index code.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-08-10 20:57:42 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.56 2001/08/10 18:57:34 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-04 00:57:43 +01:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/heapam.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "catalog/catalog.h"
|
2000-02-18 10:30:20 +01:00
|
|
|
#include "catalog/catname.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/heap.h"
|
|
|
|
#include "catalog/index.h"
|
2000-07-15 00:18:02 +02:00
|
|
|
#include "catalog/pg_am.h"
|
2000-04-23 03:44:55 +02:00
|
|
|
#include "catalog/pg_amop.h"
|
|
|
|
#include "catalog/pg_database.h"
|
2000-06-15 05:33:12 +02:00
|
|
|
#include "catalog/pg_index.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
2000-04-25 04:45:54 +02:00
|
|
|
#include "catalog/pg_operator.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "commands/defrem.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "optimizer/clauses.h"
|
1999-08-22 22:15:04 +02:00
|
|
|
#include "optimizer/planmain.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/prep.h"
|
|
|
|
#include "parser/parsetree.h"
|
2000-04-25 04:45:54 +02:00
|
|
|
#include "parser/parse_coerce.h"
|
2000-02-25 03:58:48 +01:00
|
|
|
#include "parser/parse_func.h"
|
2000-04-23 03:44:55 +02:00
|
|
|
#include "parser/parse_type.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/fmgroids.h"
|
2001-07-17 23:53:01 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-07-17 23:53:01 +02:00
|
|
|
|
2000-02-25 03:58:48 +01:00
|
|
|
#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args != NIL)
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* non-export function prototypes */
|
1997-09-08 23:56:23 +02:00
|
|
|
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
|
2000-07-15 00:18:02 +02:00
|
|
|
static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
|
2001-03-22 05:01:46 +01:00
|
|
|
IndexElem *funcIndex,
|
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName, Oid accessMethodId);
|
2000-07-15 00:18:02 +02:00
|
|
|
static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
|
2001-03-22 05:01:46 +01:00
|
|
|
List *attList,
|
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName, Oid accessMethodId);
|
|
|
|
static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
|
|
|
char *accessMethodName, Oid accessMethodId);
|
1997-09-08 04:41:22 +02:00
|
|
|
static char *GetDefaultOpClass(Oid atttypid);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* DefineIndex
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates a new index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* 'attributeList' is a list of IndexElem specifying either a functional
|
1997-09-07 07:04:48 +02:00
|
|
|
* index or a list of attributes to index on.
|
1999-10-02 23:33:33 +02:00
|
|
|
* 'parameterList' is a list of DefElem specified in the with clause.
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'predicate' is the qual specified in the where clause.
|
2000-07-15 00:18:02 +02:00
|
|
|
* 'rangetable' is needed to interpret the predicate
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
DefineIndex(char *heapRelationName,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *indexRelationName,
|
|
|
|
char *accessMethodName,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *attributeList,
|
|
|
|
List *parameterList,
|
1997-09-07 07:04:48 +02:00
|
|
|
bool unique,
|
1999-01-21 23:48:20 +01:00
|
|
|
bool primary,
|
1997-09-08 23:56:23 +02:00
|
|
|
Expr *predicate,
|
|
|
|
List *rangetable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid *classObjectId;
|
|
|
|
Oid accessMethodId;
|
|
|
|
Oid relationId;
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_am accessMethodForm;
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexInfo *indexInfo;
|
1997-09-08 04:41:22 +02:00
|
|
|
int numberOfAttributes;
|
2000-07-15 00:18:02 +02:00
|
|
|
List *cnfPred = NIL;
|
2000-06-18 01:41:51 +02:00
|
|
|
bool lossy = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
List *pl;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-07-15 00:18:02 +02:00
|
|
|
* count attributes in index
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
numberOfAttributes = length(attributeList);
|
|
|
|
if (numberOfAttributes <= 0)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "DefineIndex: must specify at least one attribute");
|
2000-01-12 06:04:42 +01:00
|
|
|
if (numberOfAttributes > INDEX_MAX_KEYS)
|
|
|
|
elog(ERROR, "Cannot use more than %d attributes in an index",
|
|
|
|
INDEX_MAX_KEYS);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* compute heap relation id
|
|
|
|
*/
|
1999-02-02 04:45:56 +01:00
|
|
|
if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(ERROR, "DefineIndex: relation \"%s\" not found",
|
1997-09-07 07:04:48 +02:00
|
|
|
heapRelationName);
|
|
|
|
|
|
|
|
/*
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
* look up the access method, verify it can handle the requested features
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
tuple = SearchSysCache(AMNAME,
|
|
|
|
PointerGetDatum(accessMethodName),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(ERROR, "DefineIndex: access method \"%s\" not found",
|
1997-09-07 07:04:48 +02:00
|
|
|
accessMethodName);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
accessMethodId = tuple->t_data->t_oid;
|
|
|
|
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
if (unique && ! accessMethodForm->amcanunique)
|
|
|
|
elog(ERROR, "DefineIndex: access method \"%s\" does not support UNIQUE indexes",
|
|
|
|
accessMethodName);
|
|
|
|
if (numberOfAttributes > 1 && ! accessMethodForm->amcanmulticol)
|
|
|
|
elog(ERROR, "DefineIndex: access method \"%s\" does not support multi-column indexes",
|
|
|
|
accessMethodName);
|
2000-07-15 00:18:02 +02:00
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
ReleaseSysCache(tuple);
|
2000-07-15 00:18:02 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
|
|
|
|
*/
|
|
|
|
foreach(pl, parameterList)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
DefElem *param = (DefElem *) lfirst(pl);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-02 23:33:33 +02:00
|
|
|
if (!strcasecmp(param->defname, "islossy"))
|
2000-07-15 00:18:02 +02:00
|
|
|
lossy = true;
|
1999-10-02 23:33:33 +02:00
|
|
|
else
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(NOTICE, "Unrecognized index attribute \"%s\" ignored",
|
1999-10-02 23:33:33 +02:00
|
|
|
param->defname);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-07-16 07:07:00 +02:00
|
|
|
* Convert the partial-index predicate from parsetree form to
|
|
|
|
* an implicit-AND qual expression, for easier evaluation at runtime.
|
2001-08-06 20:09:45 +02:00
|
|
|
* While we are at it, we reduce it to a canonical (CNF or DNF) form
|
|
|
|
* to simplify the task of proving implications.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (predicate != NULL && rangetable != NIL)
|
|
|
|
{
|
2001-08-06 20:09:45 +02:00
|
|
|
cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
|
1999-08-22 22:15:04 +02:00
|
|
|
fix_opids((Node *) cnfPred);
|
1997-09-07 07:04:48 +02:00
|
|
|
CheckPredicate(cnfPred, rangetable, relationId);
|
|
|
|
}
|
|
|
|
|
2000-02-18 10:30:20 +01:00
|
|
|
if (!IsBootstrapProcessingMode() && !IndexesAreActive(relationId, false))
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(ERROR, "Existing indexes are inactive. REINDEX first");
|
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Prepare arguments for index_create, primarily an IndexInfo
|
|
|
|
* structure
|
2000-07-15 00:18:02 +02:00
|
|
|
*/
|
|
|
|
indexInfo = makeNode(IndexInfo);
|
2001-07-16 07:07:00 +02:00
|
|
|
indexInfo->ii_Predicate = cnfPred;
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo->ii_FuncOid = InvalidOid;
|
|
|
|
indexInfo->ii_Unique = unique;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (IsFuncIndex(attributeList))
|
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexElem *funcIndex = (IndexElem *) lfirst(attributeList);
|
1997-09-08 04:41:22 +02:00
|
|
|
int nargs;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/* Parser should have given us only one list item, but check */
|
|
|
|
if (numberOfAttributes != 1)
|
|
|
|
elog(ERROR, "Functional index can only have one attribute");
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
nargs = length(funcIndex->args);
|
|
|
|
if (nargs > INDEX_MAX_KEYS)
|
2000-01-12 06:04:42 +01:00
|
|
|
elog(ERROR, "Index function can take at most %d arguments",
|
|
|
|
INDEX_MAX_KEYS);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo->ii_NumIndexAttrs = 1;
|
|
|
|
indexInfo->ii_NumKeyAttrs = nargs;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-02-25 03:58:48 +01:00
|
|
|
classObjectId = (Oid *) palloc(sizeof(Oid));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
FuncIndexArgs(indexInfo, classObjectId, funcIndex,
|
|
|
|
relationId, accessMethodName, accessMethodId);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
|
|
|
indexInfo->ii_NumKeyAttrs = numberOfAttributes;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-02-25 03:58:48 +01:00
|
|
|
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
NormIndexAttrs(indexInfo, classObjectId, attributeList,
|
|
|
|
relationId, accessMethodName, accessMethodId);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
index_create(heapRelationName, indexRelationName,
|
|
|
|
indexInfo, accessMethodId, classObjectId,
|
|
|
|
lossy, primary, allowSystemTableMods);
|
|
|
|
|
2000-06-17 23:49:04 +02:00
|
|
|
/*
|
|
|
|
* We update the relation's pg_class tuple even if it already has
|
2001-03-22 05:01:46 +01:00
|
|
|
* relhasindex = true. This is needed to cause a shared-cache-inval
|
2000-06-17 23:49:04 +02:00
|
|
|
* message to be sent for the pg_class tuple, which will cause other
|
|
|
|
* backends to flush their relcache entries and in particular their
|
|
|
|
* cached lists of the indexes for this relation.
|
|
|
|
*/
|
2001-08-10 20:57:42 +02:00
|
|
|
setRelhasindex(relationId, true, primary, InvalidOid);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CheckPredicate
|
1997-09-07 07:04:48 +02:00
|
|
|
* Checks that the given list of partial-index predicates refer
|
2001-07-16 07:07:00 +02:00
|
|
|
* (via the given range table) only to the given base relation oid.
|
|
|
|
*
|
|
|
|
* This used to also constrain the form of the predicate to forms that
|
|
|
|
* indxpath.c could do something with. However, that seems overly
|
|
|
|
* restrictive. One useful application of partial indexes is to apply
|
|
|
|
* a UNIQUE constraint across a subset of a table, and in that scenario
|
|
|
|
* any evaluatable predicate will work. So accept any predicate here
|
|
|
|
* (except ones requiring a plan), and let indxpath.c fend for itself.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-07-16 07:07:00 +02:00
|
|
|
if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR,
|
2001-07-16 07:07:00 +02:00
|
|
|
"Partial-index predicates may refer only to the base relation");
|
|
|
|
|
2001-07-17 23:53:01 +02:00
|
|
|
/*
|
|
|
|
* We don't currently support generation of an actual query plan for a
|
|
|
|
* predicate, only simple scalar expressions; hence these restrictions.
|
|
|
|
*/
|
2001-07-16 07:07:00 +02:00
|
|
|
if (contain_subplans((Node *) predList))
|
|
|
|
elog(ERROR, "Cannot use subselect in index predicate");
|
|
|
|
if (contain_agg_clause((Node *) predList))
|
|
|
|
elog(ERROR, "Cannot use aggregate in index predicate");
|
2001-07-17 23:53:01 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A predicate using noncachable functions is probably wrong, for the
|
|
|
|
* same reasons that we don't allow a functional index to use one.
|
|
|
|
*/
|
|
|
|
if (contain_noncachable_functions((Node *) predList))
|
|
|
|
elog(ERROR, "Cannot use non-cachable function in index predicate");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
static void
|
2000-07-15 00:18:02 +02:00
|
|
|
FuncIndexArgs(IndexInfo *indexInfo,
|
|
|
|
Oid *classOidP,
|
|
|
|
IndexElem *funcIndex,
|
2000-04-23 03:44:55 +02:00
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName,
|
|
|
|
Oid accessMethodId)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
Oid argTypes[FUNC_MAX_ARGS];
|
|
|
|
List *arglist;
|
2000-08-20 02:44:19 +02:00
|
|
|
int nargs = 0;
|
|
|
|
int i;
|
|
|
|
Oid funcid;
|
|
|
|
Oid rettype;
|
|
|
|
bool retset;
|
|
|
|
Oid *true_typeids;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-02-25 03:58:48 +01:00
|
|
|
* process the function arguments, which are a list of T_String
|
|
|
|
* (someday ought to allow more general expressions?)
|
2000-07-15 00:18:02 +02:00
|
|
|
*
|
|
|
|
* Note caller already checked that list is not too long.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-07-15 00:18:02 +02:00
|
|
|
MemSet(argTypes, 0, sizeof(argTypes));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
foreach(arglist, funcIndex->args)
|
2000-02-25 03:58:48 +01:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
char *arg = strVal(lfirst(arglist));
|
2000-08-20 02:44:19 +02:00
|
|
|
HeapTuple tuple;
|
2000-02-25 03:58:48 +01:00
|
|
|
Form_pg_attribute att;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(ATTNAME,
|
|
|
|
ObjectIdGetDatum(relId),
|
|
|
|
PointerGetDatum(arg),
|
|
|
|
0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-02-25 03:58:48 +01:00
|
|
|
elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg);
|
1998-09-01 05:29:17 +02:00
|
|
|
att = (Form_pg_attribute) GETSTRUCT(tuple);
|
2000-08-20 02:44:19 +02:00
|
|
|
indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
|
|
|
|
argTypes[nargs] = att->atttypid;
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2000-08-20 02:44:19 +02:00
|
|
|
nargs++;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-02-25 03:58:48 +01:00
|
|
|
* Lookup the function procedure to get its OID and result type.
|
2000-07-15 00:18:02 +02:00
|
|
|
*
|
2001-03-22 07:16:21 +01:00
|
|
|
* We rely on parse_func.c to find the correct function in the possible
|
|
|
|
* presence of binary-compatible types. However, parse_func may do
|
|
|
|
* too much: it will accept a function that requires run-time coercion
|
|
|
|
* of input types, and the executor is not currently set up to support
|
|
|
|
* that. So, check to make sure that the selected function has
|
|
|
|
* exact-match or binary-compatible input types.
|
2000-02-25 03:58:48 +01:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!func_get_detail(funcIndex->name, nargs, argTypes,
|
|
|
|
&funcid, &rettype, &retset, &true_typeids))
|
2000-08-20 02:44:19 +02:00
|
|
|
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
|
|
|
|
|
|
|
|
if (retset)
|
|
|
|
elog(ERROR, "DefineIndex: cannot index on a function returning a set");
|
|
|
|
|
|
|
|
for (i = 0; i < nargs; i++)
|
2000-02-25 03:58:48 +01:00
|
|
|
{
|
2000-08-20 02:44:19 +02:00
|
|
|
if (argTypes[i] != true_typeids[i] &&
|
2001-03-22 05:01:46 +01:00
|
|
|
!IS_BINARY_COMPATIBLE(argTypes[i], true_typeids[i]))
|
2000-08-20 02:44:19 +02:00
|
|
|
func_error("DefineIndex", funcIndex->name, nargs, argTypes,
|
|
|
|
"Index function must be binary-compatible with table datatype");
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
|
2001-07-17 23:53:01 +02:00
|
|
|
/*
|
|
|
|
* Require that the function be marked cachable. Using a noncachable
|
|
|
|
* function for a functional index is highly questionable, since if you
|
|
|
|
* aren't going to get the same result for the same data every time,
|
|
|
|
* it's not clear what the index entries mean at all.
|
|
|
|
*/
|
|
|
|
if (!func_iscachable(funcid))
|
|
|
|
elog(ERROR, "DefineIndex: index function must be marked iscachable");
|
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/* Process opclass, using func return type as default type */
|
|
|
|
|
2000-08-20 02:44:19 +02:00
|
|
|
classOidP[0] = GetAttrOpClass(funcIndex, rettype,
|
2000-07-15 00:18:02 +02:00
|
|
|
accessMethodName, accessMethodId);
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2000-08-20 02:44:19 +02:00
|
|
|
/* OK, return results */
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2000-08-20 02:44:19 +02:00
|
|
|
indexInfo->ii_FuncOid = funcid;
|
|
|
|
/* Need to do the fmgr function lookup now, too */
|
2001-03-22 05:01:46 +01:00
|
|
|
fmgr_info(funcid, &indexInfo->ii_FuncInfo);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
static void
|
2000-07-15 00:18:02 +02:00
|
|
|
NormIndexAttrs(IndexInfo *indexInfo,
|
1998-08-26 07:22:58 +02:00
|
|
|
Oid *classOidP,
|
2000-07-15 00:18:02 +02:00
|
|
|
List *attList, /* list of IndexElem's */
|
2000-04-23 03:44:55 +02:00
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName,
|
|
|
|
Oid accessMethodId)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *rest;
|
2000-07-15 00:18:02 +02:00
|
|
|
int attn = 0;
|
1996-08-15 09:42:52 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* process attributeList
|
|
|
|
*/
|
2000-02-25 03:58:48 +01:00
|
|
|
foreach(rest, attList)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexElem *attribute = (IndexElem *) lfirst(rest);
|
2000-02-25 03:58:48 +01:00
|
|
|
HeapTuple atttuple;
|
1998-09-01 05:29:17 +02:00
|
|
|
Form_pg_attribute attform;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (attribute->name == NULL)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "missing attribute for define index");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
atttuple = SearchSysCache(ATTNAME,
|
|
|
|
ObjectIdGetDatum(relId),
|
|
|
|
PointerGetDatum(attribute->name),
|
|
|
|
0, 0);
|
1998-08-26 18:43:54 +02:00
|
|
|
if (!HeapTupleIsValid(atttuple))
|
2000-02-25 03:58:48 +01:00
|
|
|
elog(ERROR, "DefineIndex: attribute \"%s\" not found",
|
1997-09-07 07:04:48 +02:00
|
|
|
attribute->name);
|
1998-09-01 05:29:17 +02:00
|
|
|
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
|
2001-03-22 05:01:46 +01:00
|
|
|
accessMethodName, accessMethodId);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(atttuple);
|
2000-07-15 00:18:02 +02:00
|
|
|
attn++;
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Oid
|
2000-04-23 03:44:55 +02:00
|
|
|
GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
|
|
|
char *accessMethodName, Oid accessMethodId)
|
2000-02-25 03:58:48 +01:00
|
|
|
{
|
2000-04-23 03:44:55 +02:00
|
|
|
Relation relation;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
ScanKeyData entry[2];
|
2000-02-25 03:58:48 +01:00
|
|
|
HeapTuple tuple;
|
2000-04-25 04:45:54 +02:00
|
|
|
Oid opClassId,
|
|
|
|
oprId;
|
|
|
|
bool doTypeCheck = true;
|
2000-02-25 03:58:48 +01:00
|
|
|
|
|
|
|
if (attribute->class == NULL)
|
|
|
|
{
|
|
|
|
/* no operator class specified, so find the default */
|
|
|
|
attribute->class = GetDefaultOpClass(attrType);
|
|
|
|
if (attribute->class == NULL)
|
2001-08-09 20:28:18 +02:00
|
|
|
elog(ERROR, "data type %s has no default operator class"
|
|
|
|
"\n\tYou must specify an operator class for the index or define a"
|
|
|
|
"\n\tdefault operator class for the data type",
|
|
|
|
format_type_be(attrType));
|
2000-04-25 04:45:54 +02:00
|
|
|
/* assume we need not check type compatibility */
|
|
|
|
doTypeCheck = false;
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
opClassId = GetSysCacheOid(CLANAME,
|
|
|
|
PointerGetDatum(attribute->class),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!OidIsValid(opClassId))
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(ERROR, "DefineIndex: opclass \"%s\" not found",
|
2000-02-25 03:58:48 +01:00
|
|
|
attribute->class);
|
2000-04-23 03:44:55 +02:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Assume the opclass is supported by this index access method if we
|
|
|
|
* can find at least one relevant entry in pg_amop.
|
2000-04-23 03:44:55 +02:00
|
|
|
*/
|
|
|
|
ScanKeyEntryInitialize(&entry[0], 0,
|
|
|
|
Anum_pg_amop_amopid,
|
|
|
|
F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(accessMethodId));
|
|
|
|
ScanKeyEntryInitialize(&entry[1], 0,
|
|
|
|
Anum_pg_amop_amopclaid,
|
|
|
|
F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(opClassId));
|
|
|
|
|
|
|
|
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
|
|
|
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
2000-04-23 03:44:55 +02:00
|
|
|
elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
|
|
|
|
attribute->class, accessMethodName);
|
|
|
|
|
2000-04-25 04:45:54 +02:00
|
|
|
oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
|
|
|
|
|
2000-04-23 03:44:55 +02:00
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(relation, AccessShareLock);
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2000-04-25 04:45:54 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Make sure the operators associated with this opclass actually
|
|
|
|
* accept the column data type. This prevents possible coredumps
|
|
|
|
* caused by user errors like applying text_ops to an int4 column. We
|
|
|
|
* will accept an opclass as OK if the operator's input datatype is
|
|
|
|
* binary-compatible with the actual column datatype. Note we assume
|
|
|
|
* that all the operators associated with an opclass accept the same
|
|
|
|
* datatypes, so checking the first one we happened to find in the
|
|
|
|
* table is sufficient.
|
2000-04-25 04:45:54 +02:00
|
|
|
*
|
|
|
|
* If the opclass was the default for the datatype, assume we can skip
|
2001-03-22 05:01:46 +01:00
|
|
|
* this check --- that saves a few cycles in the most common case. If
|
|
|
|
* pg_opclass is wrong then we're probably screwed anyway...
|
2000-04-25 04:45:54 +02:00
|
|
|
*/
|
|
|
|
if (doTypeCheck)
|
|
|
|
{
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(OPEROID,
|
|
|
|
ObjectIdGetDatum(oprId),
|
|
|
|
0, 0, 0);
|
2000-04-25 04:45:54 +02:00
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
{
|
|
|
|
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
|
2001-03-22 05:01:46 +01:00
|
|
|
Oid opInputType = (optup->oprkind == 'l') ?
|
|
|
|
optup->oprright : optup->oprleft;
|
2000-04-25 04:45:54 +02:00
|
|
|
|
|
|
|
if (attrType != opInputType &&
|
2001-03-22 05:01:46 +01:00
|
|
|
!IS_BINARY_COMPATIBLE(attrType, opInputType))
|
2001-08-09 20:28:18 +02:00
|
|
|
elog(ERROR, "operator class \"%s\" does not accept data type %s",
|
|
|
|
attribute->class, format_type_be(attrType));
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2000-04-25 04:45:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-04-23 03:44:55 +02:00
|
|
|
return opClassId;
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static char *
|
1996-08-15 09:42:52 +02:00
|
|
|
GetDefaultOpClass(Oid atttypid)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
2000-11-16 23:30:52 +01:00
|
|
|
char *result;
|
1996-08-15 09:42:52 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(CLADEFTYPE,
|
|
|
|
ObjectIdGetDatum(atttypid),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-04-23 03:44:55 +02:00
|
|
|
return NULL;
|
1996-08-15 09:42:52 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
result = pstrdup(NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
return result;
|
1996-08-15 09:42:52 +02:00
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* RemoveIndex
|
1997-09-07 07:04:48 +02:00
|
|
|
* Deletes an index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Exceptions:
|
1997-09-07 07:04:48 +02:00
|
|
|
* BadArg if name is invalid.
|
2000-12-15 05:08:15 +01:00
|
|
|
* "ERROR" if index nonexistent.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ...
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveIndex(char *name)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(RELNAME,
|
|
|
|
PointerGetDatum(name),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(ERROR, "index \"%s\" does not exist", name);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "relation \"%s\" is of type \"%c\"",
|
2000-11-16 23:30:52 +01:00
|
|
|
name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-12-10 04:56:14 +01:00
|
|
|
index_drop(tuple->t_data->t_oid);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reindex
|
|
|
|
* Recreate an index.
|
|
|
|
*
|
|
|
|
* Exceptions:
|
|
|
|
* "ERROR" if index nonexistent.
|
|
|
|
* ...
|
|
|
|
*/
|
|
|
|
void
|
2000-04-12 19:17:23 +02:00
|
|
|
ReindexIndex(const char *name, bool force /* currently unused */ )
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2001-02-23 10:26:14 +01:00
|
|
|
bool overwrite = false;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* REINDEX within a transaction block is dangerous, because if the
|
|
|
|
* transaction is later rolled back we have no way to undo truncation
|
|
|
|
* of the index's physical file. Disallow it.
|
2000-11-08 23:10:03 +01:00
|
|
|
*/
|
|
|
|
if (IsTransactionBlock())
|
|
|
|
elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(RELNAME,
|
|
|
|
PointerGetDatum(name),
|
|
|
|
0, 0, 0);
|
2000-02-18 10:30:20 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(ERROR, "index \"%s\" does not exist", name);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
|
|
|
|
elog(ERROR, "relation \"%s\" is of type \"%c\"",
|
2000-11-16 23:30:52 +01:00
|
|
|
name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2001-02-23 10:26:14 +01:00
|
|
|
if (IsIgnoringSystemIndexes())
|
|
|
|
overwrite = true;
|
|
|
|
if (!reindex_index(tuple->t_data->t_oid, force, overwrite))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(NOTICE, "index \"%s\" wasn't reindexed", name);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexTable
|
|
|
|
* Recreate indexes of a table.
|
|
|
|
*
|
|
|
|
* Exceptions:
|
|
|
|
* "ERROR" if table nonexistent.
|
|
|
|
* ...
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ReindexTable(const char *name, bool force)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* REINDEX within a transaction block is dangerous, because if the
|
|
|
|
* transaction is later rolled back we have no way to undo truncation
|
|
|
|
* of the index's physical file. Disallow it.
|
2000-11-08 23:10:03 +01:00
|
|
|
*/
|
|
|
|
if (IsTransactionBlock())
|
|
|
|
elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(RELNAME,
|
|
|
|
PointerGetDatum(name),
|
|
|
|
0, 0, 0);
|
2000-02-18 10:30:20 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(ERROR, "table \"%s\" does not exist", name);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
|
|
|
|
elog(ERROR, "relation \"%s\" is of type \"%c\"",
|
2000-11-16 23:30:52 +01:00
|
|
|
name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2000-04-25 12:38:38 +02:00
|
|
|
if (!reindex_relation(tuple->t_data->t_oid, force))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(NOTICE, "table \"%s\" wasn't reindexed", name);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexDatabase
|
|
|
|
* Recreate indexes of a database.
|
|
|
|
*
|
|
|
|
* Exceptions:
|
|
|
|
* "ERROR" if table nonexistent.
|
|
|
|
* ...
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ReindexDatabase(const char *dbname, bool force, bool all)
|
|
|
|
{
|
2001-06-13 23:44:41 +02:00
|
|
|
Relation relationRelation;
|
2000-04-12 19:17:23 +02:00
|
|
|
HeapScanDesc scan;
|
2001-06-13 23:44:41 +02:00
|
|
|
HeapTuple tuple;
|
2000-06-28 05:33:33 +02:00
|
|
|
MemoryContext private_context;
|
2000-04-12 19:17:23 +02:00
|
|
|
MemoryContext old;
|
|
|
|
int relcnt,
|
|
|
|
relalc,
|
|
|
|
i,
|
|
|
|
oncealc = 200;
|
|
|
|
Oid *relids = (Oid *) NULL;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
AssertArg(dbname);
|
|
|
|
|
2001-06-13 23:44:41 +02:00
|
|
|
if (strcmp(dbname, DatabaseName) != 0)
|
|
|
|
elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
|
2000-06-28 05:33:33 +02:00
|
|
|
|
2001-06-13 23:44:41 +02:00
|
|
|
if (! (superuser() || is_dbadmin(MyDatabaseId)))
|
2000-02-18 10:30:20 +01:00
|
|
|
elog(ERROR, "REINDEX DATABASE: Permission denied.");
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We cannot run inside a user transaction block; if we were inside a
|
|
|
|
* transaction, then our commit- and start-transaction-command calls
|
|
|
|
* would not have the intended effect!
|
2000-06-28 05:33:33 +02:00
|
|
|
*/
|
|
|
|
if (IsTransactionBlock())
|
|
|
|
elog(ERROR, "REINDEX DATABASE cannot run inside a BEGIN/END block");
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Create a memory context that will survive forced transaction
|
|
|
|
* commits we do below. Since it is a child of QueryContext, it will
|
|
|
|
* go away eventually even if we suffer an error; there's no need for
|
|
|
|
* special abort cleanup logic.
|
2000-06-28 05:33:33 +02:00
|
|
|
*/
|
|
|
|
private_context = AllocSetContextCreate(QueryContext,
|
|
|
|
"ReindexDatabase",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
relationRelation = heap_openr(RelationRelationName, AccessShareLock);
|
|
|
|
scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
|
|
|
|
relcnt = relalc = 0;
|
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
|
|
|
{
|
|
|
|
if (!all)
|
|
|
|
{
|
|
|
|
if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
|
|
|
|
continue;
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
|
|
|
|
{
|
2000-06-28 05:33:33 +02:00
|
|
|
old = MemoryContextSwitchTo(private_context);
|
2000-02-18 10:30:20 +01:00
|
|
|
if (relcnt == 0)
|
|
|
|
{
|
|
|
|
relalc = oncealc;
|
|
|
|
relids = palloc(sizeof(Oid) * relalc);
|
|
|
|
}
|
|
|
|
else if (relcnt >= relalc)
|
|
|
|
{
|
|
|
|
relalc *= 2;
|
|
|
|
relids = repalloc(relids, sizeof(Oid) * relalc);
|
|
|
|
}
|
|
|
|
MemoryContextSwitchTo(old);
|
|
|
|
relids[relcnt] = tuple->t_data->t_oid;
|
|
|
|
relcnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(relationRelation, AccessShareLock);
|
|
|
|
|
|
|
|
CommitTransactionCommand();
|
|
|
|
for (i = 0; i < relcnt; i++)
|
|
|
|
{
|
|
|
|
StartTransactionCommand();
|
2000-04-25 12:38:38 +02:00
|
|
|
if (reindex_relation(relids[i], force))
|
2000-10-23 01:32:48 +02:00
|
|
|
elog(NOTICE, "relation %u was reindexed", relids[i]);
|
2000-02-18 10:30:20 +01:00
|
|
|
CommitTransactionCommand();
|
|
|
|
}
|
|
|
|
StartTransactionCommand();
|
2000-06-28 05:33:33 +02:00
|
|
|
|
|
|
|
MemoryContextDelete(private_context);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|