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
|
|
|
*
|
2014-01-07 22:05:30 +01:00
|
|
|
* Portions Copyright (c) 1996-2014, 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
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/commands/indexcmds.c
|
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
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "access/reloptions.h"
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "access/xact.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "catalog/catalog.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/index.h"
|
2006-02-10 20:01:12 +01:00
|
|
|
#include "catalog/indexing.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
2009-12-07 06:22:23 +01:00
|
|
|
#include "catalog/pg_opfamily.h"
|
2004-11-05 20:17:13 +01:00
|
|
|
#include "catalog/pg_tablespace.h"
|
2012-01-25 21:28:07 +01:00
|
|
|
#include "catalog/pg_type.h"
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
#include "commands/comment.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"
|
2011-12-21 21:17:28 +01:00
|
|
|
#include "commands/tablecmds.h"
|
2004-06-18 08:14:31 +02:00
|
|
|
#include "commands/tablespace.h"
|
2004-06-10 19:56:03 +02:00
|
|
|
#include "mb/pg_wchar.h"
|
2000-07-04 08:11:54 +02:00
|
|
|
#include "miscadmin.h"
|
2008-08-26 00:42:34 +02:00
|
|
|
#include "nodes/nodeFuncs.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "optimizer/clauses.h"
|
2010-05-27 17:59:10 +02:00
|
|
|
#include "optimizer/planner.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"
|
2009-12-07 06:22:23 +01:00
|
|
|
#include "parser/parse_oper.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/lmgr.h"
|
2011-09-04 07:13:16 +02:00
|
|
|
#include "storage/proc.h"
|
2007-09-05 20:10:48 +02:00
|
|
|
#include "storage/procarray.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"
|
2006-02-10 20:01:12 +01:00
|
|
|
#include "utils/fmgroids.h"
|
2007-05-02 23:08:46 +02:00
|
|
|
#include "utils/inval.h"
|
2001-07-17 23:53:01 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2005-05-06 19:24:55 +02:00
|
|
|
#include "utils/memutils.h"
|
2008-03-26 19:48:59 +01:00
|
|
|
#include "utils/snapmgr.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
2008-03-26 22:10:39 +01:00
|
|
|
#include "utils/tqual.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-12-28 22:57:37 +01:00
|
|
|
static void CheckPredicate(Expr *predicate);
|
2007-01-09 03:14:16 +01:00
|
|
|
static void ComputeIndexAttrs(IndexInfo *indexInfo,
|
2012-01-25 21:28:07 +01:00
|
|
|
Oid *typeOidP,
|
2011-02-08 22:04:18 +01:00
|
|
|
Oid *collationOidP,
|
2007-01-09 03:14:16 +01:00
|
|
|
Oid *classOidP,
|
|
|
|
int16 *colOptionP,
|
2004-08-29 07:07:03 +02:00
|
|
|
List *attList,
|
2009-12-07 06:22:23 +01:00
|
|
|
List *exclusionOpNames,
|
2004-08-29 07:07:03 +02:00
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName, Oid accessMethodId,
|
2007-01-09 03:14:16 +01:00
|
|
|
bool amcanorder,
|
2004-08-29 07:07:03 +02:00
|
|
|
bool isconstraint);
|
2003-05-28 18:04:02 +02:00
|
|
|
static Oid GetIndexOpClass(List *opclass, Oid attrType,
|
2003-08-04 02:43:34 +02:00
|
|
|
char *accessMethodName, Oid accessMethodId);
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
static char *ChooseIndexName(const char *tabname, Oid namespaceId,
|
|
|
|
List *colnames, List *exclusionOpNames,
|
|
|
|
bool primary, bool isconstraint);
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
static char *ChooseIndexNameAddition(List *colnames);
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
static List *ChooseIndexColumnNames(List *indexElems);
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
|
|
|
|
Oid relId, Oid oldRelId, void *arg);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2011-07-18 17:02:48 +02:00
|
|
|
/*
|
|
|
|
* CheckIndexCompatible
|
|
|
|
* Determine whether an existing index definition is compatible with a
|
|
|
|
* prospective index definition, such that the existing index storage
|
|
|
|
* could become the storage of the new index, avoiding a rebuild.
|
|
|
|
*
|
|
|
|
* 'heapRelation': the relation the index would apply to.
|
|
|
|
* 'accessMethodName': name of the AM to use.
|
|
|
|
* 'attributeList': a list of IndexElem specifying columns and expressions
|
|
|
|
* to index on.
|
|
|
|
* 'exclusionOpNames': list of names of exclusion-constraint operators,
|
|
|
|
* or NIL if not an exclusion constraint.
|
|
|
|
*
|
|
|
|
* This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates
|
|
|
|
* any indexes that depended on a changing column from their pg_get_indexdef
|
|
|
|
* or pg_get_constraintdef definitions. We omit some of the sanity checks of
|
|
|
|
* DefineIndex. We assume that the old and new indexes have the same number
|
|
|
|
* of columns and that if one has an expression column or predicate, both do.
|
|
|
|
* Errors arising from the attribute list still apply.
|
|
|
|
*
|
2012-01-25 21:28:07 +01:00
|
|
|
* Most column type changes that can skip a table rewrite do not invalidate
|
|
|
|
* indexes. We ackowledge this when all operator classes, collations and
|
|
|
|
* exclusion operators match. Though we could further permit intra-opfamily
|
|
|
|
* changes for btree and hash indexes, that adds subtle complexity with no
|
|
|
|
* concrete benefit for core types.
|
|
|
|
|
|
|
|
* When a comparison or exclusion operator has a polymorphic input type, the
|
2014-05-06 18:12:18 +02:00
|
|
|
* actual input types must also match. This defends against the possibility
|
2012-01-25 21:28:07 +01:00
|
|
|
* that operators could vary behavior in response to get_fn_expr_argtype().
|
|
|
|
* At present, this hazard is theoretical: check_exclusion_constraint() and
|
|
|
|
* all core index access methods decline to set fn_expr for such calls.
|
2011-07-18 17:02:48 +02:00
|
|
|
*
|
|
|
|
* We do not yet implement a test to verify compatibility of expression
|
|
|
|
* columns or predicates, so assume any such index is incompatible.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
CheckIndexCompatible(Oid oldId,
|
|
|
|
char *accessMethodName,
|
|
|
|
List *attributeList,
|
|
|
|
List *exclusionOpNames)
|
|
|
|
{
|
|
|
|
bool isconstraint;
|
2012-01-25 21:28:07 +01:00
|
|
|
Oid *typeObjectId;
|
2011-07-18 17:02:48 +02:00
|
|
|
Oid *collationObjectId;
|
|
|
|
Oid *classObjectId;
|
|
|
|
Oid accessMethodId;
|
|
|
|
Oid relationId;
|
|
|
|
HeapTuple tuple;
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
Form_pg_index indexForm;
|
2011-07-18 17:02:48 +02:00
|
|
|
Form_pg_am accessMethodForm;
|
|
|
|
bool amcanorder;
|
|
|
|
int16 *coloptions;
|
|
|
|
IndexInfo *indexInfo;
|
|
|
|
int numberOfAttributes;
|
|
|
|
int old_natts;
|
|
|
|
bool isnull;
|
|
|
|
bool ret = true;
|
|
|
|
oidvector *old_indclass;
|
|
|
|
oidvector *old_indcollation;
|
2012-01-25 21:28:07 +01:00
|
|
|
Relation irel;
|
2011-07-18 17:02:48 +02:00
|
|
|
int i;
|
|
|
|
Datum d;
|
|
|
|
|
|
|
|
/* Caller should already have the relation locked in some way. */
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
relationId = IndexGetRelation(oldId, false);
|
2012-06-10 21:20:04 +02:00
|
|
|
|
2011-07-18 17:02:48 +02:00
|
|
|
/*
|
|
|
|
* We can pretend isconstraint = false unconditionally. It only serves to
|
|
|
|
* decide the text of an error message that should never happen for us.
|
|
|
|
*/
|
|
|
|
isconstraint = false;
|
|
|
|
|
|
|
|
numberOfAttributes = list_length(attributeList);
|
|
|
|
Assert(numberOfAttributes > 0);
|
|
|
|
Assert(numberOfAttributes <= INDEX_MAX_KEYS);
|
|
|
|
|
|
|
|
/* look up the access method */
|
|
|
|
tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("access method \"%s\" does not exist",
|
|
|
|
accessMethodName)));
|
|
|
|
accessMethodId = HeapTupleGetOid(tuple);
|
|
|
|
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
|
|
|
|
amcanorder = accessMethodForm->amcanorder;
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
/*
|
2012-06-10 21:20:04 +02:00
|
|
|
* Compute the operator classes, collations, and exclusion operators for
|
|
|
|
* the new index, so we can test whether it's compatible with the existing
|
|
|
|
* one. Note that ComputeIndexAttrs might fail here, but that's OK:
|
|
|
|
* DefineIndex would have called this function with the same arguments
|
2011-07-18 17:02:48 +02:00
|
|
|
* later on, and it would have failed then anyway.
|
|
|
|
*/
|
|
|
|
indexInfo = makeNode(IndexInfo);
|
|
|
|
indexInfo->ii_Expressions = NIL;
|
|
|
|
indexInfo->ii_ExpressionsState = NIL;
|
|
|
|
indexInfo->ii_PredicateState = NIL;
|
|
|
|
indexInfo->ii_ExclusionOps = NULL;
|
|
|
|
indexInfo->ii_ExclusionProcs = NULL;
|
|
|
|
indexInfo->ii_ExclusionStrats = NULL;
|
2012-01-25 21:28:07 +01:00
|
|
|
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
2011-07-18 17:02:48 +02:00
|
|
|
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
|
|
|
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
|
|
|
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
|
2012-01-25 21:28:07 +01:00
|
|
|
ComputeIndexAttrs(indexInfo,
|
|
|
|
typeObjectId, collationObjectId, classObjectId,
|
2011-07-18 17:02:48 +02:00
|
|
|
coloptions, attributeList,
|
|
|
|
exclusionOpNames, relationId,
|
|
|
|
accessMethodName, accessMethodId,
|
|
|
|
amcanorder, isconstraint);
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the soon-obsolete pg_index tuple. */
|
|
|
|
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for index %u", oldId);
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
indexForm = (Form_pg_index) GETSTRUCT(tuple);
|
2011-07-18 17:02:48 +02:00
|
|
|
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
/*
|
|
|
|
* We don't assess expressions or predicates; assume incompatibility.
|
|
|
|
* Also, if the index is invalid for any reason, treat it as incompatible.
|
|
|
|
*/
|
2011-07-18 17:02:48 +02:00
|
|
|
if (!(heap_attisnull(tuple, Anum_pg_index_indpred) &&
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
heap_attisnull(tuple, Anum_pg_index_indexprs) &&
|
|
|
|
IndexIsValid(indexForm)))
|
2011-07-18 17:02:48 +02:00
|
|
|
{
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
/* Any change in operator class or collation breaks compatibility. */
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
old_natts = indexForm->indnatts;
|
2011-07-18 17:02:48 +02:00
|
|
|
Assert(old_natts == numberOfAttributes);
|
|
|
|
|
|
|
|
d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indcollation, &isnull);
|
|
|
|
Assert(!isnull);
|
|
|
|
old_indcollation = (oidvector *) DatumGetPointer(d);
|
|
|
|
|
|
|
|
d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indclass, &isnull);
|
|
|
|
Assert(!isnull);
|
|
|
|
old_indclass = (oidvector *) DatumGetPointer(d);
|
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
ret = (memcmp(old_indclass->values, classObjectId,
|
|
|
|
old_natts * sizeof(Oid)) == 0 &&
|
|
|
|
memcmp(old_indcollation->values, collationObjectId,
|
|
|
|
old_natts * sizeof(Oid)) == 0);
|
2011-07-18 17:02:48 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2012-01-26 14:21:31 +01:00
|
|
|
if (!ret)
|
|
|
|
return false;
|
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
/* For polymorphic opcintype, column type changes break compatibility. */
|
2012-06-10 21:20:04 +02:00
|
|
|
irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */
|
2012-01-26 14:21:31 +01:00
|
|
|
for (i = 0; i < old_natts; i++)
|
|
|
|
{
|
|
|
|
if (IsPolymorphicType(get_opclass_input_type(classObjectId[i])) &&
|
2012-06-10 21:20:04 +02:00
|
|
|
irel->rd_att->attrs[i]->atttypid != typeObjectId[i])
|
2012-01-26 14:21:31 +01:00
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-01-25 21:28:07 +01:00
|
|
|
|
|
|
|
/* Any change in exclusion operator selections breaks compatibility. */
|
|
|
|
if (ret && indexInfo->ii_ExclusionOps != NULL)
|
2011-07-18 17:02:48 +02:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
Oid *old_operators,
|
|
|
|
*old_procs;
|
2011-07-18 17:02:48 +02:00
|
|
|
uint16 *old_strats;
|
|
|
|
|
|
|
|
RelationGetExclusionInfo(irel, &old_operators, &old_procs, &old_strats);
|
2012-01-25 21:28:07 +01:00
|
|
|
ret = memcmp(old_operators, indexInfo->ii_ExclusionOps,
|
|
|
|
old_natts * sizeof(Oid)) == 0;
|
2011-07-18 17:02:48 +02:00
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
/* Require an exact input type match for polymorphic operators. */
|
2012-01-26 14:21:31 +01:00
|
|
|
if (ret)
|
2012-01-25 21:28:07 +01:00
|
|
|
{
|
2012-01-26 14:21:31 +01:00
|
|
|
for (i = 0; i < old_natts && ret; i++)
|
|
|
|
{
|
|
|
|
Oid left,
|
|
|
|
right;
|
2011-07-18 17:02:48 +02:00
|
|
|
|
2012-01-26 14:21:31 +01:00
|
|
|
op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right);
|
|
|
|
if ((IsPolymorphicType(left) || IsPolymorphicType(right)) &&
|
2012-06-10 21:20:04 +02:00
|
|
|
irel->rd_att->attrs[i]->atttypid != typeObjectId[i])
|
2012-01-26 14:21:31 +01:00
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-01-25 21:28:07 +01:00
|
|
|
}
|
2011-07-18 17:02:48 +02:00
|
|
|
}
|
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
index_close(irel, NoLock);
|
2011-07-18 17:02:48 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
*
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
* 'relationId': the OID of the heap relation on which the index is to be
|
|
|
|
* created
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
* 'stmt': IndexStmt describing the properties of the new index.
|
2005-04-14 03:38:22 +02:00
|
|
|
* 'indexRelationId': normally InvalidOid, but during bootstrap can be
|
|
|
|
* nonzero to specify a preselected OID for the index.
|
2004-05-05 06:48:48 +02:00
|
|
|
* 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
|
|
|
|
* 'check_rights': check for CREATE rights in the namespace. (This should
|
|
|
|
* be true except when ALTER is deleting/recreating an index.)
|
|
|
|
* 'skip_build': make the catalog entries but leave the index file empty;
|
|
|
|
* it will be filled later.
|
|
|
|
* 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
|
2011-07-18 17:02:48 +02:00
|
|
|
*
|
|
|
|
* Returns the OID of the created index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2011-07-18 17:02:48 +02:00
|
|
|
Oid
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
DefineIndex(Oid relationId,
|
|
|
|
IndexStmt *stmt,
|
2005-04-14 03:38:22 +02:00
|
|
|
Oid indexRelationId,
|
2004-05-05 06:48:48 +02:00
|
|
|
bool is_alter_table,
|
|
|
|
bool check_rights,
|
|
|
|
bool skip_build,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
bool quiet)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
char *indexRelationName;
|
|
|
|
char *accessMethodName;
|
2012-01-25 21:28:07 +01:00
|
|
|
Oid *typeObjectId;
|
2011-02-08 22:04:18 +01:00
|
|
|
Oid *collationObjectId;
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid *classObjectId;
|
|
|
|
Oid accessMethodId;
|
2002-04-27 05:45:03 +02:00
|
|
|
Oid namespaceId;
|
2004-06-18 08:14:31 +02:00
|
|
|
Oid tablespaceId;
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
List *indexColNames;
|
2002-01-04 00:21:32 +01:00
|
|
|
Relation rel;
|
2007-09-20 19:56:33 +02:00
|
|
|
Relation indexRelation;
|
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;
|
2007-01-09 03:14:16 +01:00
|
|
|
bool amcanorder;
|
2006-07-04 00:45:41 +02:00
|
|
|
RegProcedure amoptions;
|
|
|
|
Datum reloptions;
|
2007-01-09 03:14:16 +01:00
|
|
|
int16 *coloptions;
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexInfo *indexInfo;
|
1997-09-08 04:41:22 +02:00
|
|
|
int numberOfAttributes;
|
2013-04-25 22:58:05 +02:00
|
|
|
TransactionId limitXmin;
|
2007-09-05 20:10:48 +02:00
|
|
|
VirtualTransactionId *old_snapshots;
|
2009-04-04 19:40:36 +02:00
|
|
|
int n_old_snapshots;
|
2006-08-25 06:06:58 +02:00
|
|
|
LockRelId heaprelid;
|
2006-08-27 21:14:34 +02:00
|
|
|
LOCKTAG heaplocktag;
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
LOCKMODE lockmode;
|
2006-08-25 06:06:58 +02:00
|
|
|
Snapshot snapshot;
|
2009-04-04 19:40:36 +02:00
|
|
|
int i;
|
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
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
numberOfAttributes = list_length(stmt->indexParams);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (numberOfAttributes <= 0)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("must specify at least one column")));
|
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),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("cannot use more than %d columns in an index",
|
2003-07-20 23:56:35 +02:00
|
|
|
INDEX_MAX_KEYS)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2006-08-25 06:06:58 +02:00
|
|
|
* Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard
|
|
|
|
* index build; but for concurrent builds we allow INSERT/UPDATE/DELETE
|
|
|
|
* (but not VACUUM).
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
*
|
2014-05-06 18:12:18 +02:00
|
|
|
* NB: Caller is responsible for making sure that relationId refers to the
|
|
|
|
* relation on which the index should be built; except in bootstrap mode,
|
|
|
|
* this will typically require the caller to have already locked the
|
|
|
|
* relation. To avoid lock upgrade hazards, that lock should be at least
|
|
|
|
* as strong as the one we take here.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
Avoid repeated name lookups during table and index DDL.
If the name lookups come to different conclusions due to concurrent
activity, we might perform some parts of the DDL on a different table
than other parts. At least in the case of CREATE INDEX, this can be
used to cause the permissions checks to be performed against a
different table than the index creation, allowing for a privilege
escalation attack.
This changes the calling convention for DefineIndex, CreateTrigger,
transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible
(in 9.2 and newer), and AlterTable (in 9.1 and older). In addition,
CheckRelationOwnership is removed in 9.2 and newer and the calling
convention is changed in older branches. A field has also been added
to the Constraint node (FkConstraint in 8.4). Third-party code calling
these functions or using the Constraint node will require updating.
Report by Andres Freund. Patch by Robert Haas and Andres Freund,
reviewed by Tom Lane.
Security: CVE-2014-0062
2014-02-17 15:33:31 +01:00
|
|
|
lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock;
|
|
|
|
rel = heap_open(relationId, lockmode);
|
2006-08-25 06:06:58 +02:00
|
|
|
|
|
|
|
relationId = RelationGetRelid(rel);
|
|
|
|
namespaceId = RelationGetNamespace(rel);
|
2002-01-04 00:21:32 +01:00
|
|
|
|
2013-03-04 01:23:31 +01:00
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION &&
|
|
|
|
rel->rd_rel->relkind != RELKIND_MATVIEW)
|
2011-05-05 21:47:42 +02:00
|
|
|
{
|
|
|
|
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
|
2011-06-09 20:32:50 +02:00
|
|
|
|
2011-05-05 21:47:42 +02:00
|
|
|
/*
|
2011-06-09 20:32:50 +02:00
|
|
|
* Custom error message for FOREIGN TABLE since the term is close
|
|
|
|
* to a regular table and can confuse the user.
|
2011-05-05 21:47:42 +02:00
|
|
|
*/
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("cannot create index on foreign table \"%s\"",
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
RelationGetRelationName(rel))));
|
2011-05-05 21:47:42 +02:00
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
2013-07-05 21:25:51 +02:00
|
|
|
errmsg("\"%s\" is not a table or materialized view",
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
RelationGetRelationName(rel))));
|
2011-05-05 21:47:42 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2006-08-25 06:06:58 +02:00
|
|
|
/*
|
|
|
|
* Don't try to CREATE INDEX on temp tables of other backends.
|
|
|
|
*/
|
2009-04-01 00:12:48 +02:00
|
|
|
if (RELATION_IS_OTHER_TEMP(rel))
|
2006-08-25 06:06:58 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot create indexes on temporary tables of other sessions")));
|
2002-01-04 00:21:32 +01:00
|
|
|
|
2002-04-27 05:45:03 +02:00
|
|
|
/*
|
|
|
|
* Verify we (still) have CREATE rights in the rel's namespace.
|
2005-10-15 04:49:52 +02:00
|
|
|
* (Presumably we did when the rel was created, but maybe not anymore.)
|
|
|
|
* Skip check if caller doesn't want it. Also skip check if
|
|
|
|
* bootstrapping, since permissions machinery may not be working yet.
|
2002-04-27 05:45:03 +02:00
|
|
|
*/
|
2004-05-05 06:48:48 +02:00
|
|
|
if (check_rights && !IsBootstrapProcessingMode())
|
2002-04-27 05:45:03 +02:00
|
|
|
{
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
|
|
|
|
ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
|
|
get_namespace_name(namespaceId));
|
2002-04-27 05:45:03 +02:00
|
|
|
}
|
|
|
|
|
2004-11-05 20:17:13 +01:00
|
|
|
/*
|
2007-06-03 19:08:34 +02:00
|
|
|
* Select tablespace to use. If not specified, use default tablespace
|
2004-11-05 20:17:13 +01:00
|
|
|
* (which may in turn default to database's default).
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->tableSpace)
|
2004-06-18 08:14:31 +02:00
|
|
|
{
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
|
2004-11-05 20:17:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-12-13 18:34:26 +01:00
|
|
|
tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence);
|
2004-11-05 20:17:13 +01:00
|
|
|
/* note InvalidOid is OK in this case */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check permissions except when using database's default */
|
2008-02-07 18:09:51 +01:00
|
|
|
if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
|
2004-11-05 20:17:13 +01:00
|
|
|
{
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
|
|
|
|
ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
|
2004-11-05 20:17:13 +01:00
|
|
|
get_tablespace_name(tablespaceId));
|
2004-06-18 08:14:31 +02:00
|
|
|
}
|
|
|
|
|
2004-11-05 20:17:13 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Force shared indexes into the pg_global tablespace. This is a bit of a
|
2010-02-07 21:48:13 +01:00
|
|
|
* hack but seems simpler than marking them in the BKI commands. On the
|
|
|
|
* other hand, if it's not shared, don't allow it to be placed there.
|
2004-11-05 20:17:13 +01:00
|
|
|
*/
|
|
|
|
if (rel->rd_rel->relisshared)
|
|
|
|
tablespaceId = GLOBALTABLESPACE_OID;
|
2010-02-07 21:48:13 +01:00
|
|
|
else if (tablespaceId == GLOBALTABLESPACE_OID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("only shared relations can be placed in pg_global tablespace")));
|
2004-11-05 20:17:13 +01:00
|
|
|
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
/*
|
|
|
|
* Choose the index column names.
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
indexColNames = ChooseIndexColumnNames(stmt->indexParams);
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
|
2004-05-05 06:48:48 +02:00
|
|
|
/*
|
|
|
|
* Select name for index if caller didn't specify
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
indexRelationName = stmt->idxname;
|
2004-05-05 06:48:48 +02:00
|
|
|
if (indexRelationName == NULL)
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
indexRelationName = ChooseIndexName(RelationGetRelationName(rel),
|
|
|
|
namespaceId,
|
|
|
|
indexColNames,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
stmt->excludeOpNames,
|
|
|
|
stmt->primary,
|
|
|
|
stmt->isconstraint);
|
2004-05-05 06:48:48 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* look up the access method, verify it can handle the requested features
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
accessMethodName = stmt->accessMethod;
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(AMNAME, PointerGetDatum(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
|
|
|
if (!HeapTupleIsValid(tuple))
|
2005-11-07 18:36:47 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Hack to provide more-or-less-transparent updating of old RTREE
|
2011-05-19 00:14:45 +02:00
|
|
|
* indexes to GiST: if RTREE is requested and not found, use GIST.
|
2005-11-07 18:36:47 +01:00
|
|
|
*/
|
|
|
|
if (strcmp(accessMethodName, "rtree") == 0)
|
|
|
|
{
|
|
|
|
ereport(NOTICE,
|
|
|
|
(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
|
|
|
|
accessMethodName = "gist";
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
|
2005-11-07 18:36:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
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
|
|
|
|
2014-09-10 22:54:40 +02:00
|
|
|
if (strcmp(accessMethodName, "hash") == 0)
|
|
|
|
ereport(WARNING,
|
2014-09-11 19:40:06 +02:00
|
|
|
(errmsg("hash indexes are not WAL-logged and thus are not crash-safe and cannot be used on standby servers")));
|
2014-09-10 22:54:40 +02:00
|
|
|
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->unique && !accessMethodForm->amcanunique)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
2005-10-15 04:49:52 +02:00
|
|
|
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),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("access method \"%s\" does not support multicolumn indexes",
|
|
|
|
accessMethodName)));
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->excludeOpNames && !OidIsValid(accessMethodForm->amgettuple))
|
2009-12-07 06:22:23 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
2010-02-26 03:01:40 +01:00
|
|
|
errmsg("access method \"%s\" does not support exclusion constraints",
|
|
|
|
accessMethodName)));
|
2000-07-15 00:18:02 +02:00
|
|
|
|
2007-01-21 00:13:01 +01:00
|
|
|
amcanorder = accessMethodForm->amcanorder;
|
2006-07-04 00:45:41 +02:00
|
|
|
amoptions = accessMethodForm->amoptions;
|
|
|
|
|
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
|
|
|
/*
|
2003-12-28 22:57:37 +01:00
|
|
|
* Validate predicate, if given
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->whereClause)
|
|
|
|
CheckPredicate((Expr *) stmt->whereClause);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2006-07-04 00:45:41 +02:00
|
|
|
/*
|
2007-12-02 00:44:44 +01:00
|
|
|
* Parse AM-specific options, convert to text array form, validate.
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
reloptions = transformRelOptions((Datum) 0, stmt->options,
|
|
|
|
NULL, NULL, false, false);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
(void) index_reloptions(amoptions, reloptions, true);
|
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Prepare arguments for index_create, primarily an IndexInfo structure.
|
|
|
|
* Note that ii_Predicate must be in implicit-AND format.
|
2000-07-15 00:18:02 +02:00
|
|
|
*/
|
|
|
|
indexInfo = makeNode(IndexInfo);
|
2003-05-28 18:04:02 +02:00
|
|
|
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
2003-08-04 02:43:34 +02:00
|
|
|
indexInfo->ii_Expressions = NIL; /* for now */
|
2003-05-28 18:04:02 +02:00
|
|
|
indexInfo->ii_ExpressionsState = NIL;
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
|
2002-12-15 17:17:59 +01:00
|
|
|
indexInfo->ii_PredicateState = NIL;
|
2009-12-07 06:22:23 +01:00
|
|
|
indexInfo->ii_ExclusionOps = NULL;
|
|
|
|
indexInfo->ii_ExclusionProcs = NULL;
|
|
|
|
indexInfo->ii_ExclusionStrats = NULL;
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
indexInfo->ii_Unique = stmt->unique;
|
2007-09-20 19:56:33 +02:00
|
|
|
/* In a concurrent build, mark it not-ready-for-inserts */
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
|
|
|
|
indexInfo->ii_Concurrent = stmt->concurrent;
|
2007-09-20 19:56:33 +02:00
|
|
|
indexInfo->ii_BrokenHotChain = false;
|
2000-07-15 00:18:02 +02:00
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
2011-02-08 22:04:18 +01:00
|
|
|
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
2003-05-28 18:04:02 +02:00
|
|
|
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
2007-01-09 03:14:16 +01:00
|
|
|
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
|
2012-01-25 21:28:07 +01:00
|
|
|
ComputeIndexAttrs(indexInfo,
|
|
|
|
typeObjectId, collationObjectId, classObjectId,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
coloptions, stmt->indexParams,
|
|
|
|
stmt->excludeOpNames, relationId,
|
2009-12-07 06:22:23 +01:00
|
|
|
accessMethodName, accessMethodId,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
amcanorder, stmt->isconstraint);
|
2004-05-05 06:48:48 +02:00
|
|
|
|
2011-01-25 21:42:03 +01:00
|
|
|
/*
|
|
|
|
* Extra checks when creating a PRIMARY KEY index.
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->primary)
|
2011-01-25 21:42:03 +01:00
|
|
|
index_check_primary_key(rel, indexInfo, is_alter_table);
|
|
|
|
|
2004-05-05 06:48:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Report index creation if appropriate (delay this till after most of the
|
|
|
|
* error checks)
|
2004-05-05 06:48:48 +02:00
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->isconstraint && !quiet)
|
2009-12-07 06:22:23 +01:00
|
|
|
{
|
|
|
|
const char *constraint_type;
|
|
|
|
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
if (stmt->primary)
|
2009-12-07 06:22:23 +01:00
|
|
|
constraint_type = "PRIMARY KEY";
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
else if (stmt->unique)
|
2009-12-07 06:22:23 +01:00
|
|
|
constraint_type = "UNIQUE";
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
else if (stmt->excludeOpNames != NIL)
|
2009-12-07 06:22:23 +01:00
|
|
|
constraint_type = "EXCLUDE";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "unknown constraint type");
|
2010-02-26 03:01:40 +01:00
|
|
|
constraint_type = NULL; /* keep compiler quiet */
|
2009-12-07 06:22:23 +01:00
|
|
|
}
|
|
|
|
|
2012-07-05 02:34:24 +02:00
|
|
|
ereport(DEBUG1,
|
2005-10-15 04:49:52 +02:00
|
|
|
(errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
|
|
|
|
is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
|
2009-12-07 06:22:23 +01:00
|
|
|
constraint_type,
|
2005-10-15 04:49:52 +02:00
|
|
|
indexRelationName, RelationGetRelationName(rel))));
|
2009-12-07 06:22:23 +01:00
|
|
|
}
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2011-07-18 17:02:48 +02:00
|
|
|
/*
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
* A valid stmt->oldNode implies that we already have a built form of the
|
2011-07-18 17:02:48 +02:00
|
|
|
* index. The caller should also decline any index build.
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
Assert(!OidIsValid(stmt->oldNode) || (skip_build && !stmt->concurrent));
|
2011-07-18 17:02:48 +02:00
|
|
|
|
2007-09-20 19:56:33 +02:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Make the catalog entries for the index, including constraints. Then, if
|
|
|
|
* not skip_build || concurrent, actually build the index.
|
2007-09-20 19:56:33 +02:00
|
|
|
*/
|
2006-08-25 06:06:58 +02:00
|
|
|
indexRelationId =
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
index_create(rel, indexRelationName, indexRelationId, stmt->oldNode,
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
indexInfo, indexColNames,
|
2011-04-22 23:43:18 +02:00
|
|
|
accessMethodId, tablespaceId,
|
|
|
|
collationObjectId, classObjectId,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
coloptions, reloptions, stmt->primary,
|
|
|
|
stmt->isconstraint, stmt->deferrable, stmt->initdeferred,
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
allowSystemTableMods,
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
skip_build || stmt->concurrent,
|
2012-10-23 23:07:26 +02:00
|
|
|
stmt->concurrent, !check_rights);
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
/* Add any requested comment */
|
|
|
|
if (stmt->idxcomment != NULL)
|
|
|
|
CreateComments(indexRelationId, RelationRelationId, 0,
|
|
|
|
stmt->idxcomment);
|
|
|
|
|
|
|
|
if (!stmt->concurrent)
|
2011-01-25 21:42:03 +01:00
|
|
|
{
|
|
|
|
/* Close the heap and we're done, in the non-concurrent case */
|
|
|
|
heap_close(rel, NoLock);
|
2011-07-18 17:02:48 +02:00
|
|
|
return indexRelationId;
|
2011-01-25 21:42:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* save lockrelid and locktag for below, then close rel */
|
|
|
|
heaprelid = rel->rd_lockInfo.lockRelId;
|
|
|
|
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
|
|
|
|
heap_close(rel, NoLock);
|
2006-08-25 06:06:58 +02:00
|
|
|
|
|
|
|
/*
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
* For a concurrent build, it's important to make the catalog entries
|
2010-02-26 03:01:40 +01:00
|
|
|
* visible to other transactions before we start to build the index. That
|
2014-05-06 18:12:18 +02:00
|
|
|
* will prevent them from making incompatible HOT updates. The new index
|
2010-02-26 03:01:40 +01:00
|
|
|
* will be marked not indisready and not indisvalid, so that no one else
|
|
|
|
* tries to either insert into it or use it for queries.
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
*
|
2006-08-25 06:06:58 +02:00
|
|
|
* We must commit our current transaction so that the index becomes
|
2006-10-04 02:30:14 +02:00
|
|
|
* visible; then start another. Note that all the data structures we just
|
|
|
|
* built are lost in the commit. The only data we keep past here are the
|
|
|
|
* relation IDs.
|
2006-08-25 06:06:58 +02:00
|
|
|
*
|
|
|
|
* Before committing, get a session-level lock on the table, to ensure
|
2006-10-04 02:30:14 +02:00
|
|
|
* that neither it nor the index can be dropped before we finish. This
|
|
|
|
* cannot block, even if someone else is waiting for access, because we
|
|
|
|
* already have the same lock within our transaction.
|
2006-08-25 06:06:58 +02:00
|
|
|
*
|
|
|
|
* Note: we don't currently bother with a session lock on the index,
|
2006-10-04 02:30:14 +02:00
|
|
|
* because there are no operations that could change its state while we
|
|
|
|
* hold lock on the parent table. This might need to change later.
|
2006-08-25 06:06:58 +02:00
|
|
|
*/
|
|
|
|
LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
|
|
|
|
|
2008-05-12 22:02:02 +02:00
|
|
|
PopActiveSnapshot();
|
2006-08-25 06:06:58 +02:00
|
|
|
CommitTransactionCommand();
|
|
|
|
StartTransactionCommand();
|
|
|
|
|
|
|
|
/*
|
2007-09-20 19:56:33 +02:00
|
|
|
* Phase 2 of concurrent index build (see comments for validate_index()
|
|
|
|
* for an overview of how this works)
|
|
|
|
*
|
2006-08-25 06:06:58 +02:00
|
|
|
* Now we must wait until no running transaction could have the table open
|
2014-01-03 17:22:03 +01:00
|
|
|
* with the old list of indexes. Use ShareLock to consider running
|
|
|
|
* transactions that hold locks that permit writing to the table. Note we
|
|
|
|
* do not need to worry about xacts that open the table for writing after
|
|
|
|
* this point; they will see the new index when they open it.
|
2006-08-27 21:14:34 +02:00
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Note: the reason we use actual lock acquisition here, rather than just
|
|
|
|
* checking the ProcArray and sleeping, is that deadlock is possible if
|
|
|
|
* one of the transactions in question is blocked trying to acquire an
|
|
|
|
* exclusive lock on our table. The lock code will detect deadlock and
|
|
|
|
* error out properly.
|
2006-08-25 06:06:58 +02:00
|
|
|
*/
|
2013-09-27 16:46:33 +02:00
|
|
|
WaitForLockers(heaplocktag, ShareLock);
|
2007-09-20 19:56:33 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this moment we are sure that there are no transactions with the
|
|
|
|
* table open for write that don't have this new index in their list of
|
|
|
|
* indexes. We have waited out all the existing transactions and any new
|
|
|
|
* transaction will have the new index in its list, but the index is still
|
|
|
|
* marked as "not-ready-for-inserts". The index is consulted while
|
2014-05-06 18:12:18 +02:00
|
|
|
* deciding HOT-safety though. This arrangement ensures that no new HOT
|
2007-09-20 19:56:33 +02:00
|
|
|
* chains can be created where the new tuple and the old tuple in the
|
|
|
|
* chain have different index keys.
|
|
|
|
*
|
|
|
|
* We now take a new snapshot, and build the index using all tuples that
|
2007-11-15 22:14:46 +01:00
|
|
|
* are visible in this snapshot. We can be sure that any HOT updates to
|
|
|
|
* these tuples will be compatible with the index, since any updates made
|
|
|
|
* by transactions that didn't know about the index are now committed or
|
|
|
|
* rolled back. Thus, each visible tuple is either the end of its
|
2007-09-20 19:56:33 +02:00
|
|
|
* HOT-chain or the extension of the chain is HOT-safe for this index.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Open and lock the parent heap relation */
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
rel = heap_openrv(stmt->relation, ShareUpdateExclusiveLock);
|
2007-09-20 19:56:33 +02:00
|
|
|
|
|
|
|
/* And the target index relation */
|
|
|
|
indexRelation = index_open(indexRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
/* Set ActiveSnapshot since functions in the indexes may need it */
|
2008-05-12 22:02:02 +02:00
|
|
|
PushActiveSnapshot(GetTransactionSnapshot());
|
2007-09-20 19:56:33 +02:00
|
|
|
|
|
|
|
/* We have to re-build the IndexInfo struct, since it was lost in commit */
|
|
|
|
indexInfo = BuildIndexInfo(indexRelation);
|
|
|
|
Assert(!indexInfo->ii_ReadyForInserts);
|
|
|
|
indexInfo->ii_Concurrent = true;
|
|
|
|
indexInfo->ii_BrokenHotChain = false;
|
|
|
|
|
|
|
|
/* Now build the index */
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
index_build(rel, indexRelation, indexInfo, stmt->primary, false);
|
2007-09-20 19:56:33 +02:00
|
|
|
|
|
|
|
/* Close both the relations, but keep the locks */
|
|
|
|
heap_close(rel, NoLock);
|
|
|
|
index_close(indexRelation, NoLock);
|
|
|
|
|
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Update the pg_index row to mark the index as ready for inserts. Once we
|
|
|
|
* commit this transaction, any new transactions that open the table must
|
|
|
|
* insert new entries into the index for insertions and non-HOT updates.
|
2007-09-20 19:56:33 +02:00
|
|
|
*/
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY);
|
2007-09-20 19:56:33 +02:00
|
|
|
|
2008-05-12 22:02:02 +02:00
|
|
|
/* we can do away with our snapshot */
|
|
|
|
PopActiveSnapshot();
|
|
|
|
|
2007-09-20 19:56:33 +02:00
|
|
|
/*
|
|
|
|
* Commit this transaction to make the indisready update visible.
|
|
|
|
*/
|
|
|
|
CommitTransactionCommand();
|
|
|
|
StartTransactionCommand();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Phase 3 of concurrent index build
|
|
|
|
*
|
|
|
|
* We once again wait until no transaction can have the table open with
|
|
|
|
* the index marked as read-only for updates.
|
|
|
|
*/
|
2013-09-27 16:46:33 +02:00
|
|
|
WaitForLockers(heaplocktag, ShareLock);
|
2006-08-25 06:06:58 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now take the "reference snapshot" that will be used by validate_index()
|
2014-05-06 18:12:18 +02:00
|
|
|
* to filter candidate tuples. Beware! There might still be snapshots in
|
2007-11-15 22:14:46 +01:00
|
|
|
* use that treat some transaction as in-progress that our reference
|
2007-09-05 20:10:48 +02:00
|
|
|
* snapshot treats as committed. If such a recently-committed transaction
|
|
|
|
* deleted tuples in the table, we will not include them in the index; yet
|
|
|
|
* those transactions which see the deleting one as still-in-progress will
|
2009-04-04 19:40:36 +02:00
|
|
|
* expect such tuples to be there once we mark the index as valid.
|
2007-09-05 20:10:48 +02:00
|
|
|
*
|
|
|
|
* We solve this by waiting for all endangered transactions to exit before
|
|
|
|
* we mark the index as valid.
|
2006-08-25 06:06:58 +02:00
|
|
|
*
|
2006-10-04 02:30:14 +02:00
|
|
|
* We also set ActiveSnapshot to this snap, since functions in indexes may
|
|
|
|
* need a snapshot.
|
2006-08-25 06:06:58 +02:00
|
|
|
*/
|
2008-05-12 22:02:02 +02:00
|
|
|
snapshot = RegisterSnapshot(GetTransactionSnapshot());
|
|
|
|
PushActiveSnapshot(snapshot);
|
2006-08-25 06:06:58 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the index and the heap, insert any missing index entries.
|
|
|
|
*/
|
|
|
|
validate_index(relationId, indexRelationId, snapshot);
|
|
|
|
|
2013-04-25 22:58:05 +02:00
|
|
|
/*
|
|
|
|
* Drop the reference snapshot. We must do this before waiting out other
|
|
|
|
* snapshot holders, else we will deadlock against other processes also
|
|
|
|
* doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
|
2014-05-06 18:12:18 +02:00
|
|
|
* they must wait for. But first, save the snapshot's xmin to use as
|
2013-04-25 22:58:05 +02:00
|
|
|
* limitXmin for GetCurrentVirtualXIDs().
|
|
|
|
*/
|
|
|
|
limitXmin = snapshot->xmin;
|
|
|
|
|
|
|
|
PopActiveSnapshot();
|
|
|
|
UnregisterSnapshot(snapshot);
|
|
|
|
|
2006-08-25 06:06:58 +02:00
|
|
|
/*
|
|
|
|
* The index is now valid in the sense that it contains all currently
|
2014-05-06 18:12:18 +02:00
|
|
|
* interesting tuples. But since it might not contain tuples deleted just
|
2006-10-04 02:30:14 +02:00
|
|
|
* before the reference snap was taken, we have to wait out any
|
2007-11-15 22:14:46 +01:00
|
|
|
* transactions that might have older snapshots. Obtain a list of VXIDs
|
|
|
|
* of such transactions, and wait for them individually.
|
2006-08-25 06:06:58 +02:00
|
|
|
*
|
2009-04-04 19:40:36 +02:00
|
|
|
* We can exclude any running transactions that have xmin > the xmin of
|
|
|
|
* our reference snapshot; their oldest snapshot must be newer than ours.
|
|
|
|
* We can also exclude any transactions that have xmin = zero, since they
|
2009-06-11 16:49:15 +02:00
|
|
|
* evidently have no live snapshot at all (and any one they might be in
|
|
|
|
* process of taking is certainly newer than ours). Transactions in other
|
|
|
|
* DBs can be ignored too, since they'll never even be able to see this
|
|
|
|
* index.
|
2008-01-09 22:52:36 +01:00
|
|
|
*
|
|
|
|
* We can also exclude autovacuum processes and processes running manual
|
|
|
|
* lazy VACUUMs, because they won't be fazed by missing index entries
|
2014-05-06 18:12:18 +02:00
|
|
|
* either. (Manual ANALYZEs, however, can't be excluded because they
|
2008-01-09 22:52:36 +01:00
|
|
|
* might be within transactions that are going to do arbitrary operations
|
|
|
|
* later.)
|
|
|
|
*
|
|
|
|
* Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
|
|
|
|
* check for that.
|
2009-04-04 19:40:36 +02:00
|
|
|
*
|
2009-06-11 16:49:15 +02:00
|
|
|
* If a process goes idle-in-transaction with xmin zero, we do not need to
|
|
|
|
* wait for it anymore, per the above argument. We do not have the
|
|
|
|
* infrastructure right now to stop waiting if that happens, but we can at
|
|
|
|
* least avoid the folly of waiting when it is idle at the time we would
|
|
|
|
* begin to wait. We do this by repeatedly rechecking the output of
|
2009-04-04 19:40:36 +02:00
|
|
|
* GetCurrentVirtualXIDs. If, during any iteration, a particular vxid
|
|
|
|
* doesn't show up in the output, we know we can forget about it.
|
2006-08-25 06:06:58 +02:00
|
|
|
*/
|
2013-04-25 22:58:05 +02:00
|
|
|
old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
|
2009-04-04 19:40:36 +02:00
|
|
|
PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
|
|
|
|
&n_old_snapshots);
|
2007-09-05 20:10:48 +02:00
|
|
|
|
2009-04-04 19:40:36 +02:00
|
|
|
for (i = 0; i < n_old_snapshots; i++)
|
2007-09-05 20:10:48 +02:00
|
|
|
{
|
2009-04-04 19:40:36 +02:00
|
|
|
if (!VirtualTransactionIdIsValid(old_snapshots[i]))
|
|
|
|
continue; /* found uninteresting in previous cycle */
|
|
|
|
|
|
|
|
if (i > 0)
|
|
|
|
{
|
|
|
|
/* see if anything's changed ... */
|
|
|
|
VirtualTransactionId *newer_snapshots;
|
|
|
|
int n_newer_snapshots;
|
|
|
|
int j;
|
|
|
|
int k;
|
|
|
|
|
2013-04-25 22:58:05 +02:00
|
|
|
newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
|
2009-04-04 19:40:36 +02:00
|
|
|
true, false,
|
2009-06-11 16:49:15 +02:00
|
|
|
PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
|
2009-04-04 19:40:36 +02:00
|
|
|
&n_newer_snapshots);
|
|
|
|
for (j = i; j < n_old_snapshots; j++)
|
|
|
|
{
|
|
|
|
if (!VirtualTransactionIdIsValid(old_snapshots[j]))
|
2009-06-11 16:49:15 +02:00
|
|
|
continue; /* found uninteresting in previous cycle */
|
2009-04-04 19:40:36 +02:00
|
|
|
for (k = 0; k < n_newer_snapshots; k++)
|
|
|
|
{
|
|
|
|
if (VirtualTransactionIdEquals(old_snapshots[j],
|
|
|
|
newer_snapshots[k]))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (k >= n_newer_snapshots) /* not there anymore */
|
|
|
|
SetInvalidVirtualTransactionId(old_snapshots[j]);
|
|
|
|
}
|
|
|
|
pfree(newer_snapshots);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VirtualTransactionIdIsValid(old_snapshots[i]))
|
2011-08-04 18:38:33 +02:00
|
|
|
VirtualXactLock(old_snapshots[i], true);
|
2007-09-05 20:10:48 +02:00
|
|
|
}
|
2006-08-25 06:06:58 +02:00
|
|
|
|
2007-05-02 23:08:46 +02:00
|
|
|
/*
|
|
|
|
* Index can now be marked valid -- update its pg_index entry
|
|
|
|
*/
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);
|
2006-08-25 06:06:58 +02:00
|
|
|
|
2007-05-02 23:08:46 +02:00
|
|
|
/*
|
|
|
|
* The pg_index update will cause backends (including this one) to update
|
|
|
|
* relcache entries for the index itself, but we should also send a
|
|
|
|
* relcache inval on the parent table to force replanning of cached plans.
|
|
|
|
* Otherwise existing sessions might fail to use the new index where it
|
2007-11-15 22:14:46 +01:00
|
|
|
* would be useful. (Note that our earlier commits did not create reasons
|
Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.
Commit 8cb53654dbdb4c386369eb988062d0bbb6de725e, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation. The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY. This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions. Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.
To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished. (This
change obviously is only possible in HEAD. This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)
In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update. The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption. This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.
In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate. These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.
Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation. Previously we
could have been left with stale values of some fields in an index relcache
entry. It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.
In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.
This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.
Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
2012-11-29 03:25:27 +01:00
|
|
|
* to replan; so relcache flush on the index itself was sufficient.)
|
2007-05-02 23:08:46 +02:00
|
|
|
*/
|
|
|
|
CacheInvalidateRelcacheByRelid(heaprelid.relId);
|
|
|
|
|
2006-08-25 06:06:58 +02:00
|
|
|
/*
|
|
|
|
* Last thing to do is release the session-level lock on the parent table.
|
|
|
|
*/
|
|
|
|
UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
|
2011-07-18 17:02:48 +02:00
|
|
|
|
|
|
|
return indexRelationId;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-27 17:59:10 +02:00
|
|
|
/*
|
|
|
|
* CheckMutability
|
|
|
|
* Test whether given expression is mutable
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
CheckMutability(Expr *expr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* First run the expression through the planner. This has a couple of
|
2014-05-06 18:12:18 +02:00
|
|
|
* important consequences. First, function default arguments will get
|
2010-05-27 17:59:10 +02:00
|
|
|
* inserted, which may affect volatility (consider "default now()").
|
|
|
|
* Second, inline-able functions will get inlined, which may allow us to
|
2010-07-06 21:19:02 +02:00
|
|
|
* conclude that the function is really less volatile than it's marked. As
|
|
|
|
* an example, polymorphic functions must be marked with the most volatile
|
|
|
|
* behavior that they have for any input type, but once we inline the
|
|
|
|
* function we may be able to conclude that it's not so volatile for the
|
|
|
|
* particular input type we're dealing with.
|
2010-05-27 17:59:10 +02:00
|
|
|
*
|
|
|
|
* We assume here that expression_planner() won't scribble on its input.
|
|
|
|
*/
|
|
|
|
expr = expression_planner(expr);
|
|
|
|
|
|
|
|
/* Now we can search for non-immutable functions */
|
|
|
|
return contain_mutable_functions((Node *) expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* CheckPredicate
|
2003-12-28 22:57:37 +01:00
|
|
|
* Checks that the given partial-index predicate is valid.
|
2001-07-16 07:07:00 +02:00
|
|
|
*
|
|
|
|
* This used to also constrain the form of the predicate to forms that
|
2014-05-06 18:12:18 +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-12-28 22:57:37 +01:00
|
|
|
CheckPredicate(Expr *predicate)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-07-17 23:53:01 +02:00
|
|
|
/*
|
Centralize the logic for detecting misplaced aggregates, window funcs, etc.
Formerly we relied on checking after-the-fact to see if an expression
contained aggregates, window functions, or sub-selects when it shouldn't.
This is grotty, easily forgotten (indeed, we had forgotten to teach
DefineIndex about rejecting window functions), and none too efficient
since it requires extra traversals of the parse tree. To improve matters,
define an enum type that classifies all SQL sub-expressions, store it in
ParseState to show what kind of expression we are currently parsing, and
make transformAggregateCall, transformWindowFuncCall, and transformSubLink
check the expression type and throw error if the type indicates the
construct is disallowed. This allows removal of a large number of ad-hoc
checks scattered around the code base. The enum type is sufficiently
fine-grained that we can still produce error messages of at least the
same specificity as before.
Bringing these error checks together revealed that we'd been none too
consistent about phrasing of the error messages, so standardize the wording
a bit.
Also, rewrite checking of aggregate arguments so that it requires only one
traversal of the arguments, rather than up to three as before.
In passing, clean up some more comments left over from add_missing_from
support, and annotate some tests that I think are dead code now that that's
gone. (I didn't risk actually removing said dead code, though.)
2012-08-10 17:35:33 +02:00
|
|
|
* transformExpr() should have already rejected subqueries, aggregates,
|
|
|
|
* and window functions, based on the EXPR_KIND_ for a 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
|
|
|
*/
|
2010-05-27 17:59:10 +02:00
|
|
|
if (CheckMutability(predicate))
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("functions in index predicate must be marked IMMUTABLE")));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2007-01-09 03:14:16 +01:00
|
|
|
/*
|
|
|
|
* Compute per-index-column information, including indexed column numbers
|
|
|
|
* or index expressions, opclasses, and indoptions.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
static void
|
2003-05-28 18:04:02 +02:00
|
|
|
ComputeIndexAttrs(IndexInfo *indexInfo,
|
2012-01-25 21:28:07 +01:00
|
|
|
Oid *typeOidP,
|
2011-02-08 22:04:18 +01:00
|
|
|
Oid *collationOidP,
|
2003-05-28 18:04:02 +02:00
|
|
|
Oid *classOidP,
|
2007-01-09 03:14:16 +01:00
|
|
|
int16 *colOptionP,
|
2003-05-28 18:04:02 +02:00
|
|
|
List *attList, /* list of IndexElem's */
|
2009-12-07 06:22:23 +01:00
|
|
|
List *exclusionOpNames,
|
2003-05-28 18:04:02 +02:00
|
|
|
Oid relId,
|
|
|
|
char *accessMethodName,
|
2004-05-05 06:48:48 +02:00
|
|
|
Oid accessMethodId,
|
2007-01-09 03:14:16 +01:00
|
|
|
bool amcanorder,
|
2004-05-05 06:48:48 +02:00
|
|
|
bool isconstraint)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2009-12-07 06:22:23 +01:00
|
|
|
ListCell *nextExclOp;
|
|
|
|
ListCell *lc;
|
|
|
|
int attn;
|
|
|
|
|
|
|
|
/* Allocate space for exclusion operator info, if needed */
|
|
|
|
if (exclusionOpNames)
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
int ncols = list_length(attList);
|
2009-12-07 06:22:23 +01:00
|
|
|
|
|
|
|
Assert(list_length(exclusionOpNames) == ncols);
|
|
|
|
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
|
|
|
|
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
|
|
|
|
indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * ncols);
|
|
|
|
nextExclOp = list_head(exclusionOpNames);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
nextExclOp = NULL;
|
1996-08-15 09:42:52 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* process attributeList
|
|
|
|
*/
|
2009-12-07 06:22:23 +01:00
|
|
|
attn = 0;
|
|
|
|
foreach(lc, attList)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2009-12-07 06:22:23 +01:00
|
|
|
IndexElem *attribute = (IndexElem *) lfirst(lc);
|
2003-05-28 18:04:02 +02:00
|
|
|
Oid atttype;
|
2011-02-08 22:04:18 +01:00
|
|
|
Oid attcollation;
|
2000-02-25 03:58:48 +01:00
|
|
|
|
2007-01-09 03:14:16 +01:00
|
|
|
/*
|
|
|
|
* Process the column-or-expression to be indexed.
|
|
|
|
*/
|
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))
|
2004-05-05 06:48:48 +02:00
|
|
|
{
|
|
|
|
/* difference in error message spellings is historical */
|
|
|
|
if (isconstraint)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("column \"%s\" named in key does not exist",
|
|
|
|
attribute->name)));
|
2004-05-05 06:48:48 +02:00
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("column \"%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;
|
2011-02-08 22:04:18 +01:00
|
|
|
attcollation = attform->attcollation;
|
2003-05-28 18:04:02 +02:00
|
|
|
ReleaseSysCache(atttuple);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Index expression */
|
2011-04-10 17:42:00 +02:00
|
|
|
Node *expr = attribute->expr;
|
2003-05-28 18:04:02 +02:00
|
|
|
|
2011-03-24 20:29:52 +01:00
|
|
|
Assert(expr != NULL);
|
|
|
|
atttype = exprType(expr);
|
|
|
|
attcollation = exprCollation(expr);
|
2003-05-28 18:04:02 +02:00
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Strip any top-level COLLATE clause. This ensures that we treat
|
2011-03-24 20:29:52 +01:00
|
|
|
* "x COLLATE y" and "(x COLLATE y)" alike.
|
2003-05-28 18:04:02 +02:00
|
|
|
*/
|
2011-03-24 20:29:52 +01:00
|
|
|
while (IsA(expr, CollateExpr))
|
|
|
|
expr = (Node *) ((CollateExpr *) expr)->arg;
|
|
|
|
|
|
|
|
if (IsA(expr, Var) &&
|
|
|
|
((Var *) expr)->varattno != InvalidAttrNumber)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* User wrote "(column)" or "(column COLLATE something)".
|
|
|
|
* Treat it like simple attribute anyway.
|
|
|
|
*/
|
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = ((Var *) expr)->varattno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
|
2011-03-24 20:29:52 +01:00
|
|
|
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
|
|
|
|
expr);
|
|
|
|
|
|
|
|
/*
|
Centralize the logic for detecting misplaced aggregates, window funcs, etc.
Formerly we relied on checking after-the-fact to see if an expression
contained aggregates, window functions, or sub-selects when it shouldn't.
This is grotty, easily forgotten (indeed, we had forgotten to teach
DefineIndex about rejecting window functions), and none too efficient
since it requires extra traversals of the parse tree. To improve matters,
define an enum type that classifies all SQL sub-expressions, store it in
ParseState to show what kind of expression we are currently parsing, and
make transformAggregateCall, transformWindowFuncCall, and transformSubLink
check the expression type and throw error if the type indicates the
construct is disallowed. This allows removal of a large number of ad-hoc
checks scattered around the code base. The enum type is sufficiently
fine-grained that we can still produce error messages of at least the
same specificity as before.
Bringing these error checks together revealed that we'd been none too
consistent about phrasing of the error messages, so standardize the wording
a bit.
Also, rewrite checking of aggregate arguments so that it requires only one
traversal of the arguments, rather than up to three as before.
In passing, clean up some more comments left over from add_missing_from
support, and annotate some tests that I think are dead code now that that's
gone. (I didn't risk actually removing said dead code, though.)
2012-08-10 17:35:33 +02:00
|
|
|
* transformExpr() should have already rejected subqueries,
|
|
|
|
* aggregates, and window functions, based on the EXPR_KIND_
|
|
|
|
* for an index expression.
|
2011-03-24 20:29:52 +01: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 (CheckMutability((Expr *) expr))
|
|
|
|
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
|
|
|
|
2012-01-25 21:28:07 +01:00
|
|
|
typeOidP[attn] = atttype;
|
|
|
|
|
2011-02-08 22:04:18 +01:00
|
|
|
/*
|
2011-03-20 01:29:08 +01:00
|
|
|
* Apply collation override if any
|
2011-02-08 22:04:18 +01:00
|
|
|
*/
|
|
|
|
if (attribute->collation)
|
2011-03-20 01:29:08 +01:00
|
|
|
attcollation = get_collation_oid(attribute->collation, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check we have a collation iff it's a collatable type. The only
|
|
|
|
* expected failures here are (1) COLLATE applied to a noncollatable
|
2011-04-10 17:42:00 +02:00
|
|
|
* type, or (2) index expression had an unresolved collation. But we
|
|
|
|
* might as well code this to be a complete consistency check.
|
2011-03-20 01:29:08 +01:00
|
|
|
*/
|
|
|
|
if (type_is_collatable(atttype))
|
|
|
|
{
|
|
|
|
if (!OidIsValid(attcollation))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INDETERMINATE_COLLATION),
|
2011-03-22 21:55:32 +01:00
|
|
|
errmsg("could not determine which collation to use for index expression"),
|
2011-03-20 01:29:08 +01:00
|
|
|
errhint("Use the COLLATE clause to set the collation explicitly.")));
|
|
|
|
}
|
|
|
|
else
|
2011-02-08 22:04:18 +01:00
|
|
|
{
|
2011-03-20 01:29:08 +01:00
|
|
|
if (OidIsValid(attcollation))
|
2011-02-08 22:04:18 +01:00
|
|
|
ereport(ERROR,
|
2011-03-20 01:29:08 +01:00
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
2011-02-08 22:04:18 +01:00
|
|
|
errmsg("collations are not supported by type %s",
|
|
|
|
format_type_be(atttype))));
|
|
|
|
}
|
2011-03-20 01:29:08 +01:00
|
|
|
|
2011-02-08 22:04:18 +01:00
|
|
|
collationOidP[attn] = attcollation;
|
|
|
|
|
2007-01-09 03:14:16 +01:00
|
|
|
/*
|
|
|
|
* Identify the opclass to use.
|
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
classOidP[attn] = GetIndexOpClass(attribute->opclass,
|
|
|
|
atttype,
|
|
|
|
accessMethodName,
|
|
|
|
accessMethodId);
|
2007-01-09 03:14:16 +01:00
|
|
|
|
2009-12-07 06:22:23 +01:00
|
|
|
/*
|
|
|
|
* Identify the exclusion operator, if any.
|
|
|
|
*/
|
|
|
|
if (nextExclOp)
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
List *opname = (List *) lfirst(nextExclOp);
|
|
|
|
Oid opid;
|
|
|
|
Oid opfamily;
|
|
|
|
int strat;
|
2009-12-07 06:22:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the operator --- it must accept the column datatype
|
|
|
|
* without runtime coercion (but binary compatibility is OK)
|
|
|
|
*/
|
|
|
|
opid = compatible_oper_opid(opname, atttype, atttype, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only allow commutative operators to be used in exclusion
|
|
|
|
* constraints. If X conflicts with Y, but Y does not conflict
|
|
|
|
* with X, bad things will happen.
|
|
|
|
*/
|
|
|
|
if (get_commutator(opid) != opid)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("operator %s is not commutative",
|
|
|
|
format_operator(opid)),
|
|
|
|
errdetail("Only commutative operators can be used in exclusion constraints.")));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Operator must be a member of the right opfamily, too
|
|
|
|
*/
|
|
|
|
opfamily = get_opclass_family(classOidP[attn]);
|
|
|
|
strat = get_op_opfamily_strategy(opid, opfamily);
|
|
|
|
if (strat == 0)
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
HeapTuple opftuple;
|
2009-12-07 06:22:23 +01:00
|
|
|
Form_pg_opfamily opfform;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* attribute->opclass might not explicitly name the opfamily,
|
|
|
|
* so fetch the name of the selected opfamily for use in the
|
|
|
|
* error message.
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
opftuple = SearchSysCache1(OPFAMILYOID,
|
|
|
|
ObjectIdGetDatum(opfamily));
|
2009-12-07 06:22:23 +01:00
|
|
|
if (!HeapTupleIsValid(opftuple))
|
|
|
|
elog(ERROR, "cache lookup failed for opfamily %u",
|
|
|
|
opfamily);
|
|
|
|
opfform = (Form_pg_opfamily) GETSTRUCT(opftuple);
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("operator %s is not a member of operator family \"%s\"",
|
|
|
|
format_operator(opid),
|
|
|
|
NameStr(opfform->opfname)),
|
|
|
|
errdetail("The exclusion operator must be related to the index operator class for the constraint.")));
|
|
|
|
}
|
|
|
|
|
|
|
|
indexInfo->ii_ExclusionOps[attn] = opid;
|
|
|
|
indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
|
|
|
|
indexInfo->ii_ExclusionStrats[attn] = strat;
|
|
|
|
nextExclOp = lnext(nextExclOp);
|
|
|
|
}
|
|
|
|
|
2007-01-09 03:14:16 +01:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Set up the per-column options (indoption field). For now, this is
|
|
|
|
* zero for any un-ordered index, while ordered indexes have DESC and
|
|
|
|
* NULLS FIRST/LAST options.
|
2007-01-09 03:14:16 +01:00
|
|
|
*/
|
|
|
|
colOptionP[attn] = 0;
|
|
|
|
if (amcanorder)
|
|
|
|
{
|
|
|
|
/* default ordering is ASC */
|
|
|
|
if (attribute->ordering == SORTBY_DESC)
|
|
|
|
colOptionP[attn] |= INDOPTION_DESC;
|
|
|
|
/* default null ordering is LAST for ASC, FIRST for DESC */
|
|
|
|
if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
|
|
|
|
{
|
|
|
|
if (attribute->ordering == SORTBY_DESC)
|
|
|
|
colOptionP[attn] |= INDOPTION_NULLS_FIRST;
|
|
|
|
}
|
|
|
|
else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
|
|
|
|
colOptionP[attn] |= INDOPTION_NULLS_FIRST;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* index AM does not support ordering */
|
|
|
|
if (attribute->ordering != SORTBY_DEFAULT)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("access method \"%s\" does not support ASC/DESC options",
|
|
|
|
accessMethodName)));
|
|
|
|
if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
|
|
|
|
accessMethodName)));
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/*
|
2005-10-15 04:49:52 +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
|
2003-05-28 18:04:02 +02:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Release 7.1 removes lztext_ops, so suppress that too for a while. tgl
|
|
|
|
* 2000/07/30
|
2003-05-28 18:04:02 +02:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
|
2014-05-06 18:12:18 +02:00
|
|
|
* too for awhile. I'm starting to think we need a better approach. tgl
|
2005-10-15 04:49:52 +02:00
|
|
|
* 2000/10/01
|
2003-11-12 22:15:59 +01:00
|
|
|
*
|
2004-08-04 23:34:35 +02:00
|
|
|
* Release 8.0 removes bigbox_ops (which was dead code for a long while
|
2003-11-12 22:15:59 +01:00
|
|
|
* anyway). tgl 2003/11/11
|
2003-05-28 18:04:02 +02:00
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
if (list_length(opclass) == 1)
|
2003-05-28 18:04:02 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
char *claname = strVal(linitial(opclass));
|
2003-05-28 18:04:02 +02:00
|
|
|
|
|
|
|
if (strcmp(claname, "network_ops") == 0 ||
|
|
|
|
strcmp(claname, "timespan_ops") == 0 ||
|
|
|
|
strcmp(claname, "datetime_ops") == 0 ||
|
|
|
|
strcmp(claname, "lztext_ops") == 0 ||
|
2003-11-12 22:15:59 +01:00
|
|
|
strcmp(claname, "timestamp_ops") == 0 ||
|
|
|
|
strcmp(claname, "bigbox_ops") == 0)
|
2003-05-28 18:04:02 +02:00
|
|
|
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
|
|
|
|
2013-01-26 19:24:50 +01:00
|
|
|
namespaceId = LookupExplicitNamespace(schemaname, false);
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache3(CLAAMNAMENSP,
|
|
|
|
ObjectIdGetDatum(accessMethodId),
|
|
|
|
PointerGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
2002-04-17 22:57:57 +02:00
|
|
|
}
|
|
|
|
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)));
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
|
2002-04-17 22:57:57 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Verify that the index operator class accepts this datatype. Note we
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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),
|
2005-10-15 04:49:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-02-10 20:01:12 +01:00
|
|
|
/*
|
|
|
|
* GetDefaultOpClass
|
|
|
|
*
|
|
|
|
* Given the OIDs of a datatype and an access method, find the default
|
2014-05-06 18:12:18 +02:00
|
|
|
* operator class, if any. Returns InvalidOid if there is none.
|
2006-02-10 20:01:12 +01:00
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
GetDefaultOpClass(Oid type_id, Oid am_id)
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
2006-12-23 01:43:13 +01:00
|
|
|
Oid result = InvalidOid;
|
2001-08-21 18:36:06 +02:00
|
|
|
int nexact = 0;
|
|
|
|
int ncompatible = 0;
|
2006-12-23 01:43:13 +01:00
|
|
|
int ncompatiblepreferred = 0;
|
2006-02-10 20:01:12 +01:00
|
|
|
Relation rel;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
2009-06-11 16:49:15 +02:00
|
|
|
TYPCATEGORY tcategory;
|
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 */
|
2006-02-10 20:01:12 +01:00
|
|
|
type_id = getBaseType(type_id);
|
2002-08-16 22:55:09 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
tcategory = TypeCategory(type_id);
|
|
|
|
|
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).
|
|
|
|
*
|
2006-12-23 01:43:13 +01:00
|
|
|
* We could find more than one binary-compatible match. If just one is
|
|
|
|
* for a preferred type, use that one; otherwise we fail, forcing the user
|
|
|
|
* to specify which one he wants. (The preferred-type special case is a
|
|
|
|
* kluge for varchar: it's binary-compatible to both text and bpchar, so
|
|
|
|
* we need a tiebreaker.) If we find more than one exact match, then
|
|
|
|
* someone put bogus entries in pg_opclass.
|
2000-04-25 04:45:54 +02:00
|
|
|
*/
|
2006-02-10 20:01:12 +01:00
|
|
|
rel = heap_open(OperatorClassRelationId, AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
2006-12-23 01:43:13 +01:00
|
|
|
Anum_pg_opclass_opcmethod,
|
2006-02-10 20:01:12 +01:00
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(am_id));
|
|
|
|
|
|
|
|
scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2006-02-10 20:01:12 +01:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
2000-04-25 04:45:54 +02:00
|
|
|
{
|
2006-02-10 20:01:12 +01:00
|
|
|
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* ignore altogether if not a default opclass */
|
|
|
|
if (!opclass->opcdefault)
|
|
|
|
continue;
|
|
|
|
if (opclass->opcintype == type_id)
|
|
|
|
{
|
|
|
|
nexact++;
|
|
|
|
result = HeapTupleGetOid(tup);
|
|
|
|
}
|
|
|
|
else if (nexact == 0 &&
|
|
|
|
IsBinaryCoercible(type_id, opclass->opcintype))
|
2000-04-25 04:45:54 +02:00
|
|
|
{
|
2006-12-23 01:43:13 +01:00
|
|
|
if (IsPreferredType(tcategory, opclass->opcintype))
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
2006-12-23 01:43:13 +01:00
|
|
|
ncompatiblepreferred++;
|
|
|
|
result = HeapTupleGetOid(tup);
|
2001-08-21 18:36:06 +02:00
|
|
|
}
|
2006-12-23 01:43:13 +01:00
|
|
|
else if (ncompatiblepreferred == 0)
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
|
|
|
ncompatible++;
|
2006-12-23 01:43:13 +01:00
|
|
|
result = HeapTupleGetOid(tup);
|
2001-08-21 18:36:06 +02:00
|
|
|
}
|
2000-04-25 04:45:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-10 20:01:12 +01:00
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* raise error if pg_opclass contains inconsistent data */
|
|
|
|
if (nexact > 1)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("there are multiple default operator classes for data type %s",
|
2006-02-10 20:01:12 +01:00
|
|
|
format_type_be(type_id))));
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
if (nexact == 1 ||
|
|
|
|
ncompatiblepreferred == 1 ||
|
|
|
|
(ncompatiblepreferred == 0 && ncompatible == 1))
|
|
|
|
return result;
|
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
|
|
|
}
|
|
|
|
|
2004-05-05 06:48:48 +02:00
|
|
|
/*
|
2004-06-10 19:56:03 +02:00
|
|
|
* makeObjectName()
|
|
|
|
*
|
|
|
|
* Create a name for an implicitly created index, sequence, constraint, etc.
|
|
|
|
*
|
|
|
|
* The parameters are typically: the original table name, the original field
|
2014-05-06 18:12:18 +02:00
|
|
|
* name, and a "type" string (such as "seq" or "pkey"). The field name
|
2004-06-10 19:56:03 +02:00
|
|
|
* and/or type can be NULL if not relevant.
|
|
|
|
*
|
|
|
|
* The result is a palloc'd string.
|
|
|
|
*
|
|
|
|
* The basic result we want is "name1_name2_label", omitting "_name2" or
|
|
|
|
* "_label" when those parameters are NULL. However, we must generate
|
|
|
|
* a name with less than NAMEDATALEN characters! So, we truncate one or
|
2014-05-06 18:12:18 +02:00
|
|
|
* both names if necessary to make a short-enough string. The label part
|
2004-06-10 19:56:03 +02:00
|
|
|
* is never truncated (so it had better be reasonably short).
|
|
|
|
*
|
|
|
|
* The caller is responsible for checking uniqueness of the generated
|
|
|
|
* name and retrying as needed; retrying will be done by altering the
|
|
|
|
* "label" string (which is why we never truncate that part).
|
2004-05-05 06:48:48 +02:00
|
|
|
*/
|
2004-06-10 19:56:03 +02:00
|
|
|
char *
|
|
|
|
makeObjectName(const char *name1, const char *name2, const char *label)
|
2004-05-05 06:48:48 +02:00
|
|
|
{
|
2004-06-10 19:56:03 +02:00
|
|
|
char *name;
|
|
|
|
int overhead = 0; /* chars needed for label and underscores */
|
|
|
|
int availchars; /* chars available for name(s) */
|
|
|
|
int name1chars; /* chars allocated to name1 */
|
|
|
|
int name2chars; /* chars allocated to name2 */
|
|
|
|
int ndx;
|
|
|
|
|
|
|
|
name1chars = strlen(name1);
|
|
|
|
if (name2)
|
|
|
|
{
|
|
|
|
name2chars = strlen(name2);
|
|
|
|
overhead++; /* allow for separating underscore */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
name2chars = 0;
|
|
|
|
if (label)
|
|
|
|
overhead += strlen(label) + 1;
|
|
|
|
|
|
|
|
availchars = NAMEDATALEN - 1 - overhead;
|
|
|
|
Assert(availchars > 0); /* else caller chose a bad label */
|
2004-05-05 06:48:48 +02:00
|
|
|
|
|
|
|
/*
|
2004-06-10 19:56:03 +02:00
|
|
|
* If we must truncate, preferentially truncate the longer name. This
|
2005-10-15 04:49:52 +02:00
|
|
|
* logic could be expressed without a loop, but it's simple and obvious as
|
|
|
|
* a loop.
|
2004-05-05 06:48:48 +02:00
|
|
|
*/
|
2004-06-10 19:56:03 +02:00
|
|
|
while (name1chars + name2chars > availchars)
|
|
|
|
{
|
|
|
|
if (name1chars > name2chars)
|
|
|
|
name1chars--;
|
|
|
|
else
|
|
|
|
name2chars--;
|
|
|
|
}
|
|
|
|
|
2005-06-21 02:35:05 +02:00
|
|
|
name1chars = pg_mbcliplen(name1, name1chars, name1chars);
|
2004-06-10 19:56:03 +02:00
|
|
|
if (name2)
|
|
|
|
name2chars = pg_mbcliplen(name2, name2chars, name2chars);
|
|
|
|
|
|
|
|
/* Now construct the string using the chosen lengths */
|
|
|
|
name = palloc(name1chars + name2chars + overhead + 1);
|
|
|
|
memcpy(name, name1, name1chars);
|
|
|
|
ndx = name1chars;
|
|
|
|
if (name2)
|
|
|
|
{
|
|
|
|
name[ndx++] = '_';
|
|
|
|
memcpy(name + ndx, name2, name2chars);
|
|
|
|
ndx += name2chars;
|
|
|
|
}
|
|
|
|
if (label)
|
|
|
|
{
|
|
|
|
name[ndx++] = '_';
|
|
|
|
strcpy(name + ndx, label);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
name[ndx] = '\0';
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select a nonconflicting name for a new relation. This is ordinarily
|
|
|
|
* used to choose index names (which is why it's here) but it can also
|
|
|
|
* be used for sequences, or any autogenerated relation kind.
|
|
|
|
*
|
|
|
|
* name1, name2, and label are used the same way as for makeObjectName(),
|
|
|
|
* except that the label can't be NULL; digits will be appended to the label
|
|
|
|
* if needed to create a name that is unique within the specified namespace.
|
|
|
|
*
|
|
|
|
* Note: it is theoretically possible to get a collision anyway, if someone
|
|
|
|
* else chooses the same name concurrently. This is fairly unlikely to be
|
|
|
|
* a problem in practice, especially if one is holding an exclusive lock on
|
|
|
|
* the relation identified by name1. However, if choosing multiple names
|
|
|
|
* within a single command, you'd better create the new object and do
|
|
|
|
* CommandCounterIncrement before choosing the next one!
|
|
|
|
*
|
|
|
|
* Returns a palloc'd string.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
ChooseRelationName(const char *name1, const char *name2,
|
2009-07-16 08:33:46 +02:00
|
|
|
const char *label, Oid namespaceid)
|
2004-06-10 19:56:03 +02:00
|
|
|
{
|
|
|
|
int pass = 0;
|
|
|
|
char *relname = NULL;
|
|
|
|
char modlabel[NAMEDATALEN];
|
|
|
|
|
|
|
|
/* try the unmodified label first */
|
|
|
|
StrNCpy(modlabel, label, sizeof(modlabel));
|
2004-05-05 06:48:48 +02:00
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
2004-06-10 19:56:03 +02:00
|
|
|
relname = makeObjectName(name1, name2, modlabel);
|
2004-05-05 06:48:48 +02:00
|
|
|
|
2009-07-16 08:33:46 +02:00
|
|
|
if (!OidIsValid(get_relname_relid(relname, namespaceid)))
|
2004-05-05 06:48:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* found a conflict, so try a new name component */
|
2004-06-10 19:56:03 +02:00
|
|
|
pfree(relname);
|
|
|
|
snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
|
2004-05-05 06:48:48 +02:00
|
|
|
}
|
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
return relname;
|
2004-05-05 06:48:48 +02:00
|
|
|
}
|
|
|
|
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
/*
|
|
|
|
* Select the name to be used for an index.
|
|
|
|
*
|
|
|
|
* The argument list is pretty ad-hoc :-(
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
static char *
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
ChooseIndexName(const char *tabname, Oid namespaceId,
|
|
|
|
List *colnames, List *exclusionOpNames,
|
|
|
|
bool primary, bool isconstraint)
|
|
|
|
{
|
|
|
|
char *indexname;
|
|
|
|
|
|
|
|
if (primary)
|
|
|
|
{
|
|
|
|
/* the primary key's name does not depend on the specific column(s) */
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
NULL,
|
|
|
|
"pkey",
|
|
|
|
namespaceId);
|
|
|
|
}
|
|
|
|
else if (exclusionOpNames != NIL)
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
2010-03-22 16:24:11 +01:00
|
|
|
"excl",
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
namespaceId);
|
|
|
|
}
|
|
|
|
else if (isconstraint)
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
|
|
|
"key",
|
|
|
|
namespaceId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
|
|
|
"idx",
|
|
|
|
namespaceId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return indexname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate "name2" for a new index given the list of column names for it
|
|
|
|
* (as produced by ChooseIndexColumnNames). This will be passed to
|
|
|
|
* ChooseRelationName along with the parent table name and a suitable label.
|
|
|
|
*
|
|
|
|
* We know that less than NAMEDATALEN characters will actually be used,
|
|
|
|
* so we can truncate the result once we've generated that many.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
ChooseIndexNameAddition(List *colnames)
|
|
|
|
{
|
|
|
|
char buf[NAMEDATALEN * 2];
|
|
|
|
int buflen = 0;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
foreach(lc, colnames)
|
|
|
|
{
|
|
|
|
const char *name = (const char *) lfirst(lc);
|
|
|
|
|
|
|
|
if (buflen > 0)
|
2010-02-26 03:01:40 +01:00
|
|
|
buf[buflen++] = '_'; /* insert _ between names */
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we have buflen <= NAMEDATALEN. name should be less
|
|
|
|
* than NAMEDATALEN already, but use strlcpy for paranoia.
|
|
|
|
*/
|
|
|
|
strlcpy(buf + buflen, name, NAMEDATALEN);
|
|
|
|
buflen += strlen(buf + buflen);
|
|
|
|
if (buflen >= NAMEDATALEN)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return pstrdup(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select the actual names to be used for the columns of an index, given the
|
2014-05-06 18:12:18 +02:00
|
|
|
* list of IndexElems for the columns. This is mostly about ensuring the
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
* names are unique so we don't get a conflicting-attribute-names error.
|
|
|
|
*
|
|
|
|
* Returns a List of plain strings (char *, not String nodes).
|
|
|
|
*/
|
Avoid pre-determining index names during CREATE TABLE LIKE parsing.
Formerly, when trying to copy both indexes and comments, CREATE TABLE LIKE
had to pre-assign names to indexes that had comments, because it made up an
explicit CommentStmt command to apply the comment and so it had to know the
name for the index. This creates bad interactions with other indexes, as
shown in bug #6734 from Daniele Varrazzo: the preassignment logic couldn't
take any other indexes into account so it could choose a conflicting name.
To fix, add a field to IndexStmt that allows it to carry a comment to be
assigned to the new index. (This isn't a user-exposed feature of CREATE
INDEX, only an internal option.) Now we don't need preassignment of index
names in any situation.
I also took the opportunity to refactor DefineIndex to accept the IndexStmt
as such, rather than passing all its fields individually in a mile-long
parameter list.
Back-patch to 9.2, but no further, because it seems too dangerous to change
IndexStmt or DefineIndex's API in released branches. The bug exists back
to 9.0 where CREATE TABLE LIKE grew the ability to copy comments, but given
the lack of prior complaints we'll just let it go unfixed before 9.2.
2012-07-16 19:25:18 +02:00
|
|
|
static List *
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
ChooseIndexColumnNames(List *indexElems)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, indexElems)
|
|
|
|
{
|
|
|
|
IndexElem *ielem = (IndexElem *) lfirst(lc);
|
|
|
|
const char *origname;
|
|
|
|
const char *curname;
|
|
|
|
int i;
|
|
|
|
char buf[NAMEDATALEN];
|
|
|
|
|
|
|
|
/* Get the preliminary name from the IndexElem */
|
|
|
|
if (ielem->indexcolname)
|
2010-02-26 03:01:40 +01:00
|
|
|
origname = ielem->indexcolname; /* caller-specified name */
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
else if (ielem->name)
|
2010-02-26 03:01:40 +01:00
|
|
|
origname = ielem->name; /* simple column reference */
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
else
|
2010-02-26 03:01:40 +01:00
|
|
|
origname = "expr"; /* default name for expression */
|
Adjust naming of indexes and their columns per recent discussion.
Index expression columns are now named after the FigureColname result for
their expressions, rather than always being "pg_expression_N". Digits are
appended to this name if needed to make the column name unique within the
index. (That happens for regular columns too, thus fixing the old problem
that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes
there was no real reason to do such a thing, but now maybe there is.)
Default names for indexes and associated constraints now include the column
names of all their columns, not only the first one as in previous practice.
(Of course, this will be truncated as needed to fit in NAMEDATALEN. Also,
pkey indexes retain the historical behavior of not naming specific columns
at all.)
An example of the results:
regression=# create table foo (f1 int, f2 text,
regression(# exclude (f1 with =, lower(f2) with =));
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo"
CREATE TABLE
regression=# \d foo_f1_lower_exclusion
Index "public.foo_f1_lower_exclusion"
Column | Type | Definition
--------+---------+------------
f1 | integer | f1
lower | text | lower(f2)
btree, for table "public.foo"
2009-12-23 03:35:25 +01:00
|
|
|
|
|
|
|
/* If it conflicts with any previous column, tweak it */
|
|
|
|
curname = origname;
|
|
|
|
for (i = 1;; i++)
|
|
|
|
{
|
|
|
|
ListCell *lc2;
|
|
|
|
char nbuf[32];
|
|
|
|
int nlen;
|
|
|
|
|
|
|
|
foreach(lc2, result)
|
|
|
|
{
|
|
|
|
if (strcmp(curname, (char *) lfirst(lc2)) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lc2 == NULL)
|
|
|
|
break; /* found nonconflicting name */
|
|
|
|
|
|
|
|
sprintf(nbuf, "%d", i);
|
|
|
|
|
|
|
|
/* Ensure generated names are shorter than NAMEDATALEN */
|
|
|
|
nlen = pg_mbcliplen(origname, strlen(origname),
|
|
|
|
NAMEDATALEN - 1 - strlen(nbuf));
|
|
|
|
memcpy(buf, origname, nlen);
|
|
|
|
strcpy(buf + nlen, nbuf);
|
|
|
|
curname = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And attach to the result list */
|
|
|
|
result = lappend(result, pstrdup(curname));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2000-02-18 10:30:20 +01:00
|
|
|
/*
|
2002-08-29 17:56:20 +02:00
|
|
|
* ReindexIndex
|
2005-06-22 23:14:31 +02:00
|
|
|
* Recreate a specific index.
|
2000-02-18 10:30:20 +01:00
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexIndex(RangeVar *indexRelation)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid indOid;
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
Oid heapOid = InvalidOid;
|
|
|
|
|
|
|
|
/* lock level used here should match index lock reindex_index() */
|
|
|
|
indOid = RangeVarGetRelidExtended(indexRelation, AccessExclusiveLock,
|
|
|
|
false, false,
|
|
|
|
RangeVarCallbackForReindexIndex,
|
|
|
|
(void *) &heapOid);
|
|
|
|
|
|
|
|
reindex_index(indOid, false);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return indOid;
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check permissions on table before acquiring relation lock; also lock
|
|
|
|
* the heap before the RangeVarGetRelidExtended takes the index lock, to avoid
|
|
|
|
* deadlocks.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
RangeVarCallbackForReindexIndex(const RangeVar *relation,
|
|
|
|
Oid relId, Oid oldRelId, void *arg)
|
|
|
|
{
|
|
|
|
char relkind;
|
|
|
|
Oid *heapOid = (Oid *) arg;
|
2000-11-08 23:10:03 +01:00
|
|
|
|
2011-07-09 04:19:30 +02:00
|
|
|
/*
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
* If we previously locked some other index's heap, and the name we're
|
|
|
|
* looking up no longer refers to that relation, release the now-useless
|
|
|
|
* lock.
|
2011-07-09 04:19:30 +02:00
|
|
|
*/
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
if (relId != oldRelId && OidIsValid(oldRelId))
|
|
|
|
{
|
|
|
|
/* lock level here should match reindex_index() heap lock */
|
|
|
|
UnlockRelationOid(*heapOid, ShareLock);
|
|
|
|
*heapOid = InvalidOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the relation does not exist, there's nothing more to do. */
|
|
|
|
if (!OidIsValid(relId))
|
|
|
|
return;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
/*
|
2012-06-10 21:20:04 +02:00
|
|
|
* If the relation does exist, check whether it's an index. But note that
|
|
|
|
* the relation might have been dropped between the time we did the name
|
2014-05-06 18:12:18 +02:00
|
|
|
* lookup and now. In that case, there's nothing to do.
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
*/
|
|
|
|
relkind = get_rel_relkind(relId);
|
|
|
|
if (!relkind)
|
|
|
|
return;
|
|
|
|
if (relkind != RELKIND_INDEX)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
errmsg("\"%s\" is not an index", relation->relname)));
|
2002-03-26 20:17:02 +01:00
|
|
|
|
2003-09-24 20:54:02 +02:00
|
|
|
/* Check permissions */
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
if (!pg_class_ownercheck(relId, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
/* Lock heap before index to avoid deadlock. */
|
|
|
|
if (relId != oldRelId)
|
|
|
|
{
|
|
|
|
/*
|
2012-06-10 21:20:04 +02:00
|
|
|
* Lock level here should match reindex_index() heap lock. If the OID
|
|
|
|
* isn't valid, it means the index as concurrently dropped, which is
|
|
|
|
* not a problem for us; just return normally.
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
*/
|
|
|
|
*heapOid = IndexGetRelation(relId, true);
|
|
|
|
if (OidIsValid(*heapOid))
|
|
|
|
LockRelationOid(*heapOid, ShareLock);
|
|
|
|
}
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexTable
|
2005-06-22 23:14:31 +02:00
|
|
|
* Recreate all indexes of a table (and of its toast table, if any)
|
2000-02-18 10:30:20 +01:00
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexTable(RangeVar *relation)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid heapOid;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2011-07-09 04:19:30 +02:00
|
|
|
/* The lock level used here should match reindex_relation(). */
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false,
|
2011-12-21 21:17:28 +01:00
|
|
|
RangeVarCallbackOwnsTable, NULL);
|
2002-10-22 00:06:20 +02:00
|
|
|
|
2013-07-31 00:36:52 +02:00
|
|
|
if (!reindex_relation(heapOid,
|
|
|
|
REINDEX_REL_PROCESS_TOAST |
|
|
|
|
REINDEX_REL_CHECK_CONSTRAINTS))
|
2003-10-02 08:34:04 +02:00
|
|
|
ereport(NOTICE,
|
2003-09-24 20:54:02 +02:00
|
|
|
(errmsg("table \"%s\" has no indexes",
|
2003-07-20 23:56:35 +02:00
|
|
|
relation->relname)));
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return heapOid;
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexDatabase
|
|
|
|
* Recreate indexes of a database.
|
2003-09-24 20:54:02 +02:00
|
|
|
*
|
|
|
|
* To reduce the probability of deadlocks, each table is reindexed in a
|
|
|
|
* separate transaction, so we can release the lock on it right away.
|
2007-03-13 01:33:44 +01:00
|
|
|
* That means this must not be called within a user transaction block!
|
2000-02-18 10:30:20 +01:00
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
Relation relationRelation;
|
2000-04-12 19:17:23 +02:00
|
|
|
HeapScanDesc scan;
|
2004-08-29 07:07:03 +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;
|
2004-08-29 07:07:03 +02:00
|
|
|
List *relids = NIL;
|
|
|
|
ListCell *l;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2005-06-22 23:14:31 +02:00
|
|
|
AssertArg(databaseName);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2005-06-22 23:14:31 +02:00
|
|
|
if (strcmp(databaseName, 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-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
2005-06-22 23:14:31 +02:00
|
|
|
databaseName);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Create a memory context that will survive forced transaction commits we
|
|
|
|
* do below. Since it is a child of PortalContext, 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
|
|
|
*/
|
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
|
|
|
|
2003-09-24 20:54:02 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We always want to reindex pg_class first. This ensures that if there
|
|
|
|
* is any corruption in pg_class' indexes, they will be fixed before we
|
|
|
|
* process any other tables. This is critical because reindexing itself
|
|
|
|
* will try to update pg_class.
|
2003-09-24 20:54:02 +02:00
|
|
|
*/
|
2005-06-22 23:14:31 +02:00
|
|
|
if (do_system)
|
|
|
|
{
|
|
|
|
old = MemoryContextSwitchTo(private_context);
|
|
|
|
relids = lappend_oid(relids, RelationRelationId);
|
|
|
|
MemoryContextSwitchTo(old);
|
|
|
|
}
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2001-11-20 03:46:13 +01:00
|
|
|
/*
|
|
|
|
* Scan pg_class to build a list of the relations we need to reindex.
|
2003-09-24 20:54:02 +02:00
|
|
|
*
|
2013-07-05 21:25:51 +02:00
|
|
|
* We only consider plain relations and materialized views here (toast
|
|
|
|
* rels will be processed indirectly by reindex_relation).
|
2001-11-20 03:46:13 +01:00
|
|
|
*/
|
2005-04-14 22:03:27 +02:00
|
|
|
relationRelation = heap_open(RelationRelationId, AccessShareLock);
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
scan = heap_beginscan_catalog(relationRelation, 0, NULL);
|
2002-05-21 01:51:44 +02:00
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2003-09-24 20:54:02 +02:00
|
|
|
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
|
Refine our definition of what constitutes a system relation.
Although user-defined relations can't be directly created in
pg_catalog, it's possible for them to end up there, because you can
create them in some other schema and then use ALTER TABLE .. SET SCHEMA
to move them there. Previously, such relations couldn't afterwards
be manipulated, because IsSystemRelation()/IsSystemClass() rejected
all attempts to modify objects in the pg_catalog schema, regardless
of their origin. With this patch, they now reject only those
objects in pg_catalog which were created at initdb-time, allowing
most operations on user-created tables in pg_catalog to proceed
normally.
This patch also adds new functions IsCatalogRelation() and
IsCatalogClass(), which is similar to IsSystemRelation() and
IsSystemClass() but with a slightly narrower definition: only TOAST
tables of system catalogs are included, rather than *all* TOAST tables.
This is currently used only for making decisions about when
invalidation messages need to be sent, but upcoming logical decoding
patches will find other uses for this information.
Andres Freund, with some modifications by me.
2013-11-29 02:57:20 +01:00
|
|
|
Oid relid = HeapTupleGetOid(tuple);
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2013-03-04 01:23:31 +01:00
|
|
|
if (classtuple->relkind != RELKIND_RELATION &&
|
|
|
|
classtuple->relkind != RELKIND_MATVIEW)
|
2003-09-24 20:54:02 +02:00
|
|
|
continue;
|
2002-08-29 17:56:20 +02:00
|
|
|
|
2007-09-10 23:59:37 +02:00
|
|
|
/* Skip temp tables of other backends; we can't reindex them at all */
|
2010-12-13 18:34:26 +01:00
|
|
|
if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
|
2009-04-01 00:12:48 +02:00
|
|
|
!isTempNamespace(classtuple->relnamespace))
|
2007-09-10 23:59:37 +02:00
|
|
|
continue;
|
|
|
|
|
2005-06-22 23:14:31 +02:00
|
|
|
/* Check user/system classification, and optionally skip */
|
Refine our definition of what constitutes a system relation.
Although user-defined relations can't be directly created in
pg_catalog, it's possible for them to end up there, because you can
create them in some other schema and then use ALTER TABLE .. SET SCHEMA
to move them there. Previously, such relations couldn't afterwards
be manipulated, because IsSystemRelation()/IsSystemClass() rejected
all attempts to modify objects in the pg_catalog schema, regardless
of their origin. With this patch, they now reject only those
objects in pg_catalog which were created at initdb-time, allowing
most operations on user-created tables in pg_catalog to proceed
normally.
This patch also adds new functions IsCatalogRelation() and
IsCatalogClass(), which is similar to IsSystemRelation() and
IsSystemClass() but with a slightly narrower definition: only TOAST
tables of system catalogs are included, rather than *all* TOAST tables.
This is currently used only for making decisions about when
invalidation messages need to be sent, but upcoming logical decoding
patches will find other uses for this information.
Andres Freund, with some modifications by me.
2013-11-29 02:57:20 +01:00
|
|
|
if (IsSystemClass(relid, classtuple))
|
2005-06-22 23:14:31 +02:00
|
|
|
{
|
|
|
|
if (!do_system)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2005-06-22 23:14:31 +02:00
|
|
|
if (!do_user)
|
2000-02-18 10:30:20 +01:00
|
|
|
continue;
|
|
|
|
}
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2005-04-14 03:38:22 +02:00
|
|
|
if (HeapTupleGetOid(tuple) == RelationRelationId)
|
2003-09-24 20:54:02 +02:00
|
|
|
continue; /* got it already */
|
|
|
|
|
|
|
|
old = MemoryContextSwitchTo(private_context);
|
Refine our definition of what constitutes a system relation.
Although user-defined relations can't be directly created in
pg_catalog, it's possible for them to end up there, because you can
create them in some other schema and then use ALTER TABLE .. SET SCHEMA
to move them there. Previously, such relations couldn't afterwards
be manipulated, because IsSystemRelation()/IsSystemClass() rejected
all attempts to modify objects in the pg_catalog schema, regardless
of their origin. With this patch, they now reject only those
objects in pg_catalog which were created at initdb-time, allowing
most operations on user-created tables in pg_catalog to proceed
normally.
This patch also adds new functions IsCatalogRelation() and
IsCatalogClass(), which is similar to IsSystemRelation() and
IsSystemClass() but with a slightly narrower definition: only TOAST
tables of system catalogs are included, rather than *all* TOAST tables.
This is currently used only for making decisions about when
invalidation messages need to be sent, but upcoming logical decoding
patches will find other uses for this information.
Andres Freund, with some modifications by me.
2013-11-29 02:57:20 +01:00
|
|
|
relids = lappend_oid(relids, relid);
|
2003-09-24 20:54:02 +02:00
|
|
|
MemoryContextSwitchTo(old);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(relationRelation, AccessShareLock);
|
|
|
|
|
2001-11-20 03:46:13 +01:00
|
|
|
/* Now reindex each rel in a separate transaction */
|
2008-05-12 22:02:02 +02:00
|
|
|
PopActiveSnapshot();
|
2003-05-14 05:26:03 +02:00
|
|
|
CommitTransactionCommand();
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, relids)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
Oid relid = lfirst_oid(l);
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2003-05-14 05:26:03 +02:00
|
|
|
StartTransactionCommand();
|
2004-09-13 22:10:13 +02:00
|
|
|
/* functions in indexes may want a snapshot set */
|
2008-05-12 22:02:02 +02:00
|
|
|
PushActiveSnapshot(GetTransactionSnapshot());
|
2013-07-31 00:36:52 +02:00
|
|
|
if (reindex_relation(relid,
|
|
|
|
REINDEX_REL_PROCESS_TOAST |
|
|
|
|
REINDEX_REL_CHECK_CONSTRAINTS))
|
2006-06-07 19:20:17 +02:00
|
|
|
ereport(NOTICE,
|
2010-06-01 02:33:23 +02:00
|
|
|
(errmsg("table \"%s.%s\" was reindexed",
|
|
|
|
get_namespace_name(get_rel_namespace(relid)),
|
2003-09-24 20:54:02 +02:00
|
|
|
get_rel_name(relid))));
|
2008-05-12 22:02:02 +02:00
|
|
|
PopActiveSnapshot();
|
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);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return MyDatabaseId;
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|