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
|
|
|
*
|
2002-06-20 22:29:54 +02:00
|
|
|
* Portions Copyright (c) 1996-2002, 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
|
2003-07-20 23:56:35 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.102 2003/07/20 21:56:32 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/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"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "catalog/dependency.h"
|
2003-01-02 20:29:22 +01:00
|
|
|
#include "catalog/heap.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/index.h"
|
2002-03-26 20:17:02 +01:00
|
|
|
#include "catalog/namespace.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
2002-04-05 02:31:36 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
2003-06-27 16:45:32 +02:00
|
|
|
#include "commands/dbcommands.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "commands/defrem.h"
|
2003-01-02 20:29:22 +01:00
|
|
|
#include "commands/tablecmds.h"
|
2002-12-13 20:46:01 +01:00
|
|
|
#include "executor/executor.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-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"
|
2003-05-28 18:04:02 +02:00
|
|
|
#include "parser/parse_expr.h"
|
2000-02-25 03:58:48 +01:00
|
|
|
#include "parser/parse_func.h"
|
2002-04-27 05:45:03 +02:00
|
|
|
#include "utils/acl.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* non-export function prototypes */
|
2003-05-28 18:04:02 +02:00
|
|
|
static void CheckPredicate(List *predList);
|
|
|
|
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
|
2001-03-22 05:01:46 +01:00
|
|
|
List *attList,
|
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName, Oid accessMethodId);
|
2003-05-28 18:04:02 +02:00
|
|
|
static Oid GetIndexOpClass(List *opclass, Oid attrType,
|
2001-03-22 05:01:46 +01:00
|
|
|
char *accessMethodName, Oid accessMethodId);
|
2001-10-25 07:50:21 +02:00
|
|
|
static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
|
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
|
|
|
*
|
2003-05-28 18:04:02 +02:00
|
|
|
* 'attributeList' is a list of IndexElem specifying columns and expressions
|
|
|
|
* to index on.
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'predicate' is the qual specified in the where clause.
|
2001-08-21 18:36:06 +02:00
|
|
|
* 'rangetable' is needed to interpret the predicate.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2002-03-26 20:17:02 +01:00
|
|
|
DefineIndex(RangeVar *heapRelation,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *indexRelationName,
|
|
|
|
char *accessMethodName,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *attributeList,
|
1997-09-07 07:04:48 +02:00
|
|
|
bool unique,
|
1999-01-21 23:48:20 +01:00
|
|
|
bool primary,
|
2002-07-12 20:43:19 +02:00
|
|
|
bool isconstraint,
|
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;
|
2002-04-27 05:45:03 +02:00
|
|
|
Oid namespaceId;
|
2002-01-04 00:21:32 +01:00
|
|
|
Relation rel;
|
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;
|
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)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("must specify at least one attribute")));
|
2000-01-12 06:04:42 +01:00
|
|
|
if (numberOfAttributes > INDEX_MAX_KEYS)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_TOO_MANY_COLUMNS),
|
|
|
|
errmsg("cannot use more than %d attributes in an index",
|
|
|
|
INDEX_MAX_KEYS)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2002-01-04 00:21:32 +01:00
|
|
|
* Open heap relation, acquire a suitable lock on it, remember its OID
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-03-26 20:17:02 +01:00
|
|
|
rel = heap_openrv(heapRelation, ShareLock);
|
2002-01-04 00:21:32 +01:00
|
|
|
|
|
|
|
/* Note: during bootstrap may see uncataloged relation */
|
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION &&
|
|
|
|
rel->rd_rel->relkind != RELKIND_UNCATALOGED)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("relation \"%s\" is not a table",
|
|
|
|
heapRelation->relname)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-01-04 00:21:32 +01:00
|
|
|
relationId = RelationGetRelid(rel);
|
2002-04-27 05:45:03 +02:00
|
|
|
namespaceId = RelationGetNamespace(rel);
|
2002-01-04 00:21:32 +01:00
|
|
|
|
|
|
|
if (!IsBootstrapProcessingMode() &&
|
2002-04-12 22:38:31 +02:00
|
|
|
IsSystemRelation(rel) &&
|
2002-09-23 02:42:48 +02:00
|
|
|
!IndexesAreActive(rel))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INDEXES_DEACTIVATED),
|
|
|
|
errmsg("existing indexes are inactive"),
|
|
|
|
errhint("REINDEX the table first.")));
|
2002-01-04 00:21:32 +01:00
|
|
|
|
2002-04-12 22:38:31 +02:00
|
|
|
heap_close(rel, NoLock);
|
|
|
|
|
2002-04-27 05:45:03 +02:00
|
|
|
/*
|
|
|
|
* Verify we (still) have CREATE rights in the rel's namespace.
|
2002-09-04 22:31:48 +02:00
|
|
|
* (Presumably we did when the rel was created, but maybe not
|
|
|
|
* anymore.) Skip check if bootstrapping, since permissions machinery
|
|
|
|
* may not be working yet.
|
2002-04-27 05:45:03 +02:00
|
|
|
*/
|
2002-08-07 23:45:02 +02:00
|
|
|
if (!IsBootstrapProcessingMode())
|
2002-04-27 05:45:03 +02:00
|
|
|
{
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
|
|
|
|
ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, get_namespace_name(namespaceId));
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +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))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("access method \"%s\" does not exist",
|
|
|
|
accessMethodName)));
|
2002-07-20 07:16:59 +02:00
|
|
|
accessMethodId = HeapTupleGetOid(tuple);
|
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
|
|
|
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
if (unique && !accessMethodForm->amcanunique)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("access method \"%s\" does not support UNIQUE indexes",
|
|
|
|
accessMethodName)));
|
2001-10-25 07:50:21 +02:00
|
|
|
if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("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
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
/*
|
|
|
|
* If a range table was created then check that only the base rel is
|
|
|
|
* mentioned.
|
|
|
|
*/
|
|
|
|
if (rangetable != NIL)
|
|
|
|
{
|
|
|
|
if (length(rangetable) != 1 || getrelid(1, rangetable) != relationId)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
|
|
|
errmsg("index expressions and predicates may refer only to the base relation")));
|
2003-05-28 18:04:02 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +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
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
if (predicate)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-08-06 20:09:45 +02:00
|
|
|
cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
|
2003-05-28 18:04:02 +02:00
|
|
|
CheckPredicate(cnfPred);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2003-01-02 20:29:22 +01:00
|
|
|
/*
|
|
|
|
* Check that all of the attributes in a primary key are marked
|
|
|
|
* as not null, otherwise attempt to ALTER TABLE .. SET NOT NULL
|
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
if (primary)
|
2003-01-02 20:29:22 +01:00
|
|
|
{
|
|
|
|
List *keys;
|
|
|
|
|
|
|
|
foreach(keys, attributeList)
|
|
|
|
{
|
|
|
|
IndexElem *key = (IndexElem *) lfirst(keys);
|
|
|
|
HeapTuple atttuple;
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (!key->name)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("primary keys cannot be expressions")));
|
2003-05-28 18:04:02 +02:00
|
|
|
|
2003-01-02 20:29:22 +01:00
|
|
|
/* System attributes are never null, so no problem */
|
|
|
|
if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
atttuple = SearchSysCacheAttName(relationId, key->name);
|
|
|
|
if (HeapTupleIsValid(atttuple))
|
|
|
|
{
|
|
|
|
if (! ((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Try to make it NOT NULL.
|
|
|
|
*
|
|
|
|
* XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade
|
|
|
|
* to child tables? Currently, since the PRIMARY KEY
|
|
|
|
* itself doesn't cascade, we don't cascade the notnull
|
|
|
|
* constraint either; but this is pretty debatable.
|
|
|
|
*/
|
|
|
|
AlterTableAlterColumnSetNotNull(relationId, false,
|
|
|
|
key->name);
|
|
|
|
}
|
|
|
|
ReleaseSysCache(atttuple);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This shouldn't happen if parser did its job ... */
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("column \"%s\" named in key does not exist",
|
|
|
|
key->name)));
|
2003-01-02 20:29:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
2003-05-28 18:04:02 +02:00
|
|
|
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
|
|
|
indexInfo->ii_Expressions = NIL; /* for now */
|
|
|
|
indexInfo->ii_ExpressionsState = NIL;
|
2001-07-16 07:07:00 +02:00
|
|
|
indexInfo->ii_Predicate = cnfPred;
|
2002-12-15 17:17:59 +01:00
|
|
|
indexInfo->ii_PredicateState = NIL;
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo->ii_Unique = unique;
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
|
|
|
ComputeIndexAttrs(indexInfo, classObjectId, attributeList,
|
2000-07-15 00:18:02 +02:00
|
|
|
relationId, accessMethodName, accessMethodId);
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
index_create(relationId, indexRelationName,
|
2000-07-15 00:18:02 +02:00
|
|
|
indexInfo, accessMethodId, classObjectId,
|
2002-07-12 20:43:19 +02:00
|
|
|
primary, isconstraint, allowSystemTableMods);
|
2000-07-15 00:18:02 +02:00
|
|
|
|
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
|
2003-05-28 18:04:02 +02:00
|
|
|
* Checks that the given list of partial-index predicates is valid.
|
2001-07-16 07:07:00 +02:00
|
|
|
*
|
|
|
|
* This used to also constrain the form of the predicate to forms that
|
2001-10-25 07:50:21 +02:00
|
|
|
* indxpath.c could do something with. However, that seems overly
|
2001-07-16 07:07:00 +02:00
|
|
|
* 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
|
2003-05-28 18:04:02 +02:00
|
|
|
CheckPredicate(List *predList)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-07-17 23:53:01 +02:00
|
|
|
/*
|
|
|
|
* We don't currently support generation of an actual query plan for a
|
2001-10-25 07:50:21 +02:00
|
|
|
* predicate, only simple scalar expressions; hence these
|
|
|
|
* restrictions.
|
2001-07-17 23:53:01 +02:00
|
|
|
*/
|
2001-07-16 07:07:00 +02:00
|
|
|
if (contain_subplans((Node *) predList))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot use sub-select in index predicate")));
|
2001-07-16 07:07:00 +02:00
|
|
|
if (contain_agg_clause((Node *) predList))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_GROUPING_ERROR),
|
|
|
|
errmsg("cannot use aggregate in index predicate")));
|
2001-07-17 23:53:01 +02:00
|
|
|
|
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* A predicate using mutable functions is probably wrong, for the same
|
2003-05-28 18:04:02 +02:00
|
|
|
* reasons that we don't allow an index expression to use one.
|
2001-07-17 23:53:01 +02:00
|
|
|
*/
|
2002-04-05 02:31:36 +02:00
|
|
|
if (contain_mutable_functions((Node *) predList))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("functions in index predicate must be marked IMMUTABLE")));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
static void
|
2003-05-28 18:04:02 +02:00
|
|
|
ComputeIndexAttrs(IndexInfo *indexInfo,
|
|
|
|
Oid *classOidP,
|
|
|
|
List *attList, /* list of IndexElem's */
|
|
|
|
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);
|
2003-05-28 18:04:02 +02:00
|
|
|
Oid atttype;
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (attribute->name != NULL)
|
|
|
|
{
|
|
|
|
/* Simple index attribute */
|
|
|
|
HeapTuple atttuple;
|
|
|
|
Form_pg_attribute attform;
|
|
|
|
|
|
|
|
Assert(attribute->expr == NULL);
|
|
|
|
atttuple = SearchSysCacheAttName(relId, attribute->name);
|
|
|
|
if (!HeapTupleIsValid(atttuple))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("attribute \"%s\" does not exist",
|
|
|
|
attribute->name)));
|
2003-05-28 18:04:02 +02:00
|
|
|
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
|
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
|
|
|
|
atttype = attform->atttypid;
|
|
|
|
ReleaseSysCache(atttuple);
|
|
|
|
}
|
|
|
|
else if (attribute->expr && IsA(attribute->expr, Var))
|
|
|
|
{
|
|
|
|
/* Tricky tricky, he wrote (column) ... treat as simple attr */
|
|
|
|
Var *var = (Var *) attribute->expr;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
|
|
|
|
atttype = get_atttype(relId, var->varattno);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Index expression */
|
|
|
|
Assert(attribute->expr != NULL);
|
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
|
|
|
|
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
|
|
|
|
attribute->expr);
|
|
|
|
atttype = exprType(attribute->expr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't currently support generation of an actual query plan
|
|
|
|
* for an index expression, only simple scalar expressions;
|
|
|
|
* hence these restrictions.
|
|
|
|
*/
|
|
|
|
if (contain_subplans(attribute->expr))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot use sub-select in index expression")));
|
2003-05-28 18:04:02 +02:00
|
|
|
if (contain_agg_clause(attribute->expr))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_GROUPING_ERROR),
|
|
|
|
errmsg("cannot use aggregate in index expression")));
|
2003-05-28 18:04:02 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A expression using mutable functions is probably wrong,
|
|
|
|
* 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 (contain_mutable_functions(attribute->expr))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("functions in index expression must be marked IMMUTABLE")));
|
2003-05-28 18:04:02 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
classOidP[attn] = GetIndexOpClass(attribute->opclass,
|
|
|
|
atttype,
|
|
|
|
accessMethodName,
|
|
|
|
accessMethodId);
|
2000-07-15 00:18:02 +02:00
|
|
|
attn++;
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
/*
|
|
|
|
* Resolve possibly-defaulted operator class specification
|
|
|
|
*/
|
2000-02-25 03:58:48 +01:00
|
|
|
static Oid
|
2003-05-28 18:04:02 +02:00
|
|
|
GetIndexOpClass(List *opclass, Oid attrType,
|
|
|
|
char *accessMethodName, Oid accessMethodId)
|
2000-02-25 03:58:48 +01:00
|
|
|
{
|
2002-07-30 01:46:35 +02:00
|
|
|
char *schemaname;
|
|
|
|
char *opcname;
|
2000-02-25 03:58:48 +01:00
|
|
|
HeapTuple tuple;
|
2000-04-25 04:45:54 +02:00
|
|
|
Oid opClassId,
|
2001-08-21 18:36:06 +02:00
|
|
|
opInputType;
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
/*
|
|
|
|
* Release 7.0 removed network_ops, timespan_ops, and
|
|
|
|
* datetime_ops, so we ignore those opclass names
|
|
|
|
* so the default *_ops is used. This can be
|
|
|
|
* removed in some later release. bjm 2000/02/07
|
|
|
|
*
|
|
|
|
* Release 7.1 removes lztext_ops, so suppress that too
|
|
|
|
* for a while. tgl 2000/07/30
|
|
|
|
*
|
|
|
|
* Release 7.2 renames timestamp_ops to timestamptz_ops,
|
|
|
|
* so suppress that too for awhile. I'm starting to
|
|
|
|
* think we need a better approach. tgl 2000/10/01
|
|
|
|
*/
|
|
|
|
if (length(opclass) == 1)
|
|
|
|
{
|
|
|
|
char *claname = strVal(lfirst(opclass));
|
|
|
|
|
|
|
|
if (strcmp(claname, "network_ops") == 0 ||
|
|
|
|
strcmp(claname, "timespan_ops") == 0 ||
|
|
|
|
strcmp(claname, "datetime_ops") == 0 ||
|
|
|
|
strcmp(claname, "lztext_ops") == 0 ||
|
|
|
|
strcmp(claname, "timestamp_ops") == 0)
|
|
|
|
opclass = NIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opclass == NIL)
|
2000-02-25 03:58:48 +01:00
|
|
|
{
|
|
|
|
/* no operator class specified, so find the default */
|
2001-08-21 18:36:06 +02:00
|
|
|
opClassId = GetDefaultOpClass(attrType, accessMethodId);
|
|
|
|
if (!OidIsValid(opClassId))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("data type %s has no default operator class for access method \"%s\"",
|
|
|
|
format_type_be(attrType), accessMethodName),
|
|
|
|
errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
|
2001-08-21 18:36:06 +02:00
|
|
|
return opClassId;
|
2000-02-25 03:58:48 +01:00
|
|
|
}
|
|
|
|
|
2000-04-23 03:44:55 +02:00
|
|
|
/*
|
2002-04-17 22:57:57 +02:00
|
|
|
* Specific opclass name given, so look up the opclass.
|
2000-04-23 03:44:55 +02:00
|
|
|
*/
|
2002-04-17 22:57:57 +02:00
|
|
|
|
|
|
|
/* deconstruct the name list */
|
2003-05-28 18:04:02 +02:00
|
|
|
DeconstructQualifiedName(opclass, &schemaname, &opcname);
|
2002-04-17 22:57:57 +02:00
|
|
|
|
|
|
|
if (schemaname)
|
|
|
|
{
|
|
|
|
/* Look in specific schema only */
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid namespaceId;
|
2002-04-17 22:57:57 +02:00
|
|
|
|
2002-07-30 01:46:35 +02:00
|
|
|
namespaceId = LookupExplicitNamespace(schemaname);
|
2002-04-17 22:57:57 +02:00
|
|
|
tuple = SearchSysCache(CLAAMNAMENSP,
|
|
|
|
ObjectIdGetDatum(accessMethodId),
|
|
|
|
PointerGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(namespaceId),
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unqualified opclass name, so search the search path */
|
|
|
|
opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
|
|
|
|
if (!OidIsValid(opClassId))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
|
|
|
opcname, accessMethodName)));
|
2002-04-17 22:57:57 +02:00
|
|
|
tuple = SearchSysCache(CLAOID,
|
|
|
|
ObjectIdGetDatum(opClassId),
|
|
|
|
0, 0, 0);
|
|
|
|
}
|
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
|
|
|
NameListToString(opclass), accessMethodName)));
|
2002-04-17 22:57:57 +02:00
|
|
|
|
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Verify that the index operator class accepts this datatype. Note
|
|
|
|
* we will accept binary compatibility.
|
2002-04-17 22:57:57 +02:00
|
|
|
*/
|
2002-07-20 07:16:59 +02:00
|
|
|
opClassId = HeapTupleGetOid(tuple);
|
2001-08-21 18:36:06 +02:00
|
|
|
opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
|
2000-04-23 03:44:55 +02:00
|
|
|
|
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
2002-09-18 23:35:25 +02:00
|
|
|
if (!IsBinaryCoercible(attrType, opInputType))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("operator class \"%s\" does not accept data type %s",
|
|
|
|
NameListToString(opclass), format_type_be(attrType))));
|
2002-04-17 22:57:57 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
2000-04-25 04:45:54 +02:00
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
return opClassId;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Oid
|
|
|
|
GetDefaultOpClass(Oid attrType, Oid accessMethodId)
|
|
|
|
{
|
2002-04-17 22:57:57 +02:00
|
|
|
OpclassCandidateList opclass;
|
2001-08-21 18:36:06 +02:00
|
|
|
int nexact = 0;
|
|
|
|
int ncompatible = 0;
|
|
|
|
Oid exactOid = InvalidOid;
|
|
|
|
Oid compatibleOid = InvalidOid;
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2002-08-16 22:55:09 +02:00
|
|
|
/* If it's a domain, look at the base type instead */
|
|
|
|
attrType = getBaseType(attrType);
|
|
|
|
|
2000-04-25 04:45:54 +02:00
|
|
|
/*
|
2001-08-21 18:36:06 +02:00
|
|
|
* We scan through all the opclasses available for the access method,
|
|
|
|
* looking for one that is marked default and matches the target type
|
|
|
|
* (either exactly or binary-compatibly, but prefer an exact match).
|
|
|
|
*
|
|
|
|
* We could find more than one binary-compatible match, in which case we
|
2001-10-25 07:50:21 +02:00
|
|
|
* require the user to specify which one he wants. If we find more
|
|
|
|
* than one exact match, then someone put bogus entries in pg_opclass.
|
2000-04-25 04:45:54 +02:00
|
|
|
*
|
2002-04-17 22:57:57 +02:00
|
|
|
* The initial search is done by namespace.c so that we only consider
|
|
|
|
* opclasses visible in the current namespace search path.
|
2000-04-25 04:45:54 +02:00
|
|
|
*/
|
2002-04-17 22:57:57 +02:00
|
|
|
for (opclass = OpclassGetCandidates(accessMethodId);
|
|
|
|
opclass != NULL;
|
|
|
|
opclass = opclass->next)
|
2000-04-25 04:45:54 +02:00
|
|
|
{
|
2001-08-21 18:36:06 +02:00
|
|
|
if (opclass->opcdefault)
|
2000-04-25 04:45:54 +02:00
|
|
|
{
|
2001-08-21 18:36:06 +02:00
|
|
|
if (opclass->opcintype == attrType)
|
|
|
|
{
|
|
|
|
nexact++;
|
2002-04-17 22:57:57 +02:00
|
|
|
exactOid = opclass->oid;
|
2001-08-21 18:36:06 +02:00
|
|
|
}
|
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
2002-09-18 23:35:25 +02:00
|
|
|
else if (IsBinaryCoercible(attrType, opclass->opcintype))
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
|
|
|
ncompatible++;
|
2002-04-17 22:57:57 +02:00
|
|
|
compatibleOid = opclass->oid;
|
2001-08-21 18:36:06 +02:00
|
|
|
}
|
2000-04-25 04:45:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
if (nexact == 1)
|
|
|
|
return exactOid;
|
|
|
|
if (nexact != 0)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("there are multiple default operator classes for data type %s",
|
|
|
|
format_type_be(attrType))));
|
2001-08-21 18:36:06 +02:00
|
|
|
if (ncompatible == 1)
|
|
|
|
return compatibleOid;
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
return InvalidOid;
|
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
|
|
|
*/
|
|
|
|
void
|
2002-07-01 17:27:56 +02:00
|
|
|
RemoveIndex(RangeVar *relation, DropBehavior behavior)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid indOid;
|
2002-09-20 01:40:56 +02:00
|
|
|
char relkind;
|
2002-07-12 20:43:19 +02:00
|
|
|
ObjectAddress object;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
indOid = RangeVarGetRelid(relation, false);
|
2002-09-20 01:40:56 +02:00
|
|
|
relkind = get_rel_relkind(indOid);
|
|
|
|
if (relkind != RELKIND_INDEX)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("relation \"%s\" is not an index",
|
|
|
|
relation->relname)));
|
2002-03-26 20:17:02 +01:00
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
object.classId = RelOid_pg_class;
|
|
|
|
object.objectId = indOid;
|
|
|
|
object.objectSubId = 0;
|
|
|
|
|
|
|
|
performDeletion(&object, behavior);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
/*
|
2002-08-29 17:56:20 +02:00
|
|
|
* ReindexIndex
|
2000-02-18 10:30:20 +01:00
|
|
|
* Recreate an index.
|
|
|
|
*/
|
|
|
|
void
|
2002-03-26 20:17:02 +01:00
|
|
|
ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid indOid;
|
2000-02-18 10:30:20 +01:00
|
|
|
HeapTuple tuple;
|
2002-10-22 00:06:20 +02:00
|
|
|
bool overwrite;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2002-10-22 00:06:20 +02:00
|
|
|
/* Choose in-place-or-not mode */
|
|
|
|
overwrite = IsIgnoringSystemIndexes();
|
2000-11-08 23:10:03 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
indOid = RangeVarGetRelid(indexRelation, false);
|
|
|
|
tuple = SearchSysCache(RELOID,
|
|
|
|
ObjectIdGetDatum(indOid),
|
2000-11-16 23:30:52 +01:00
|
|
|
0, 0, 0);
|
2003-07-20 23:56:35 +02:00
|
|
|
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
|
|
|
|
elog(ERROR, "cache lookup failed for relation %u", indOid);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
|
|
|
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("relation \"%s\" is not an index",
|
|
|
|
indexRelation->relname)));
|
2002-03-26 20:17:02 +01:00
|
|
|
|
2002-08-29 17:56:20 +02:00
|
|
|
if (IsSystemClass((Form_pg_class) GETSTRUCT(tuple)) &&
|
|
|
|
!IsToastClass((Form_pg_class) GETSTRUCT(tuple)))
|
2002-04-12 22:38:31 +02:00
|
|
|
{
|
|
|
|
if (!allowSystemTableMods)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("\"%s\" is a system index",
|
|
|
|
indexRelation->relname),
|
|
|
|
errhint("Do REINDEX in standalone postgres with -O -P options.")));
|
2002-04-12 22:38:31 +02:00
|
|
|
if (!IsIgnoringSystemIndexes())
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("\"%s\" is a system index",
|
|
|
|
indexRelation->relname),
|
|
|
|
errhint("Do REINDEX in standalone postgres with -P -O options.")));
|
2002-04-12 22:38:31 +02:00
|
|
|
}
|
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2002-10-22 00:06:20 +02:00
|
|
|
/*
|
|
|
|
* In-place 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.
|
|
|
|
*/
|
|
|
|
if (overwrite)
|
|
|
|
PreventTransactionChain((void *) indexRelation, "REINDEX");
|
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
if (!reindex_index(indOid, force, overwrite))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(WARNING,
|
|
|
|
(errmsg("index \"%s\" wasn't reindexed",
|
|
|
|
indexRelation->relname)));
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexTable
|
|
|
|
* Recreate indexes of a table.
|
|
|
|
*/
|
|
|
|
void
|
2002-03-26 20:17:02 +01:00
|
|
|
ReindexTable(RangeVar *relation, bool force)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid heapOid;
|
2002-08-29 17:56:20 +02:00
|
|
|
char relkind;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
heapOid = RangeVarGetRelid(relation, false);
|
2002-09-20 01:40:56 +02:00
|
|
|
relkind = get_rel_relkind(heapOid);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2002-08-29 17:56:20 +02:00
|
|
|
if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("relation \"%s\" is not a table",
|
|
|
|
relation->relname)));
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2002-10-22 00:06:20 +02:00
|
|
|
/*
|
|
|
|
* In-place REINDEX within a transaction block is dangerous, because
|
|
|
|
* if the transaction is later rolled back we have no way to undo
|
|
|
|
* truncation of the index's physical file. Disallow it.
|
|
|
|
*
|
|
|
|
* XXX we assume that in-place reindex will only be done if
|
|
|
|
* IsIgnoringSystemIndexes() is true.
|
|
|
|
*/
|
|
|
|
if (IsIgnoringSystemIndexes())
|
|
|
|
PreventTransactionChain((void *) relation, "REINDEX");
|
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
if (!reindex_relation(heapOid, force))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(WARNING,
|
|
|
|
(errmsg("table \"%s\" wasn't reindexed",
|
|
|
|
relation->relname)));
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexDatabase
|
|
|
|
* Recreate indexes of a database.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|
2003-06-27 16:45:32 +02:00
|
|
|
if (strcmp(dbname, get_database_name(MyDatabaseId)) != 0)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("can only reindex the currently open database")));
|
2000-06-28 05:33:33 +02:00
|
|
|
|
2003-06-27 16:45:32 +02:00
|
|
|
if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("permission denied")));
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2002-04-12 22:38:31 +02:00
|
|
|
if (!allowSystemTableMods)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("REINDEX DATABASE must be done in standalone postgres with -O -P options")));
|
2002-04-12 22:38:31 +02:00
|
|
|
if (!IsIgnoringSystemIndexes())
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("REINDEX DATABASE must be done in standalone postgres with -P -O options")));
|
2002-04-12 22:38:31 +02:00
|
|
|
|
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
|
|
|
*/
|
2002-10-22 00:06:20 +02:00
|
|
|
PreventTransactionChain((void *) dbname, "REINDEX");
|
2002-10-19 22:15:09 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Create a memory context that will survive forced transaction
|
2003-05-02 22:54:36 +02:00
|
|
|
* commits we do below. Since it is a child of PortalContext, it will
|
2001-03-22 05:01:46 +01:00
|
|
|
* 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
|
|
|
*/
|
2003-05-02 22:54:36 +02:00
|
|
|
private_context = AllocSetContextCreate(PortalContext,
|
2000-06-28 05:33:33 +02:00
|
|
|
"ReindexDatabase",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2001-11-20 03:46:13 +01:00
|
|
|
/*
|
|
|
|
* Scan pg_class to build a list of the relations we need to reindex.
|
|
|
|
*/
|
2000-02-18 10:30:20 +01:00
|
|
|
relationRelation = heap_openr(RelationRelationName, AccessShareLock);
|
2002-05-21 01:51:44 +02:00
|
|
|
scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
|
2000-02-18 10:30:20 +01:00
|
|
|
relcnt = relalc = 0;
|
2002-05-21 01:51:44 +02:00
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
char relkind;
|
2002-08-29 17:56:20 +02:00
|
|
|
|
2000-02-18 10:30:20 +01:00
|
|
|
if (!all)
|
|
|
|
{
|
2002-08-29 17:56:20 +02:00
|
|
|
if (!(IsSystemClass((Form_pg_class) GETSTRUCT(tuple)) &&
|
|
|
|
!IsToastClass((Form_pg_class) GETSTRUCT(tuple))))
|
2000-02-18 10:30:20 +01:00
|
|
|
continue;
|
|
|
|
}
|
2002-08-29 17:56:20 +02:00
|
|
|
relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
|
|
|
|
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
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);
|
2002-07-20 07:16:59 +02:00
|
|
|
relids[relcnt] = HeapTupleGetOid(tuple);
|
2000-02-18 10:30:20 +01:00
|
|
|
relcnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(relationRelation, AccessShareLock);
|
|
|
|
|
2001-11-20 03:46:13 +01:00
|
|
|
/* Now reindex each rel in a separate transaction */
|
2003-05-14 05:26:03 +02:00
|
|
|
CommitTransactionCommand();
|
2000-02-18 10:30:20 +01:00
|
|
|
for (i = 0; i < relcnt; i++)
|
|
|
|
{
|
2003-05-14 05:26:03 +02:00
|
|
|
StartTransactionCommand();
|
2003-05-28 18:04:02 +02:00
|
|
|
SetQuerySnapshot(); /* might be needed for functions in indexes */
|
2000-04-25 12:38:38 +02:00
|
|
|
if (reindex_relation(relids[i], force))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(NOTICE,
|
|
|
|
(errmsg("relation %u was reindexed", relids[i])));
|
2003-05-14 05:26:03 +02:00
|
|
|
CommitTransactionCommand();
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
2003-05-14 05:26:03 +02:00
|
|
|
StartTransactionCommand();
|
2000-06-28 05:33:33 +02:00
|
|
|
|
|
|
|
MemoryContextDelete(private_context);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|