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
|
|
|
*
|
2018-01-03 05:30:12 +01:00
|
|
|
* Portions Copyright (c) 1996-2018, 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
|
|
|
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
#include "access/amapi.h"
|
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"
|
2016-04-16 18:11:41 +02:00
|
|
|
#include "access/sysattr.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"
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
#include "catalog/partition.h"
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
#include "catalog/pg_am.h"
|
2018-04-08 20:35:29 +02:00
|
|
|
#include "catalog/pg_constraint.h"
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
#include "catalog/pg_inherits.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"
|
2018-10-07 00:17:46 +02:00
|
|
|
#include "commands/event_trigger.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"
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
#include "nodes/makefuncs.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"
|
2016-04-16 18:11:41 +02:00
|
|
|
#include "optimizer/var.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"
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
#include "rewrite/rewriteManip.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"
|
2018-04-15 02:12:14 +02:00
|
|
|
#include "utils/partcache.h"
|
2017-01-21 02:29:53 +01:00
|
|
|
#include "utils/regproc.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,
|
2017-10-31 15:34:31 +01:00
|
|
|
const char *accessMethodName, Oid accessMethodId,
|
2007-01-09 03:14:16 +01:00
|
|
|
bool amcanorder,
|
2004-08-29 07:07:03 +02:00
|
|
|
bool isconstraint);
|
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);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
static void ReindexPartitionedIndex(Relation parentIdx);
|
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
|
2017-02-06 10:33:58 +01:00
|
|
|
* indexes. We acknowledge this when all operator classes, collations and
|
2012-01-25 21:28:07 +01:00
|
|
|
* exclusion operators match. Though we could further permit intra-opfamily
|
|
|
|
* changes for btree and hash indexes, that adds subtle complexity with no
|
2018-04-07 22:00:39 +02:00
|
|
|
* concrete benefit for core types. Note, that INCLUDE columns aren't
|
|
|
|
* checked by this function, for them it's enough that table rewrite is
|
|
|
|
* skipped.
|
|
|
|
*
|
2012-01-25 21:28:07 +01:00
|
|
|
* 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,
|
2017-10-31 15:34:31 +01:00
|
|
|
const char *accessMethodName,
|
2011-07-18 17:02:48 +02:00
|
|
|
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;
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
IndexAmRoutine *amRoutine;
|
2011-07-18 17:02:48 +02:00
|
|
|
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)));
|
|
|
|
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
accessMethodId = accessMethodForm->oid;
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
|
2011-07-18 17:02:48 +02:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
amcanorder = amRoutine->amcanorder;
|
|
|
|
|
2011-07-18 17:02:48 +02:00
|
|
|
/*
|
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
|
2018-04-08 23:23:39 +02:00
|
|
|
* later on, and it would have failed then anyway. Our attributeList
|
|
|
|
* contains only key attributes, thus we're filling ii_NumIndexAttrs and
|
|
|
|
* ii_NumIndexKeyAttrs with same value.
|
2011-07-18 17:02:48 +02:00
|
|
|
*/
|
|
|
|
indexInfo = makeNode(IndexInfo);
|
2018-04-08 23:23:39 +02:00
|
|
|
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
|
|
|
indexInfo->ii_NumIndexKeyAttrs = numberOfAttributes;
|
2011-07-18 17:02:48 +02:00
|
|
|
indexInfo->ii_Expressions = NIL;
|
|
|
|
indexInfo->ii_ExpressionsState = NIL;
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
indexInfo->ii_PredicateState = NULL;
|
2011-07-18 17:02:48 +02:00
|
|
|
indexInfo->ii_ExclusionOps = NULL;
|
|
|
|
indexInfo->ii_ExclusionProcs = NULL;
|
|
|
|
indexInfo->ii_ExclusionStrats = NULL;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
indexInfo->ii_Am = accessMethodId;
|
Allow index AMs to cache data across aminsert calls within a SQL command.
It's always been possible for index AMs to cache data across successive
amgettuple calls within a single SQL command: the IndexScanDesc.opaque
field is meant for precisely that. However, no comparable facility
exists for amortizing setup work across successive aminsert calls.
This patch adds such a feature and teaches GIN, GIST, and BRIN to use it
to amortize catalog lookups they'd previously been doing on every call.
(The other standard index AMs keep everything they need in the relcache,
so there's little to improve there.)
For GIN, the overall improvement in a statement that inserts many rows
can be as much as 10%, though it seems a bit less for the other two.
In addition, this makes a really significant difference in runtime
for CLOBBER_CACHE_ALWAYS tests, since in those builds the repeated
catalog lookups are vastly more expensive.
The reason this has been hard up to now is that the aminsert function is
not passed any useful place to cache per-statement data. What I chose to
do is to add suitable fields to struct IndexInfo and pass that to aminsert.
That's not widening the index AM API very much because IndexInfo is already
within the ken of ambuild; in fact, by passing the same info to aminsert
as to ambuild, this is really removing an inconsistency in the AM API.
Discussion: https://postgr.es/m/27568.1486508680@sss.pgh.pa.us
2017-02-09 17:52:12 +01:00
|
|
|
indexInfo->ii_AmCache = NULL;
|
|
|
|
indexInfo->ii_Context = CurrentMemoryContext;
|
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.
|
|
|
|
*/
|
2018-03-28 02:13:52 +02:00
|
|
|
if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
|
|
|
|
heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
|
2018-12-27 10:07:46 +01:00
|
|
|
indexForm->indisvalid))
|
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. */
|
2018-04-07 22:00:39 +02:00
|
|
|
old_natts = indexForm->indnkeyatts;
|
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])) &&
|
2017-08-20 20:19:07 +02:00
|
|
|
TupleDescAttr(irel->rd_att, 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)) &&
|
2017-08-20 20:19:07 +02:00
|
|
|
TupleDescAttr(irel->rd_att, 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.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
* 'parentIndexId': the OID of the parent index; InvalidOid if not the child
|
|
|
|
* of a partitioned index.
|
2018-02-19 20:59:37 +01:00
|
|
|
* 'parentConstraintId': the OID of the parent constraint; InvalidOid if not
|
|
|
|
* the child of a constraint (only used when recursing)
|
2004-05-05 06:48:48 +02:00
|
|
|
* 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
|
2017-02-12 22:03:41 +01:00
|
|
|
* 'check_rights': check for CREATE rights in namespace and tablespace. (This
|
|
|
|
* should be true except when ALTER is deleting/recreating an index.)
|
2017-06-04 18:02:31 +02:00
|
|
|
* 'check_not_in_use': check for table not already in use in current session.
|
|
|
|
* This should be true unless caller is holding the table open, in which
|
|
|
|
* case the caller had better have checked it earlier.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
* 'skip_build': make the catalog entries but don't create the index files
|
2004-05-05 06:48:48 +02:00
|
|
|
* 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
|
2011-07-18 17:02:48 +02:00
|
|
|
*
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
* Returns the object address of the created index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
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,
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
Oid parentIndexId,
|
2018-04-26 20:47:16 +02:00
|
|
|
Oid parentConstraintId,
|
2004-05-05 06:48:48 +02:00
|
|
|
bool is_alter_table,
|
|
|
|
bool check_rights,
|
2017-06-04 18:02:31 +02:00
|
|
|
bool check_not_in_use,
|
2004-05-05 06:48:48 +02:00
|
|
|
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;
|
2018-02-19 20:59:37 +01:00
|
|
|
Oid createdConstraintId = InvalidOid;
|
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;
|
2018-04-12 16:25:13 +02:00
|
|
|
List *allIndexParams;
|
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;
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
IndexAmRoutine *amRoutine;
|
2007-01-09 03:14:16 +01:00
|
|
|
bool amcanorder;
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
amoptions_function amoptions;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
bool partitioned;
|
2006-07-04 00:45:41 +02:00
|
|
|
Datum reloptions;
|
2007-01-09 03:14:16 +01:00
|
|
|
int16 *coloptions;
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexInfo *indexInfo;
|
2017-11-14 15:19:05 +01:00
|
|
|
bits16 flags;
|
|
|
|
bits16 constr_flags;
|
1997-09-08 04:41:22 +02:00
|
|
|
int numberOfAttributes;
|
2018-04-07 22:00:39 +02:00
|
|
|
int numberOfKeyAttributes;
|
2013-04-25 22:58:05 +02:00
|
|
|
TransactionId limitXmin;
|
2007-09-05 20:10:48 +02:00
|
|
|
VirtualTransactionId *old_snapshots;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
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
|
|
|
|
|
|
|
/*
|
2018-04-07 22:00:39 +02:00
|
|
|
* count key attributes in index
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2018-04-07 22:00:39 +02:00
|
|
|
numberOfKeyAttributes = list_length(stmt->indexParams);
|
|
|
|
|
|
|
|
/*
|
2018-04-12 16:25:13 +02:00
|
|
|
* Calculate the new list of index columns including both key columns and
|
2018-04-26 20:47:16 +02:00
|
|
|
* INCLUDE columns. Later we can determine which of these are key
|
|
|
|
* columns, and which are just part of the INCLUDE list by checking the
|
|
|
|
* list position. A list item in a position less than ii_NumIndexKeyAttrs
|
|
|
|
* is part of the key columns, and anything equal to and over is part of
|
|
|
|
* the INCLUDE columns.
|
2018-04-07 22:00:39 +02:00
|
|
|
*/
|
2018-04-12 16:25:13 +02:00
|
|
|
allIndexParams = list_concat(list_copy(stmt->indexParams),
|
|
|
|
list_copy(stmt->indexIncludingParams));
|
|
|
|
numberOfAttributes = list_length(allIndexParams);
|
2018-04-07 22:00:39 +02:00
|
|
|
|
2016-04-08 20:52:13 +02:00
|
|
|
if (numberOfAttributes <= 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
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.
|
Support parallel btree index builds.
To make this work, tuplesort.c and logtape.c must also support
parallelism, so this patch adds that infrastructure and then applies
it to the particular case of parallel btree index builds. Testing
to date shows that this can often be 2-3x faster than a serial
index build.
The model for deciding how many workers to use is fairly primitive
at present, but it's better than not having the feature. We can
refine it as we get more experience.
Peter Geoghegan with some help from Rushabh Lathia. While Heikki
Linnakangas is not an author of this patch, he wrote other patches
without which this feature would not have been possible, and
therefore the release notes should possibly credit him as an author
of this feature. Reviewed by Claudio Freire, Heikki Linnakangas,
Thomas Munro, Tels, Amit Kapila, me.
Discussion: http://postgr.es/m/CAM3SWZQKM=Pzc=CAHzRixKjp2eO5Q0Jg1SoFQqeXFQ647JiwqQ@mail.gmail.com
Discussion: http://postgr.es/m/CAH2-Wz=AxWqDoVvGU7dq856S4r6sJAj6DBn7VMtigkB33N5eyg@mail.gmail.com
2018-02-02 19:25:55 +01:00
|
|
|
*
|
|
|
|
* NB: If the lock strength here ever changes, code that is run by
|
|
|
|
* parallel workers under the control of certain particular ambuild
|
|
|
|
* functions will need to be updated, too.
|
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
|
|
|
|
2017-10-16 12:22:18 +02:00
|
|
|
/* Ensure that it makes sense to index this kind of relation */
|
|
|
|
switch (rel->rd_rel->relkind)
|
2011-05-05 21:47:42 +02:00
|
|
|
{
|
2017-10-16 12:22:18 +02:00
|
|
|
case RELKIND_RELATION:
|
|
|
|
case RELKIND_MATVIEW:
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
case RELKIND_PARTITIONED_TABLE:
|
2017-10-16 12:22:18 +02:00
|
|
|
/* OK */
|
|
|
|
break;
|
|
|
|
case RELKIND_FOREIGN_TABLE:
|
2018-04-26 20:47:16 +02:00
|
|
|
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01: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))));
|
2018-05-02 01:35:08 +02:00
|
|
|
break;
|
2017-10-16 12:22:18 +02:00
|
|
|
default:
|
2011-05-05 21:47:42 +02:00
|
|
|
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))));
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Establish behavior for partitioned tables, and verify sanity of
|
|
|
|
* parameters.
|
|
|
|
*
|
|
|
|
* We do not build an actual index in this case; we only create a few
|
|
|
|
* catalog entries. The actual indexes are built by recursing for each
|
|
|
|
* partition.
|
|
|
|
*/
|
|
|
|
partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
|
|
|
|
if (partitioned)
|
|
|
|
{
|
|
|
|
if (stmt->concurrent)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot create index on partitioned table \"%s\" concurrently",
|
|
|
|
RelationGetRelationName(rel))));
|
|
|
|
if (stmt->excludeOpNames)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot create exclusion constraints on partitioned table \"%s\"",
|
|
|
|
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
|
|
|
|
2017-06-04 18:02:31 +02:00
|
|
|
/*
|
|
|
|
* Unless our caller vouches for having checked this already, insist that
|
|
|
|
* the table not be in use by our own session, either. Otherwise we might
|
|
|
|
* fail to make entries in the new index (for instance, if an INSERT or
|
|
|
|
* UPDATE is in progress and has already made its list of target indexes).
|
|
|
|
*/
|
|
|
|
if (check_not_in_use)
|
|
|
|
CheckTableNotInUse(rel, "CREATE INDEX");
|
|
|
|
|
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)
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(aclresult, OBJECT_SCHEMA,
|
2003-08-01 02:15:26 +02:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2017-02-12 22:03:41 +01:00
|
|
|
/* Check tablespace permissions */
|
|
|
|
if (check_rights &&
|
|
|
|
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)
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(aclresult, OBJECT_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.
|
|
|
|
*/
|
2018-04-12 16:25:13 +02:00
|
|
|
indexColNames = ChooseIndexColumnNames(allIndexParams);
|
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)));
|
|
|
|
}
|
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);
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
accessMethodId = accessMethodForm->oid;
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
if (stmt->unique && !amRoutine->amcanunique)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("access method \"%s\" does not support unique indexes",
|
|
|
|
accessMethodName)));
|
2018-07-18 20:43:03 +02:00
|
|
|
if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
|
2018-04-07 22:00:39 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("access method \"%s\" does not support included columns",
|
|
|
|
accessMethodName)));
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
if (numberOfAttributes > 1 && !amRoutine->amcanmulticol)
|
2003-07-20 23:56:35 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("access method \"%s\" does not support multicolumn indexes",
|
|
|
|
accessMethodName)));
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
if (stmt->excludeOpNames && amRoutine->amgettuple == NULL)
|
2009-12-07 06:22:23 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("access method \"%s\" does not support exclusion constraints",
|
|
|
|
accessMethodName)));
|
2000-07-15 00:18:02 +02:00
|
|
|
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
amcanorder = amRoutine->amcanorder;
|
|
|
|
amoptions = amRoutine->amoptions;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
Restructure index access method API to hide most of it at the C level.
This patch reduces pg_am to just two columns, a name and a handler
function. All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function. This is similar to
the designs we've adopted for FDWs and tablesample methods. There
are multiple advantages. For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.
A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL. We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.
Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
2016-01-18 01:36:59 +01:00
|
|
|
pfree(amRoutine);
|
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;
|
2018-04-07 22:00:39 +02:00
|
|
|
indexInfo->ii_NumIndexKeyAttrs = numberOfKeyAttributes;
|
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);
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
indexInfo->ii_PredicateState = NULL;
|
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;
|
Support parallel btree index builds.
To make this work, tuplesort.c and logtape.c must also support
parallelism, so this patch adds that infrastructure and then applies
it to the particular case of parallel btree index builds. Testing
to date shows that this can often be 2-3x faster than a serial
index build.
The model for deciding how many workers to use is fairly primitive
at present, but it's better than not having the feature. We can
refine it as we get more experience.
Peter Geoghegan with some help from Rushabh Lathia. While Heikki
Linnakangas is not an author of this patch, he wrote other patches
without which this feature would not have been possible, and
therefore the release notes should possibly credit him as an author
of this feature. Reviewed by Claudio Freire, Heikki Linnakangas,
Thomas Munro, Tels, Amit Kapila, me.
Discussion: http://postgr.es/m/CAM3SWZQKM=Pzc=CAHzRixKjp2eO5Q0Jg1SoFQqeXFQ647JiwqQ@mail.gmail.com
Discussion: http://postgr.es/m/CAH2-Wz=AxWqDoVvGU7dq856S4r6sJAj6DBn7VMtigkB33N5eyg@mail.gmail.com
2018-02-02 19:25:55 +01:00
|
|
|
indexInfo->ii_ParallelWorkers = 0;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
indexInfo->ii_Am = accessMethodId;
|
Allow index AMs to cache data across aminsert calls within a SQL command.
It's always been possible for index AMs to cache data across successive
amgettuple calls within a single SQL command: the IndexScanDesc.opaque
field is meant for precisely that. However, no comparable facility
exists for amortizing setup work across successive aminsert calls.
This patch adds such a feature and teaches GIN, GIST, and BRIN to use it
to amortize catalog lookups they'd previously been doing on every call.
(The other standard index AMs keep everything they need in the relcache,
so there's little to improve there.)
For GIN, the overall improvement in a statement that inserts many rows
can be as much as 10%, though it seems a bit less for the other two.
In addition, this makes a really significant difference in runtime
for CLOBBER_CACHE_ALWAYS tests, since in those builds the repeated
catalog lookups are vastly more expensive.
The reason this has been hard up to now is that the aminsert function is
not passed any useful place to cache per-statement data. What I chose to
do is to add suitable fields to struct IndexInfo and pass that to aminsert.
That's not widening the index AM API very much because IndexInfo is already
within the ken of ambuild; in fact, by passing the same info to aminsert
as to ambuild, this is really removing an inconsistency in the AM API.
Discussion: https://postgr.es/m/27568.1486508680@sss.pgh.pa.us
2017-02-09 17:52:12 +01:00
|
|
|
indexInfo->ii_AmCache = NULL;
|
|
|
|
indexInfo->ii_Context = CurrentMemoryContext;
|
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));
|
2018-04-08 23:23:39 +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,
|
2018-04-12 16:25:13 +02:00
|
|
|
coloptions, allIndexParams,
|
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, 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)
|
2018-10-07 00:17:46 +02:00
|
|
|
index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
|
2011-01-25 21:42:03 +01:00
|
|
|
|
2018-02-19 20:59:37 +01:00
|
|
|
/*
|
|
|
|
* If this table is partitioned and we're creating a unique index or a
|
|
|
|
* primary key, make sure that the indexed columns are part of the
|
|
|
|
* partition key. Otherwise it would be possible to violate uniqueness by
|
|
|
|
* putting values that ought to be unique in different partitions.
|
|
|
|
*
|
|
|
|
* We could lift this limitation if we had global indexes, but those have
|
|
|
|
* their own problems, so this is a useful feature combination.
|
|
|
|
*/
|
|
|
|
if (partitioned && (stmt->unique || stmt->primary))
|
|
|
|
{
|
|
|
|
PartitionKey key = rel->rd_partkey;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A partitioned table can have unique indexes, as long as all the
|
|
|
|
* columns in the partition key appear in the unique key. A
|
|
|
|
* partition-local index can enforce global uniqueness iff the PK
|
|
|
|
* value completely determines the partition that a row is in.
|
|
|
|
*
|
2018-04-26 20:47:16 +02:00
|
|
|
* Thus, verify that all the columns in the partition key appear in
|
|
|
|
* the unique key definition.
|
2018-02-19 20:59:37 +01:00
|
|
|
*/
|
|
|
|
for (i = 0; i < key->partnatts; i++)
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
bool found = false;
|
|
|
|
int j;
|
2018-02-19 20:59:37 +01:00
|
|
|
const char *constraint_type;
|
|
|
|
|
|
|
|
if (stmt->primary)
|
|
|
|
constraint_type = "PRIMARY KEY";
|
|
|
|
else if (stmt->unique)
|
|
|
|
constraint_type = "UNIQUE";
|
|
|
|
else if (stmt->excludeOpNames != NIL)
|
|
|
|
constraint_type = "EXCLUDE";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "unknown constraint type");
|
|
|
|
constraint_type = NULL; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It may be possible to support UNIQUE constraints when partition
|
|
|
|
* keys are expressions, but is it worth it? Give up for now.
|
|
|
|
*/
|
|
|
|
if (key->partattrs[i] == 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("unsupported %s constraint with partition key definition",
|
|
|
|
constraint_type),
|
|
|
|
errdetail("%s constraints cannot be used when partition keys include expressions.",
|
2018-04-26 20:47:16 +02:00
|
|
|
constraint_type)));
|
2018-02-19 20:59:37 +01:00
|
|
|
|
|
|
|
for (j = 0; j < indexInfo->ii_NumIndexAttrs; j++)
|
|
|
|
{
|
2018-04-12 12:02:45 +02:00
|
|
|
if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
|
2018-02-19 20:59:37 +01:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
Form_pg_attribute att;
|
|
|
|
|
|
|
|
att = TupleDescAttr(RelationGetDescr(rel), key->partattrs[i] - 1);
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("insufficient columns in %s constraint definition",
|
|
|
|
constraint_type),
|
|
|
|
errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
|
|
|
|
constraint_type, RelationGetRelationName(rel),
|
|
|
|
NameStr(att->attname))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-16 18:11:41 +02:00
|
|
|
/*
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
* We disallow indexes on system columns. They would not necessarily get
|
|
|
|
* updated correctly, and they don't seem useful anyway.
|
2016-04-16 18:11:41 +02:00
|
|
|
*/
|
|
|
|
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
|
|
|
|
{
|
2018-04-12 12:02:45 +02:00
|
|
|
AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
|
2016-04-16 18:11:41 +02:00
|
|
|
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
if (attno < 0)
|
2016-04-16 18:11:41 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("index creation on system columns is not supported")));
|
2016-04-16 18:11:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also check for system columns used in expressions or predicates.
|
|
|
|
*/
|
|
|
|
if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
|
|
|
|
{
|
|
|
|
Bitmapset *indexattrs = NULL;
|
|
|
|
|
|
|
|
pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
|
|
|
|
pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);
|
|
|
|
|
|
|
|
for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
|
|
|
|
{
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
|
2016-04-16 18:11:41 +02:00
|
|
|
indexattrs))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("index creation on system columns is not supported")));
|
2016-04-16 18:11:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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");
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02: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,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
(errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
|
|
|
|
is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
|
|
|
|
constraint_type,
|
|
|
|
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
|
|
|
/*
|
2017-11-14 15:19:05 +01:00
|
|
|
* Make the catalog entries for the index, including constraints. This
|
|
|
|
* step also actually builds the index, except if caller requested not to
|
2018-04-26 20:47:16 +02:00
|
|
|
* or in concurrent mode, in which case it'll be done later, or doing a
|
|
|
|
* partitioned index (because those don't have storage).
|
2007-09-20 19:56:33 +02:00
|
|
|
*/
|
2017-11-14 15:19:05 +01:00
|
|
|
flags = constr_flags = 0;
|
|
|
|
if (stmt->isconstraint)
|
|
|
|
flags |= INDEX_CREATE_ADD_CONSTRAINT;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (skip_build || stmt->concurrent || partitioned)
|
2017-11-14 15:19:05 +01:00
|
|
|
flags |= INDEX_CREATE_SKIP_BUILD;
|
|
|
|
if (stmt->if_not_exists)
|
|
|
|
flags |= INDEX_CREATE_IF_NOT_EXISTS;
|
|
|
|
if (stmt->concurrent)
|
|
|
|
flags |= INDEX_CREATE_CONCURRENT;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (partitioned)
|
|
|
|
flags |= INDEX_CREATE_PARTITIONED;
|
2017-11-14 15:19:05 +01:00
|
|
|
if (stmt->primary)
|
|
|
|
flags |= INDEX_CREATE_IS_PRIMARY;
|
2018-12-05 17:31:51 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the table is partitioned, and recursion was declined but partitions
|
|
|
|
* exist, mark the index as invalid.
|
|
|
|
*/
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (partitioned && stmt->relation && !stmt->relation->inh)
|
2018-12-05 17:31:51 +01:00
|
|
|
{
|
|
|
|
PartitionDesc pd = RelationGetPartitionDesc(rel);
|
|
|
|
|
|
|
|
if (pd->nparts != 0)
|
|
|
|
flags |= INDEX_CREATE_INVALID;
|
|
|
|
}
|
2017-11-14 15:19:05 +01:00
|
|
|
|
|
|
|
if (stmt->deferrable)
|
|
|
|
constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
|
|
|
|
if (stmt->initdeferred)
|
|
|
|
constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
|
|
|
|
|
2006-08-25 06:06:58 +02:00
|
|
|
indexRelationId =
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
index_create(rel, indexRelationName, indexRelationId, parentIndexId,
|
2018-02-19 20:59:37 +01:00
|
|
|
parentConstraintId,
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
stmt->oldNode, indexInfo, indexColNames,
|
2011-04-22 23:43:18 +02:00
|
|
|
accessMethodId, tablespaceId,
|
|
|
|
collationObjectId, classObjectId,
|
2017-11-14 15:19:05 +01:00
|
|
|
coloptions, reloptions,
|
|
|
|
flags, constr_flags,
|
2018-02-19 20:59:37 +01:00
|
|
|
allowSystemTableMods, !check_rights,
|
|
|
|
&createdConstraintId);
|
2014-11-06 10:48:33 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, RelationRelationId, indexRelationId);
|
|
|
|
|
2014-11-06 10:48:33 +01:00
|
|
|
if (!OidIsValid(indexRelationId))
|
|
|
|
{
|
|
|
|
heap_close(rel, NoLock);
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2014-11-06 10:48:33 +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
|
|
|
|
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);
|
|
|
|
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (partitioned)
|
|
|
|
{
|
|
|
|
/*
|
2018-04-26 20:47:16 +02:00
|
|
|
* Unless caller specified to skip this step (via ONLY), process each
|
|
|
|
* partition to make sure they all contain a corresponding index.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
*
|
|
|
|
* If we're called internally (no stmt->relation), recurse always.
|
|
|
|
*/
|
|
|
|
if (!stmt->relation || stmt->relation->inh)
|
|
|
|
{
|
|
|
|
PartitionDesc partdesc = RelationGetPartitionDesc(rel);
|
|
|
|
int nparts = partdesc->nparts;
|
|
|
|
Oid *part_oids = palloc(sizeof(Oid) * nparts);
|
|
|
|
bool invalidate_parent = false;
|
|
|
|
TupleDesc parentDesc;
|
|
|
|
Oid *opfamOids;
|
|
|
|
|
|
|
|
memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
|
|
|
|
|
|
|
|
parentDesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
2018-04-12 16:25:13 +02:00
|
|
|
opfamOids = palloc(sizeof(Oid) * numberOfKeyAttributes);
|
|
|
|
for (i = 0; i < numberOfKeyAttributes; i++)
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
opfamOids[i] = get_opclass_family(classObjectId[i]);
|
|
|
|
|
|
|
|
heap_close(rel, NoLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For each partition, scan all existing indexes; if one matches
|
|
|
|
* our index definition and is not already attached to some other
|
|
|
|
* parent index, attach it to the one we just created.
|
|
|
|
*
|
|
|
|
* If none matches, build a new index by calling ourselves
|
|
|
|
* recursively with the same options (except for the index name).
|
|
|
|
*/
|
|
|
|
for (i = 0; i < nparts; i++)
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
Oid childRelid = part_oids[i];
|
|
|
|
Relation childrel;
|
|
|
|
List *childidxs;
|
|
|
|
ListCell *cell;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
AttrNumber *attmap;
|
2018-04-26 20:47:16 +02:00
|
|
|
bool found = false;
|
|
|
|
int maplen;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
childrel = heap_open(childRelid, lockmode);
|
|
|
|
childidxs = RelationGetIndexList(childrel);
|
|
|
|
attmap =
|
|
|
|
convert_tuples_by_name_map(RelationGetDescr(childrel),
|
|
|
|
parentDesc,
|
|
|
|
gettext_noop("could not convert row type"));
|
|
|
|
maplen = parentDesc->natts;
|
|
|
|
|
|
|
|
|
|
|
|
foreach(cell, childidxs)
|
|
|
|
{
|
|
|
|
Oid cldidxid = lfirst_oid(cell);
|
|
|
|
Relation cldidx;
|
|
|
|
IndexInfo *cldIdxInfo;
|
|
|
|
|
|
|
|
/* this index is already partition of another one */
|
|
|
|
if (has_superclass(cldidxid))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cldidx = index_open(cldidxid, lockmode);
|
|
|
|
cldIdxInfo = BuildIndexInfo(cldidx);
|
|
|
|
if (CompareIndexInfo(cldIdxInfo, indexInfo,
|
|
|
|
cldidx->rd_indcollation,
|
|
|
|
collationObjectId,
|
|
|
|
cldidx->rd_opfamily,
|
|
|
|
opfamOids,
|
|
|
|
attmap, maplen))
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
Oid cldConstrOid = InvalidOid;
|
2018-02-19 20:59:37 +01:00
|
|
|
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
/*
|
2018-02-19 20:59:37 +01:00
|
|
|
* Found a match.
|
|
|
|
*
|
|
|
|
* If this index is being created in the parent
|
|
|
|
* because of a constraint, then the child needs to
|
|
|
|
* have a constraint also, so look for one. If there
|
|
|
|
* is no such constraint, this index is no good, so
|
|
|
|
* keep looking.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
*/
|
2018-02-19 20:59:37 +01:00
|
|
|
if (createdConstraintId != InvalidOid)
|
|
|
|
{
|
|
|
|
cldConstrOid =
|
|
|
|
get_relation_idx_constraint_oid(childRelid,
|
|
|
|
cldidxid);
|
|
|
|
if (cldConstrOid == InvalidOid)
|
|
|
|
{
|
|
|
|
index_close(cldidx, lockmode);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attach index to parent and we're done. */
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
IndexSetParentIndex(cldidx, indexRelationId);
|
2018-02-19 20:59:37 +01:00
|
|
|
if (createdConstraintId != InvalidOid)
|
|
|
|
ConstraintSetParentConstraint(cldConstrOid,
|
|
|
|
createdConstraintId);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
2018-12-27 10:07:46 +01:00
|
|
|
if (!cldidx->rd_index->indisvalid)
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
invalidate_parent = true;
|
|
|
|
|
|
|
|
found = true;
|
2018-02-19 20:59:37 +01:00
|
|
|
/* keep lock till commit */
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
index_close(cldidx, NoLock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
index_close(cldidx, lockmode);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_free(childidxs);
|
|
|
|
heap_close(childrel, NoLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If no matching index was found, create our own.
|
|
|
|
*/
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
IndexStmt *childStmt = copyObject(stmt);
|
|
|
|
bool found_whole_row;
|
2018-06-22 21:12:53 +02:00
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust any Vars (both in expressions and in the index's
|
|
|
|
* WHERE clause) to match the partition's column numbering
|
|
|
|
* in case it's different from the parent's.
|
|
|
|
*/
|
|
|
|
foreach(lc, childStmt->indexParams)
|
|
|
|
{
|
2018-06-30 18:25:49 +02:00
|
|
|
IndexElem *ielem = lfirst(lc);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
2018-06-22 21:12:53 +02:00
|
|
|
/*
|
|
|
|
* If the index parameter is an expression, we must
|
|
|
|
* translate it to contain child Vars.
|
|
|
|
*/
|
|
|
|
if (ielem->expr)
|
|
|
|
{
|
|
|
|
ielem->expr =
|
|
|
|
map_variable_attnos((Node *) ielem->expr,
|
|
|
|
1, 0, attmap, maplen,
|
|
|
|
InvalidOid,
|
|
|
|
&found_whole_row);
|
|
|
|
if (found_whole_row)
|
|
|
|
elog(ERROR, "cannot convert whole-row table reference");
|
|
|
|
}
|
|
|
|
}
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
childStmt->whereClause =
|
|
|
|
map_variable_attnos(stmt->whereClause, 1, 0,
|
|
|
|
attmap, maplen,
|
|
|
|
InvalidOid, &found_whole_row);
|
|
|
|
if (found_whole_row)
|
|
|
|
elog(ERROR, "cannot convert whole-row table reference");
|
|
|
|
|
|
|
|
childStmt->idxname = NULL;
|
|
|
|
childStmt->relationId = childRelid;
|
|
|
|
DefineIndex(childRelid, childStmt,
|
2018-04-26 20:47:16 +02:00
|
|
|
InvalidOid, /* no predefined OID */
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
indexRelationId, /* this is our child */
|
2018-02-19 20:59:37 +01:00
|
|
|
createdConstraintId,
|
2018-03-12 23:42:32 +01:00
|
|
|
is_alter_table, check_rights, check_not_in_use,
|
2018-06-29 17:27:57 +02:00
|
|
|
skip_build, quiet);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pfree(attmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The pg_index row we inserted for this index was marked
|
2018-04-26 20:47:16 +02:00
|
|
|
* indisvalid=true. But if we attached an existing index that is
|
|
|
|
* invalid, this is incorrect, so update our row to invalid too.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
*/
|
|
|
|
if (invalidate_parent)
|
|
|
|
{
|
|
|
|
Relation pg_index = heap_open(IndexRelationId, RowExclusiveLock);
|
|
|
|
HeapTuple tup,
|
|
|
|
newtup;
|
|
|
|
|
|
|
|
tup = SearchSysCache1(INDEXRELID,
|
|
|
|
ObjectIdGetDatum(indexRelationId));
|
|
|
|
if (!tup)
|
|
|
|
elog(ERROR, "cache lookup failed for index %u",
|
|
|
|
indexRelationId);
|
|
|
|
newtup = heap_copytuple(tup);
|
|
|
|
((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
|
|
|
|
CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
heap_close(pg_index, RowExclusiveLock);
|
|
|
|
heap_freetuple(newtup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
heap_close(rel, NoLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Indexes on partitioned tables are not themselves built, so we're
|
|
|
|
* done here.
|
|
|
|
*/
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
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->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);
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
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 */
|
Support parallel btree index builds.
To make this work, tuplesort.c and logtape.c must also support
parallelism, so this patch adds that infrastructure and then applies
it to the particular case of parallel btree index builds. Testing
to date shows that this can often be 2-3x faster than a serial
index build.
The model for deciding how many workers to use is fairly primitive
at present, but it's better than not having the feature. We can
refine it as we get more experience.
Peter Geoghegan with some help from Rushabh Lathia. While Heikki
Linnakangas is not an author of this patch, he wrote other patches
without which this feature would not have been possible, and
therefore the release notes should possibly credit him as an author
of this feature. Reviewed by Claudio Freire, Heikki Linnakangas,
Thomas Munro, Tels, Amit Kapila, me.
Discussion: http://postgr.es/m/CAM3SWZQKM=Pzc=CAHzRixKjp2eO5Q0Jg1SoFQqeXFQ647JiwqQ@mail.gmail.com
Discussion: http://postgr.es/m/CAH2-Wz=AxWqDoVvGU7dq856S4r6sJAj6DBn7VMtigkB33N5eyg@mail.gmail.com
2018-02-02 19:25:55 +01:00
|
|
|
index_build(rel, indexRelation, indexInfo, stmt->primary, false, true);
|
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);
|
2018-04-18 18:07:37 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The snapshot subsystem could still contain registered snapshots that
|
|
|
|
* are holding back our process's advertised xmin; in particular, if
|
|
|
|
* default_transaction_isolation = serializable, there is a transaction
|
|
|
|
* snapshot that is still active. The CatalogSnapshot is likewise a
|
|
|
|
* hazard. To ensure no deadlocks, we must commit and start yet another
|
|
|
|
* transaction, and do our wait before any snapshot has been taken in it.
|
|
|
|
*/
|
|
|
|
CommitTransactionCommand();
|
|
|
|
StartTransactionCommand();
|
|
|
|
|
|
|
|
/* We should now definitely not be advertising any xmin. */
|
|
|
|
Assert(MyPgXact->xmin == InvalidTransactionId);
|
2013-04-25 22:58:05 +02:00
|
|
|
|
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,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +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;
|
|
|
|
}
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
if (k >= n_newer_snapshots) /* not there anymore */
|
2009-04-04 19:40:36 +02:00
|
|
|
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
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
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
|
2017-02-06 10:33:58 +01:00
|
|
|
* any evaluable predicate will work. So accept any predicate here
|
2001-07-16 07:07:00 +02:00
|
|
|
* (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),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +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
|
2018-04-12 15:37:22 +02:00
|
|
|
* or index expressions, opclasses, and indoptions. Note, all output vectors
|
|
|
|
* should be allocated for all columns, including "including" ones.
|
2007-01-09 03:14:16 +01:00
|
|
|
*/
|
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,
|
2017-10-31 15:34:31 +01:00
|
|
|
const 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;
|
2018-04-07 22:00:39 +02:00
|
|
|
int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
|
2009-12-07 06:22:23 +01:00
|
|
|
|
|
|
|
/* Allocate space for exclusion operator info, if needed */
|
|
|
|
if (exclusionOpNames)
|
|
|
|
{
|
2018-04-07 22:00:39 +02:00
|
|
|
Assert(list_length(exclusionOpNames) == nkeycols);
|
|
|
|
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * nkeycols);
|
|
|
|
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * nkeycols);
|
|
|
|
indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * nkeycols);
|
2009-12-07 06:22:23 +01:00
|
|
|
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),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +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);
|
2018-04-12 12:02:45 +02:00
|
|
|
indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
|
2003-05-28 18:04:02 +02:00
|
|
|
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);
|
2018-04-07 22:00:39 +02:00
|
|
|
|
|
|
|
if (attn >= nkeycols)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("expressions are not supported in included columns")));
|
2011-03-24 20:29:52 +01:00
|
|
|
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.
|
|
|
|
*/
|
2018-04-12 12:02:45 +02:00
|
|
|
indexInfo->ii_IndexAttrNumbers[attn] = ((Var *) expr)->varattno;
|
2011-03-24 20:29:52 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
indexInfo->ii_IndexAttrNumbers[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
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-04-26 18:42:31 +02:00
|
|
|
* An expression using mutable functions is probably wrong,
|
2011-03-24 20:29:52 +01:00
|
|
|
* 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;
|
|
|
|
|
2018-04-12 15:37:22 +02:00
|
|
|
/*
|
2018-04-26 20:47:16 +02:00
|
|
|
* Included columns have no collation, no opclass and no ordering
|
|
|
|
* options.
|
2018-04-12 15:37:22 +02:00
|
|
|
*/
|
|
|
|
if (attn >= nkeycols)
|
|
|
|
{
|
|
|
|
if (attribute->collation)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("including column does not support a collation")));
|
|
|
|
if (attribute->opclass)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("including column does not support an operator class")));
|
|
|
|
if (attribute->ordering != SORTBY_DEFAULT)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("including column does not support ASC/DESC options")));
|
|
|
|
if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("including column does not support NULLS FIRST/LAST options")));
|
|
|
|
|
|
|
|
classOidP[attn] = InvalidOid;
|
|
|
|
colOptionP[attn] = 0;
|
|
|
|
collationOidP[attn] = InvalidOid;
|
|
|
|
attn++;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
Implement table partitioning.
Table partitioning is like table inheritance and reuses much of the
existing infrastructure, but there are some important differences.
The parent is called a partitioned table and is always empty; it may
not have indexes or non-inherited constraints, since those make no
sense for a relation with no data of its own. The children are called
partitions and contain all of the actual data. Each partition has an
implicit partitioning constraint. Multiple inheritance is not
allowed, and partitioning and inheritance can't be mixed. Partitions
can't have extra columns and may not allow nulls unless the parent
does. Tuples inserted into the parent are automatically routed to the
correct partition, so tuple-routing ON INSERT triggers are not needed.
Tuple routing isn't yet supported for partitions which are foreign
tables, and it doesn't handle updates that cross partition boundaries.
Currently, tables can be range-partitioned or list-partitioned. List
partitioning is limited to a single column, but range partitioning can
involve multiple columns. A partitioning "column" can be an
expression.
Because table partitioning is less general than table inheritance, it
is hoped that it will be easier to reason about properties of
partitions, and therefore that this will serve as a better foundation
for a variety of possible optimizations, including query planner
optimizations. The tuple routing based which this patch does based on
the implicit partitioning constraints is an example of this, but it
seems likely that many other useful optimizations are also possible.
Amit Langote, reviewed and tested by Robert Haas, Ashutosh Bapat,
Amit Kapila, Rajkumar Raghuwanshi, Corey Huinker, Jaime Casanova,
Rushabh Lathia, Erik Rijkers, among others. Minor revisions by me.
2016-12-07 19:17:43 +01:00
|
|
|
classOidP[attn] = ResolveOpClass(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
|
Implement table partitioning.
Table partitioning is like table inheritance and reuses much of the
existing infrastructure, but there are some important differences.
The parent is called a partitioned table and is always empty; it may
not have indexes or non-inherited constraints, since those make no
sense for a relation with no data of its own. The children are called
partitions and contain all of the actual data. Each partition has an
implicit partitioning constraint. Multiple inheritance is not
allowed, and partitioning and inheritance can't be mixed. Partitions
can't have extra columns and may not allow nulls unless the parent
does. Tuples inserted into the parent are automatically routed to the
correct partition, so tuple-routing ON INSERT triggers are not needed.
Tuple routing isn't yet supported for partitions which are foreign
tables, and it doesn't handle updates that cross partition boundaries.
Currently, tables can be range-partitioned or list-partitioned. List
partitioning is limited to a single column, but range partitioning can
involve multiple columns. A partitioning "column" can be an
expression.
Because table partitioning is less general than table inheritance, it
is hoped that it will be easier to reason about properties of
partitions, and therefore that this will serve as a better foundation
for a variety of possible optimizations, including query planner
optimizations. The tuple routing based which this patch does based on
the implicit partitioning constraints is an example of this, but it
seems likely that many other useful optimizations are also possible.
Amit Langote, reviewed and tested by Robert Haas, Ashutosh Bapat,
Amit Kapila, Rajkumar Raghuwanshi, Corey Huinker, Jaime Casanova,
Rushabh Lathia, Erik Rijkers, among others. Minor revisions by me.
2016-12-07 19:17:43 +01:00
|
|
|
*
|
|
|
|
* Note: This is used to resolve operator class specification in index and
|
|
|
|
* partition key definitions.
|
2003-05-28 18:04:02 +02:00
|
|
|
*/
|
Implement table partitioning.
Table partitioning is like table inheritance and reuses much of the
existing infrastructure, but there are some important differences.
The parent is called a partitioned table and is always empty; it may
not have indexes or non-inherited constraints, since those make no
sense for a relation with no data of its own. The children are called
partitions and contain all of the actual data. Each partition has an
implicit partitioning constraint. Multiple inheritance is not
allowed, and partitioning and inheritance can't be mixed. Partitions
can't have extra columns and may not allow nulls unless the parent
does. Tuples inserted into the parent are automatically routed to the
correct partition, so tuple-routing ON INSERT triggers are not needed.
Tuple routing isn't yet supported for partitions which are foreign
tables, and it doesn't handle updates that cross partition boundaries.
Currently, tables can be range-partitioned or list-partitioned. List
partitioning is limited to a single column, but range partitioning can
involve multiple columns. A partitioning "column" can be an
expression.
Because table partitioning is less general than table inheritance, it
is hoped that it will be easier to reason about properties of
partitions, and therefore that this will serve as a better foundation
for a variety of possible optimizations, including query planner
optimizations. The tuple routing based which this patch does based on
the implicit partitioning constraints is an example of this, but it
seems likely that many other useful optimizations are also possible.
Amit Langote, reviewed and tested by Robert Haas, Ashutosh Bapat,
Amit Kapila, Rajkumar Raghuwanshi, Corey Huinker, Jaime Casanova,
Rushabh Lathia, Erik Rijkers, among others. Minor revisions by me.
2016-12-07 19:17:43 +01:00
|
|
|
Oid
|
|
|
|
ResolveOpClass(List *opclass, Oid attrType,
|
2017-10-31 15:34:31 +01:00
|
|
|
const 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;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Form_pg_opclass opform;
|
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
|
|
|
*/
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
opform = (Form_pg_opclass) GETSTRUCT(tuple);
|
|
|
|
opClassId = opform->oid;
|
|
|
|
opInputType = opform->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",
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
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++;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
result = opclass->oid;
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
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++;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
result = opclass->oid;
|
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++;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
result = opclass->oid;
|
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),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("there are multiple default operator classes for data type %s",
|
|
|
|
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()
|
|
|
|
*
|
Clone extended stats in CREATE TABLE (LIKE INCLUDING ALL)
The LIKE INCLUDING ALL clause to CREATE TABLE intuitively indicates
cloning of extended statistics on the source table, but it failed to do
so. Patch it up so that it does. Also include an INCLUDING STATISTICS
option to the LIKE clause, so that the behavior can be requested
individually, or excluded individually.
While at it, reorder the INCLUDING options, both in code and in docs, in
alphabetical order which makes more sense than feature-implementation
order that was previously used.
Backpatch this to Postgres 10, where extended statistics were
introduced, because this is seen as an oversight in a fresh feature
which is better to get consistent from the get-go instead of changing
only in pg11.
In pg11, comments on statistics objects are cloned too. In pg10 they
are not, because I (Álvaro) was too coward to change the parse node as
required to support it. Also, in pg10 I chose not to renumber the
parser symbols for the various INCLUDING options in LIKE, for the same
reason. Any corresponding user-visible changes (docs) are backpatched,
though.
Reported-by: Stephen Froehlich
Author: David Rowley
Reviewed-by: Álvaro Herrera, Tomas Vondra
Discussion: https://postgr.es/m/CY1PR0601MB1927315B45667A1B679D0FD5E5EF0@CY1PR0601MB1927.namprd06.prod.outlook.com
2018-03-05 23:37:19 +01:00
|
|
|
* Create a name for an implicitly created index, sequence, constraint,
|
|
|
|
* extended statistics, etc.
|
2004-06-10 19:56:03 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
* If isconstraint is true, we also avoid choosing a name matching any
|
|
|
|
* existing constraint in the same namespace. (This is stricter than what
|
|
|
|
* Postgres itself requires, but the SQL standard says that constraint names
|
|
|
|
* should be unique within schemas, so we follow that for autogenerated
|
|
|
|
* constraint names.)
|
|
|
|
*
|
2004-06-10 19:56:03 +02:00
|
|
|
* 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,
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
const char *label, Oid namespaceid,
|
|
|
|
bool isconstraint)
|
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)))
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
{
|
|
|
|
if (!isconstraint ||
|
|
|
|
!ConstraintNameExists(relname, namespaceid))
|
|
|
|
break;
|
|
|
|
}
|
2004-05-05 06:48:48 +02:00
|
|
|
|
|
|
|
/* 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",
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
namespaceId,
|
|
|
|
true);
|
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 (exclusionOpNames != NIL)
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
2010-03-22 16:24:11 +01:00
|
|
|
"excl",
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
namespaceId,
|
|
|
|
true);
|
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 (isconstraint)
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
|
|
|
"key",
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
namespaceId,
|
|
|
|
true);
|
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
|
|
|
|
{
|
|
|
|
indexname = ChooseRelationName(tabname,
|
|
|
|
ChooseIndexNameAddition(colnames),
|
|
|
|
"idx",
|
Fully enforce uniqueness of constraint names.
It's been true for a long time that we expect names of table and domain
constraints to be unique among the constraints of that table or domain.
However, the enforcement of that has been pretty haphazard, and it missed
some corner cases such as creating a CHECK constraint and then an index
constraint of the same name (as per recent report from André Hänsel).
Also, due to the lack of an actual unique index enforcing this, duplicates
could be created through race conditions.
Moreover, the code that searches pg_constraint has been quite inconsistent
about how to handle duplicate names if one did occur: some places checked
and threw errors if there was more than one match, while others just
processed the first match they came to.
To fix, create a unique index on (conrelid, contypid, conname). Since
either conrelid or contypid is zero, this will separately enforce
uniqueness of constraint names among constraints of any one table and any
one domain. (If we ever implement SQL assertions, and put them into this
catalog, more thought might be needed. But it'd be at least as reasonable
to put them into a new catalog; having overloaded this one catalog with
two kinds of constraints was a mistake already IMO.) This index can replace
the existing non-unique index on conrelid, though we need to keep the one
on contypid for query performance reasons.
Having done that, we can simplify the logic in various places that either
coped with duplicates or neglected to, as well as potentially improve
lookup performance when searching for a constraint by name.
Also, as per our usual practice, install a preliminary check so that you
get something more friendly than a unique-index violation report in the
case complained of by André. And teach ChooseIndexName to avoid choosing
autogenerated names that would draw such a failure.
While it's not possible to make such a change in the back branches,
it doesn't seem quite too late to put this into v11, so do so.
Discussion: https://postgr.es/m/0c1001d4428f$0942b430$1bc81c90$@webkr.de
2018-09-04 19:45:35 +02:00
|
|
|
namespaceId,
|
|
|
|
false);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
Clone extended stats in CREATE TABLE (LIKE INCLUDING ALL)
The LIKE INCLUDING ALL clause to CREATE TABLE intuitively indicates
cloning of extended statistics on the source table, but it failed to do
so. Patch it up so that it does. Also include an INCLUDING STATISTICS
option to the LIKE clause, so that the behavior can be requested
individually, or excluded individually.
While at it, reorder the INCLUDING options, both in code and in docs, in
alphabetical order which makes more sense than feature-implementation
order that was previously used.
Backpatch this to Postgres 10, where extended statistics were
introduced, because this is seen as an oversight in a fresh feature
which is better to get consistent from the get-go instead of changing
only in pg11.
In pg11, comments on statistics objects are cloned too. In pg10 they
are not, because I (Álvaro) was too coward to change the parse node as
required to support it. Also, in pg10 I chose not to renumber the
parser symbols for the various INCLUDING options in LIKE, for the same
reason. Any corresponding user-visible changes (docs) are backpatched,
though.
Reported-by: Stephen Froehlich
Author: David Rowley
Reviewed-by: Álvaro Herrera, Tomas Vondra
Discussion: https://postgr.es/m/CY1PR0601MB1927315B45667A1B679D0FD5E5EF0@CY1PR0601MB1927.namprd06.prod.outlook.com
2018-03-05 23:37:19 +01:00
|
|
|
*
|
|
|
|
* XXX See also ChooseExtendedStatisticNameAddition.
|
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)
|
|
|
|
{
|
|
|
|
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)
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02: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)
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02: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
|
|
|
*/
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
void
|
2015-05-15 13:09:57 +02:00
|
|
|
ReindexIndex(RangeVar *indexRelation, int options)
|
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;
|
2015-03-30 21:01:44 +02:00
|
|
|
Relation irel;
|
|
|
|
char persistence;
|
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
|
|
|
|
2015-03-30 21:01:44 +02:00
|
|
|
/*
|
|
|
|
* Find and lock index, and check permissions on table; use callback to
|
|
|
|
* obtain lock on table first, to avoid deadlock hazard. The lock level
|
|
|
|
* used here must match the index lock obtained in reindex_index().
|
|
|
|
*/
|
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
|
|
|
indOid = RangeVarGetRelidExtended(indexRelation, AccessExclusiveLock,
|
2018-03-31 01:33:42 +02:00
|
|
|
0,
|
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
|
|
|
RangeVarCallbackForReindexIndex,
|
|
|
|
(void *) &heapOid);
|
|
|
|
|
2015-03-30 21:01:44 +02:00
|
|
|
/*
|
|
|
|
* Obtain the current persistence of the existing index. We already hold
|
|
|
|
* lock on the index.
|
|
|
|
*/
|
|
|
|
irel = index_open(indOid, NoLock);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
if (irel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
|
|
|
|
{
|
|
|
|
ReindexPartitionedIndex(irel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-30 21:01:44 +02:00
|
|
|
persistence = irel->rd_rel->relpersistence;
|
|
|
|
index_close(irel, NoLock);
|
|
|
|
|
2015-05-15 13:09:57 +02:00
|
|
|
reindex_index(indOid, false, persistence, options);
|
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;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (relkind != RELKIND_INDEX &&
|
|
|
|
relkind != RELKIND_PARTITIONED_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()))
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, 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
|
2015-05-15 13:09:57 +02:00
|
|
|
ReindexTable(RangeVar *relation, int options)
|
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(). */
|
2018-03-31 01:33:42 +02:00
|
|
|
heapOid = RangeVarGetRelidExtended(relation, ShareLock, 0,
|
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 |
|
2015-05-15 13:09:57 +02:00
|
|
|
REINDEX_REL_CHECK_CONSTRAINTS,
|
|
|
|
options))
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-03-08 17:18:43 +01:00
|
|
|
* ReindexMultipleTables
|
|
|
|
* Recreate indexes of tables selected by objectName/objectKind.
|
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
|
|
|
*/
|
2015-03-08 17:18:43 +01:00
|
|
|
void
|
2015-05-15 13:09:57 +02:00
|
|
|
ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
|
|
|
|
int options)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2014-12-08 16:28:00 +01:00
|
|
|
Oid objectOid;
|
2004-08-29 07:07:03 +02:00
|
|
|
Relation relationRelation;
|
2000-04-12 19:17:23 +02:00
|
|
|
HeapScanDesc scan;
|
2015-03-08 17:18:43 +01:00
|
|
|
ScanKeyData scan_keys[1];
|
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;
|
2015-03-08 17:18:43 +01:00
|
|
|
int num_keys;
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2014-12-08 16:28:00 +01:00
|
|
|
AssertArg(objectName);
|
|
|
|
Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
|
|
|
|
objectKind == REINDEX_OBJECT_SYSTEM ||
|
|
|
|
objectKind == REINDEX_OBJECT_DATABASE);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2014-12-08 16:28:00 +01:00
|
|
|
/*
|
2015-03-08 17:18:43 +01:00
|
|
|
* Get OID of object to reindex, being the database currently being used
|
|
|
|
* by session for a database or for system catalogs, or the schema defined
|
|
|
|
* by caller. At the same time do permission checks that need different
|
|
|
|
* processing depending on the object type.
|
2014-12-08 16:28:00 +01:00
|
|
|
*/
|
|
|
|
if (objectKind == REINDEX_OBJECT_SCHEMA)
|
|
|
|
{
|
|
|
|
objectOid = get_namespace_oid(objectName, false);
|
2000-06-28 05:33:33 +02:00
|
|
|
|
2014-12-08 16:28:00 +01:00
|
|
|
if (!pg_namespace_ownercheck(objectOid, GetUserId()))
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
|
2014-12-08 16:28:00 +01:00
|
|
|
objectName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
objectOid = MyDatabaseId;
|
|
|
|
|
2015-03-08 17:18:43 +01:00
|
|
|
if (strcmp(objectName, get_database_name(objectOid)) != 0)
|
2014-12-08 16:28:00 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("can only reindex the currently open database")));
|
2015-03-08 17:18:43 +01:00
|
|
|
if (!pg_database_ownercheck(objectOid, GetUserId()))
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
|
2014-12-08 16:28:00 +01:00
|
|
|
objectName);
|
|
|
|
}
|
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,
|
2015-03-08 17:18:43 +01:00
|
|
|
"ReindexMultipleTables",
|
Add macros to make AllocSetContextCreate() calls simpler and safer.
I found that half a dozen (nearly 5%) of our AllocSetContextCreate calls
had typos in the context-sizing parameters. While none of these led to
especially significant problems, they did create minor inefficiencies,
and it's now clear that expecting people to copy-and-paste those calls
accurately is not a great idea. Let's reduce the risk of future errors
by introducing single macros that encapsulate the common use-cases.
Three such macros are enough to cover all but two special-purpose contexts;
those two calls can be left as-is, I think.
While this patch doesn't in itself improve matters for third-party
extensions, it doesn't break anything for them either, and they can
gradually adopt the simplified notation over time.
In passing, change TopMemoryContext to use the default allocation
parameters. Formerly it could only be extended 8K at a time. That was
probably reasonable when this code was written; but nowadays we create
many more contexts than we did then, so that it's not unusual to have a
couple hundred K in TopMemoryContext, even without considering various
dubious code that sticks other things there. There seems no good reason
not to let it use growing blocks like most other contexts.
Back-patch to 9.6, mostly because that's still close enough to HEAD that
it's easy to do so, and keeping the branches in sync can be expected to
avoid some future back-patching pain. The bugs fixed by these changes
don't seem to be significant enough to justify fixing them further back.
Discussion: <21072.1472321324@sss.pgh.pa.us>
2016-08-27 23:50:38 +02:00
|
|
|
ALLOCSET_SMALL_SIZES);
|
2000-02-18 10:30:20 +01:00
|
|
|
|
2003-09-24 20:54:02 +02:00
|
|
|
/*
|
2015-03-08 17:18:43 +01:00
|
|
|
* Define the search keys to find the objects to reindex. For a schema, we
|
|
|
|
* select target relations using relnamespace, something not necessary for
|
|
|
|
* a database-wide operation.
|
2014-12-08 16:28:00 +01:00
|
|
|
*/
|
|
|
|
if (objectKind == REINDEX_OBJECT_SCHEMA)
|
|
|
|
{
|
2014-12-11 23:54:05 +01:00
|
|
|
num_keys = 1;
|
2014-12-08 16:28:00 +01:00
|
|
|
ScanKeyInit(&scan_keys[0],
|
|
|
|
Anum_pg_class_relnamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectOid));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
num_keys = 0;
|
|
|
|
|
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);
|
2014-12-08 16:28:00 +01:00
|
|
|
scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
|
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);
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Oid relid = classtuple->oid;
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2014-12-11 23:54:05 +01:00
|
|
|
/*
|
2015-03-08 17:18:43 +01:00
|
|
|
* Only regular tables and matviews can have indexes, so ignore any
|
|
|
|
* other kind of relation.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
*
|
|
|
|
* It is tempting to also consider partitioned tables here, but that
|
|
|
|
* has the problem that if the children are in the same schema, they
|
|
|
|
* would be processed twice. Maybe we could have a separate list of
|
|
|
|
* partitioned tables, and expand that afterwards into relids,
|
|
|
|
* ignoring any duplicates.
|
2014-12-11 23:54:05 +01: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 */
|
2015-03-08 17:18:43 +01:00
|
|
|
if (objectKind == REINDEX_OBJECT_SYSTEM &&
|
|
|
|
!IsSystemClass(relid, classtuple))
|
2014-12-08 16:28:00 +01:00
|
|
|
continue;
|
2003-09-24 20:54:02 +02:00
|
|
|
|
2018-08-09 09:40:15 +02:00
|
|
|
/*
|
|
|
|
* The table can be reindexed if the user is superuser, the table
|
|
|
|
* owner, or the database/schema owner (but in the latter case, only
|
|
|
|
* if it's not a shared relation). pg_class_ownercheck includes the
|
|
|
|
* superuser case, and depending on objectKind we already know that
|
|
|
|
* the user has permission to run REINDEX on this database or schema
|
|
|
|
* per the permission checks at the beginning of this routine.
|
|
|
|
*/
|
|
|
|
if (classtuple->relisshared &&
|
|
|
|
!pg_class_ownercheck(relid, GetUserId()))
|
|
|
|
continue;
|
|
|
|
|
2015-03-08 17:18:43 +01:00
|
|
|
/* Save the list of relation OIDs in private context */
|
|
|
|
old = MemoryContextSwitchTo(private_context);
|
|
|
|
|
2014-12-08 16:28:00 +01:00
|
|
|
/*
|
2015-03-08 17:18:43 +01:00
|
|
|
* We always want to reindex pg_class first if it's selected to be
|
|
|
|
* reindexed. 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.
|
2014-12-08 16:28:00 +01:00
|
|
|
*/
|
2015-03-08 17:18:43 +01:00
|
|
|
if (relid == RelationRelationId)
|
|
|
|
relids = lcons_oid(relid, relids);
|
|
|
|
else
|
|
|
|
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 |
|
2015-05-15 13:09:57 +02:00
|
|
|
REINDEX_REL_CHECK_CONSTRAINTS,
|
|
|
|
options))
|
|
|
|
|
|
|
|
if (options & REINDEXOPT_VERBOSE)
|
|
|
|
ereport(INFO,
|
|
|
|
(errmsg("table \"%s.%s\" was reindexed",
|
|
|
|
get_namespace_name(get_rel_namespace(relid)),
|
|
|
|
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);
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ReindexPartitionedIndex
|
|
|
|
* Reindex each child of the given partitioned index.
|
|
|
|
*
|
|
|
|
* Not yet implemented.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ReindexPartitionedIndex(Relation parentIdx)
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("REINDEX is not yet implemented for partitioned indexes")));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert or delete an appropriate pg_inherits tuple to make the given index
|
|
|
|
* be a partition of the indicated parent index.
|
|
|
|
*
|
|
|
|
* This also corrects the pg_depend information for the affected index.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
|
|
|
|
{
|
|
|
|
Relation pg_inherits;
|
2018-04-26 20:47:16 +02:00
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
Oid partRelid = RelationGetRelid(partitionIdx);
|
|
|
|
HeapTuple tuple;
|
|
|
|
bool fix_dependencies;
|
|
|
|
|
|
|
|
/* Make sure this is an index */
|
|
|
|
Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX ||
|
|
|
|
partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan pg_inherits for rows linking our index to some parent.
|
|
|
|
*/
|
|
|
|
pg_inherits = relation_open(InheritsRelationId, RowExclusiveLock);
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_inherits_inhrelid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(partRelid));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_inherits_inhseqno,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(1));
|
|
|
|
scan = systable_beginscan(pg_inherits, InheritsRelidSeqnoIndexId, true,
|
|
|
|
NULL, 2, key);
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
{
|
|
|
|
if (parentOid == InvalidOid)
|
|
|
|
{
|
|
|
|
/*
|
2018-04-26 20:47:16 +02:00
|
|
|
* No pg_inherits row, and no parent wanted: nothing to do in this
|
|
|
|
* case.
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
*/
|
|
|
|
fix_dependencies = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
Datum values[Natts_pg_inherits];
|
|
|
|
bool isnull[Natts_pg_inherits];
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* No pg_inherits row exists, and we want a parent for this index,
|
|
|
|
* so insert it.
|
|
|
|
*/
|
|
|
|
values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(partRelid);
|
|
|
|
values[Anum_pg_inherits_inhparent - 1] =
|
|
|
|
ObjectIdGetDatum(parentOid);
|
|
|
|
values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(1);
|
|
|
|
memset(isnull, false, sizeof(isnull));
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(RelationGetDescr(pg_inherits),
|
|
|
|
values, isnull);
|
|
|
|
CatalogTupleInsert(pg_inherits, tuple);
|
|
|
|
|
|
|
|
fix_dependencies = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
if (parentOid == InvalidOid)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* There exists a pg_inherits row, which we want to clear; do so.
|
|
|
|
*/
|
|
|
|
CatalogTupleDelete(pg_inherits, &tuple->t_self);
|
|
|
|
fix_dependencies = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* A pg_inherits row exists. If it's the same we want, then we're
|
|
|
|
* good; if it differs, that amounts to a corrupt catalog and
|
|
|
|
* should not happen.
|
|
|
|
*/
|
|
|
|
if (inhForm->inhparent != parentOid)
|
|
|
|
{
|
|
|
|
/* unexpected: we should not get called in this case */
|
|
|
|
elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
|
|
|
|
inhForm->inhrelid, inhForm->inhparent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* already in the right state */
|
|
|
|
fix_dependencies = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* done with pg_inherits */
|
|
|
|
systable_endscan(scan);
|
|
|
|
relation_close(pg_inherits, RowExclusiveLock);
|
|
|
|
|
2018-10-22 04:04:48 +02:00
|
|
|
/* set relhassubclass if an index partition has been added to the parent */
|
|
|
|
if (OidIsValid(parentOid))
|
|
|
|
SetRelationHasSubclass(parentOid, true);
|
|
|
|
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (fix_dependencies)
|
|
|
|
{
|
|
|
|
ObjectAddress partIdx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert/delete pg_depend rows. If setting a parent, add an
|
|
|
|
* INTERNAL_AUTO dependency to the parent index; if making standalone,
|
|
|
|
* remove all existing rows and put back the regular dependency on the
|
|
|
|
* table.
|
|
|
|
*/
|
|
|
|
ObjectAddressSet(partIdx, RelationRelationId, partRelid);
|
|
|
|
|
|
|
|
if (OidIsValid(parentOid))
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
ObjectAddress parentIdx;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
|
|
|
|
recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_INTERNAL_AUTO);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-26 20:47:16 +02:00
|
|
|
ObjectAddress partitionTbl;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
|
|
|
ObjectAddressSet(partitionTbl, RelationRelationId,
|
|
|
|
partitionIdx->rd_index->indrelid);
|
|
|
|
|
|
|
|
deleteDependencyRecordsForClass(RelationRelationId, partRelid,
|
|
|
|
RelationRelationId,
|
|
|
|
DEPENDENCY_INTERNAL_AUTO);
|
|
|
|
|
|
|
|
recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_AUTO);
|
|
|
|
}
|
2018-03-20 15:19:41 +01:00
|
|
|
|
|
|
|
/* make our updates visible */
|
|
|
|
CommandCounterIncrement();
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
}
|
|
|
|
}
|