Drop the rule against included index columns duplicating key columns.
The initial version of the included-index-column feature stated that included columns couldn't be the same as any key column of the index. While it'd be pretty silly to do that, since the included column would be entirely redundant, we've never prohibited redundant index columns before so it's not very consistent to do so here. Moreover, the prohibition was itself badly implemented, so that it failed to reject columns that were effectively identical but not spelled quite alike, as reported by Aditya Toshniwal. (Moreover, it's not hard to imagine that for some non-btree index types, such cases would be non-silly anyhow: the index might use a lossy representation for key columns but be able to support retrieval of the original form of included columns.) Hence, let's just drop the prohibition. In passing, do some copy-editing on the documentation for the included-column feature. Yugo Nagata; documentation and test corrections by me Discussion: https://postgr.es/m/CAM9w-_mhBCys4fQNfaiQKTRrVWtoFrZ-wXmDuE9Nj5y-Y7aDKQ@mail.gmail.com
This commit is contained in:
parent
65976cd86a
commit
eb00eb4e8c
@ -3753,15 +3753,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<entry><type>int2</type></entry>
|
<entry><type>int2</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The total number of columns in the index (duplicates
|
<entry>The total number of columns in the index (duplicates
|
||||||
<literal>pg_class.relnatts</literal>). This number includes both key and included attributes.</entry>
|
<literal>pg_class.relnatts</literal>); this number includes both key and included attributes</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>indnkeyatts</structfield></entry>
|
<entry><structfield>indnkeyatts</structfield></entry>
|
||||||
<entry><type>int2</type></entry>
|
<entry><type>int2</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The number of key columns in the index. "Key columns" are ordinary
|
<entry>The number of <firstterm>key columns</firstterm> in the index,
|
||||||
index columns (as opposed to "included" columns).</entry>
|
not counting any <firstterm>included columns</firstterm>, which are
|
||||||
|
merely stored and do not participate in the index semantics</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
@ -3867,7 +3868,8 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
This is an array of <structfield>indnatts</structfield> values that
|
This is an array of <structfield>indnatts</structfield> values that
|
||||||
indicate which table columns this index indexes. For example a value
|
indicate which table columns this index indexes. For example a value
|
||||||
of <literal>1 3</literal> would mean that the first and the third table
|
of <literal>1 3</literal> would mean that the first and the third table
|
||||||
columns make up the index key. A zero in this array indicates that the
|
columns make up the index entries. Key columns come before non-key
|
||||||
|
(included) columns. A zero in this array indicates that the
|
||||||
corresponding index attribute is an expression over the table columns,
|
corresponding index attribute is an expression over the table columns,
|
||||||
rather than a simple column reference.
|
rather than a simple column reference.
|
||||||
</entry>
|
</entry>
|
||||||
@ -3878,9 +3880,10 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<entry><type>oidvector</type></entry>
|
<entry><type>oidvector</type></entry>
|
||||||
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
|
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
|
||||||
<entry>
|
<entry>
|
||||||
For each column in the index key, this contains the OID of the
|
For each column in the index key
|
||||||
collation to use for the index, or zero if the column is not
|
(<structfield>indnkeyatts</structfield> values), this contains the OID
|
||||||
of a collatable data type.
|
of the collation to use for the index, or zero if the column is not of
|
||||||
|
a collatable data type.
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
@ -3889,8 +3892,9 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<entry><type>oidvector</type></entry>
|
<entry><type>oidvector</type></entry>
|
||||||
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
|
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
|
||||||
<entry>
|
<entry>
|
||||||
For each column in the index key, this contains the OID of
|
For each column in the index key
|
||||||
the operator class to use. See
|
(<structfield>indnkeyatts</structfield> values), this contains the OID
|
||||||
|
of the operator class to use. See
|
||||||
<link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link> for details.
|
<link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link> for details.
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
@ -3900,7 +3904,7 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<entry><type>int2vector</type></entry>
|
<entry><type>int2vector</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>
|
<entry>
|
||||||
This is an array of <structfield>indnatts</structfield> values that
|
This is an array of <structfield>indnkeyatts</structfield> values that
|
||||||
store per-column flag bits. The meaning of the bits is defined by
|
store per-column flag bits. The meaning of the bits is defined by
|
||||||
the index's access method.
|
the index's access method.
|
||||||
</entry>
|
</entry>
|
||||||
|
@ -112,10 +112,10 @@ typedef struct IndexAmRoutine
|
|||||||
bool ampredlocks;
|
bool ampredlocks;
|
||||||
/* does AM support parallel scan? */
|
/* does AM support parallel scan? */
|
||||||
bool amcanparallel;
|
bool amcanparallel;
|
||||||
/* type of data stored in index, or InvalidOid if variable */
|
|
||||||
Oid amkeytype;
|
|
||||||
/* does AM support columns included with clause INCLUDE? */
|
/* does AM support columns included with clause INCLUDE? */
|
||||||
bool amcaninclude;
|
bool amcaninclude;
|
||||||
|
/* type of data stored in index, or InvalidOid if variable */
|
||||||
|
Oid amkeytype;
|
||||||
|
|
||||||
/* interface functions */
|
/* interface functions */
|
||||||
ambuild_function ambuild;
|
ambuild_function ambuild;
|
||||||
@ -988,7 +988,8 @@ amparallelrescan (IndexScanDesc scan);
|
|||||||
multiple entries with identical keys. An access method that supports this
|
multiple entries with identical keys. An access method that supports this
|
||||||
feature sets <structfield>amcanunique</structfield> true.
|
feature sets <structfield>amcanunique</structfield> true.
|
||||||
(At present, only b-tree supports it.) Columns listed in the
|
(At present, only b-tree supports it.) Columns listed in the
|
||||||
<literal>INCLUDE</literal> clause are not used to enforce uniqueness.
|
<literal>INCLUDE</literal> clause are not considered when enforcing
|
||||||
|
uniqueness.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -639,7 +639,7 @@ CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);
|
|||||||
or the uniqueness of the combined values of more than one column.
|
or the uniqueness of the combined values of more than one column.
|
||||||
<synopsis>
|
<synopsis>
|
||||||
CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <optional>, ...</optional>)
|
CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <optional>, ...</optional>)
|
||||||
[ INCLUDE (<replaceable>column</replaceable> <optional>, ...</optional>) ];
|
<optional> INCLUDE (<replaceable>column</replaceable> <optional>, ...</optional>) </optional>;
|
||||||
</synopsis>
|
</synopsis>
|
||||||
Currently, only B-tree indexes can be declared unique.
|
Currently, only B-tree indexes can be declared unique.
|
||||||
</para>
|
</para>
|
||||||
@ -648,9 +648,9 @@ CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</repla
|
|||||||
When an index is declared unique, multiple table rows with equal
|
When an index is declared unique, multiple table rows with equal
|
||||||
indexed values are not allowed. Null values are not considered
|
indexed values are not allowed. Null values are not considered
|
||||||
equal. A multicolumn unique index will only reject cases where all
|
equal. A multicolumn unique index will only reject cases where all
|
||||||
indexed columns are equal in multiple rows. Columns listed in the
|
indexed columns are equal. Columns listed in
|
||||||
<literal>INCLUDE</literal> clause aren't used to enforce constraints
|
the <literal>INCLUDE</literal> clause, if any, aren't considered when
|
||||||
(UNIQUE, PRIMARY KEY, etc).
|
determining whether index entries are equal.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -149,25 +149,28 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The optional <literal>INCLUDE</literal> clause specifies a
|
The optional <literal>INCLUDE</literal> clause specifies a
|
||||||
list of columns which will be included as a non-key part in the index.
|
list of columns which will be included in the index
|
||||||
Columns listed in this clause cannot also be present as index key columns.
|
as <firstterm>non-key</firstterm> columns. A non-key column cannot
|
||||||
The <literal>INCLUDE</literal> columns exist solely to
|
be used in an index scan search qualification, and it is disregarded
|
||||||
allow more queries to benefit from <firstterm>index-only scans</firstterm>
|
for purposes of any uniqueness or exclusion constraint enforced by
|
||||||
by including the values of the specified columns in the index. These values
|
the index. However, an index-only scan can return the contents of
|
||||||
would otherwise have to be obtained by reading the table's heap.
|
non-key columns without having to visit the index's table, since
|
||||||
|
they are available directly from the index entry. Thus, addition of
|
||||||
|
non-key columns allows index-only scans to be used for queries that
|
||||||
|
otherwise could not use them.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In <literal>UNIQUE</literal> indexes, uniqueness is only enforced
|
It's wise to be conservative about adding non-key columns to an
|
||||||
for key columns. Columns listed in the <literal>INCLUDE</literal>
|
index, especially wide columns. If an index tuple exceeds the
|
||||||
clause have no effect on uniqueness enforcement. Other constraints
|
maximum size allowed for the index type, data insertion will fail.
|
||||||
(<literal>PRIMARY KEY</literal> and <literal>EXCLUDE</literal>) work
|
In any case, non-key columns duplicate data from the index's table
|
||||||
the same way.
|
and bloat the size of the index, thus potentially slowing searches.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Columns listed in the <literal>INCLUDE</literal> clause don't need
|
Columns listed in the <literal>INCLUDE</literal> clause don't need
|
||||||
appropriate operator classes; the clause can contain non-key index
|
appropriate operator classes; the clause can include
|
||||||
columns whose data types don't have operator classes defined for
|
columns whose data types don't have operator classes defined for
|
||||||
a given access method.
|
a given access method.
|
||||||
</para>
|
</para>
|
||||||
@ -181,15 +184,8 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
|
|||||||
Currently, only the B-tree index access method supports this feature.
|
Currently, only the B-tree index access method supports this feature.
|
||||||
In B-tree indexes, the values of columns listed in the
|
In B-tree indexes, the values of columns listed in the
|
||||||
<literal>INCLUDE</literal> clause are included in leaf tuples which
|
<literal>INCLUDE</literal> clause are included in leaf tuples which
|
||||||
are linked to the heap tuples, but are not included into pivot tuples
|
correspond to heap tuples, but are not included in upper-level
|
||||||
used for tree navigation. Therefore, moving columns from the list of
|
index entries used for tree navigation.
|
||||||
key columns to the <literal>INCLUDE</literal> clause can slightly
|
|
||||||
reduce index size and improve the tree branching factor.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Indexes with columns listed in the <literal>INCLUDE</literal> clause
|
|
||||||
are also called <quote>covering indexes</quote>.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -789,8 +785,8 @@ CREATE UNIQUE INDEX title_idx ON films (title);
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
To create a unique B-tree index on the column <literal>title</literal>
|
To create a unique B-tree index on the column <literal>title</literal>
|
||||||
and included columns <literal>director</literal> and <literal>rating</literal>
|
with included columns <literal>director</literal>
|
||||||
in the table <literal>films</literal>:
|
and <literal>rating</literal> in the table <literal>films</literal>:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);
|
CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
@ -811,9 +811,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
|
|||||||
one or more columns on which the uniqueness is not enforced.
|
one or more columns on which the uniqueness is not enforced.
|
||||||
Note that although the constraint is not enforced on the included columns,
|
Note that although the constraint is not enforced on the included columns,
|
||||||
it still depends on them. Consequently, some operations on these columns
|
it still depends on them. Consequently, some operations on these columns
|
||||||
(e.g. <literal>DROP COLUMN</literal>) can cause cascade constraint and
|
(e.g. <literal>DROP COLUMN</literal>) can cause cascaded constraint and
|
||||||
index deletion. See paragraph about <literal>INCLUDE</literal> in
|
index deletion.
|
||||||
<xref linkend="sql-createindex"/> for more information.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -858,9 +857,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
|
|||||||
of columns to be specified which will be included in the non-key portion
|
of columns to be specified which will be included in the non-key portion
|
||||||
of the index. Although uniqueness is not enforced on the included columns,
|
of the index. Although uniqueness is not enforced on the included columns,
|
||||||
the constraint still depends on them. Consequently, some operations on the
|
the constraint still depends on them. Consequently, some operations on the
|
||||||
included columns (e.g. <literal>DROP COLUMN</literal>) can cause cascade
|
included columns (e.g. <literal>DROP COLUMN</literal>) can cause cascaded
|
||||||
constraint and index deletion. See paragraph about <literal>INCLUDE</literal>
|
constraint and index deletion.
|
||||||
in <xref linkend="sql-createindex"/> for more information.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -369,11 +369,6 @@ DefineIndex(Oid relationId,
|
|||||||
Snapshot snapshot;
|
Snapshot snapshot;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (list_intersection(stmt->indexParams, stmt->indexIncludingParams) != NIL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
||||||
errmsg("included columns must not intersect with key columns")));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* count key attributes in index
|
* count key attributes in index
|
||||||
*/
|
*/
|
||||||
@ -596,7 +591,7 @@ DefineIndex(Oid relationId,
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("access method \"%s\" does not support unique indexes",
|
errmsg("access method \"%s\" does not support unique indexes",
|
||||||
accessMethodName)));
|
accessMethodName)));
|
||||||
if (list_length(stmt->indexIncludingParams) > 0 && !amRoutine->amcaninclude)
|
if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("access method \"%s\" does not support included columns",
|
errmsg("access method \"%s\" does not support included columns",
|
||||||
|
@ -7,17 +7,17 @@
|
|||||||
-- Regular index with included columns
|
-- Regular index with included columns
|
||||||
CREATE TABLE tbl_include_reg (c1 int, c2 int, c3 int, c4 box);
|
CREATE TABLE tbl_include_reg (c1 int, c2 int, c3 int, c4 box);
|
||||||
INSERT INTO tbl_include_reg SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
|
INSERT INTO tbl_include_reg SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
|
||||||
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg using btree (c1, c2) INCLUDE (c3,c4);
|
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg (c1, c2) INCLUDE (c3, c4);
|
||||||
-- must fail because of intersection of key and included columns
|
-- duplicate column is pretty pointless, but we allow it anyway
|
||||||
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg using btree (c1, c2) INCLUDE (c1,c3);
|
CREATE INDEX ON tbl_include_reg (c1, c2) INCLUDE (c1, c3);
|
||||||
ERROR: included columns must not intersect with key columns
|
|
||||||
SELECT pg_get_indexdef(i.indexrelid)
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname;
|
WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname;
|
||||||
pg_get_indexdef
|
pg_get_indexdef
|
||||||
--------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------
|
||||||
|
CREATE INDEX tbl_include_reg_c1_c2_c11_c3_idx ON public.tbl_include_reg USING btree (c1, c2) INCLUDE (c1, c3)
|
||||||
CREATE INDEX tbl_include_reg_idx ON public.tbl_include_reg USING btree (c1, c2) INCLUDE (c3, c4)
|
CREATE INDEX tbl_include_reg_idx ON public.tbl_include_reg USING btree (c1, c2) INCLUDE (c3, c4)
|
||||||
(1 row)
|
(2 rows)
|
||||||
|
|
||||||
-- Unique index and unique constraint
|
-- Unique index and unique constraint
|
||||||
CREATE TABLE tbl_include_unique1 (c1 int, c2 int, c3 int, c4 box);
|
CREATE TABLE tbl_include_unique1 (c1 int, c2 int, c3 int, c4 box);
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
-- Regular index with included columns
|
-- Regular index with included columns
|
||||||
CREATE TABLE tbl_include_reg (c1 int, c2 int, c3 int, c4 box);
|
CREATE TABLE tbl_include_reg (c1 int, c2 int, c3 int, c4 box);
|
||||||
INSERT INTO tbl_include_reg SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
|
INSERT INTO tbl_include_reg SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
|
||||||
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg using btree (c1, c2) INCLUDE (c3,c4);
|
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg (c1, c2) INCLUDE (c3, c4);
|
||||||
-- must fail because of intersection of key and included columns
|
-- duplicate column is pretty pointless, but we allow it anyway
|
||||||
CREATE INDEX tbl_include_reg_idx ON tbl_include_reg using btree (c1, c2) INCLUDE (c1,c3);
|
CREATE INDEX ON tbl_include_reg (c1, c2) INCLUDE (c1, c3);
|
||||||
SELECT pg_get_indexdef(i.indexrelid)
|
SELECT pg_get_indexdef(i.indexrelid)
|
||||||
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid
|
||||||
WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname;
|
WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user