Skip to content

Commit 8b08f7d

Browse files
committed
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/[email protected]
1 parent 1ef61dd commit 8b08f7d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3172
-182
lines changed

doc/src/sgml/catalogs.sgml

+23
Original file line numberDiff line numberDiff line change
@@ -2995,6 +2995,29 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
29952995
</listitem>
29962996
</varlistentry>
29972997

2998+
<varlistentry>
2999+
<term><symbol>DEPENDENCY_INTERNAL_AUTO</symbol> (<literal>I</literal>)</term>
3000+
<listitem>
3001+
<para>
3002+
The dependent object was created as part of creation of the
3003+
referenced object, and is really just a part of its internal
3004+
implementation. A <command>DROP</command> of the dependent object
3005+
will be disallowed outright (we'll tell the user to issue a
3006+
<command>DROP</command> against the referenced object, instead).
3007+
While a regular internal dependency will prevent
3008+
the dependent object from being dropped while any such dependencies
3009+
remain, <literal>DEPENDENCY_INTERNAL_AUTO</literal> will allow such
3010+
a drop as long as the object can be found by following any of such
3011+
dependencies.
3012+
Example: an index on a partition is made internal-auto-dependent on
3013+
both the partition itself as well as on the index on the parent
3014+
partitioned table; so the partition index is dropped together with
3015+
either the partition it indexes, or with the parent index it is
3016+
attached to.
3017+
</para>
3018+
</listitem>
3019+
</varlistentry>
3020+
29983021
<varlistentry>
29993022
<term><symbol>DEPENDENCY_EXTENSION</symbol> (<literal>e</literal>)</term>
30003023
<listitem>

doc/src/sgml/ref/alter_index.sgml

+14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ PostgreSQL documentation
2323
<synopsis>
2424
ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
2525
ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET TABLESPACE <replaceable class="parameter">tablespace_name</replaceable>
26+
ALTER INDEX <replaceable class="parameter">name</replaceable> ATTACH PARTITION <replaceable class="parameter">index_name</replaceable>
2627
ALTER INDEX <replaceable class="parameter">name</replaceable> DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable>
2728
ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
2829
ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET ( <replaceable class="parameter">storage_parameter</replaceable> [, ... ] )
@@ -75,6 +76,19 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
7576
</listitem>
7677
</varlistentry>
7778

79+
<varlistentry>
80+
<term><literal>ATTACH PARTITION</literal></term>
81+
<listitem>
82+
<para>
83+
Causes the named index to become attached to the altered index.
84+
The named index must be on a partition of the table containing the
85+
index being altered, and have an equivalent definition. An attached
86+
index cannot be dropped by itself, and will automatically be dropped
87+
if its parent index is dropped.
88+
</para>
89+
</listitem>
90+
</varlistentry>
91+
7892
<varlistentry>
7993
<term><literal>DEPENDS ON EXTENSION</literal></term>
8094
<listitem>

doc/src/sgml/ref/alter_table.sgml

+6-2
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,10 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
805805
as a partition of the target table. The table can be attached
806806
as a partition for specific values using <literal>FOR VALUES
807807
</literal> or as a default partition by using <literal>DEFAULT
808-
</literal>.
808+
</literal>. For each index in the target table, a corresponding
809+
one will be created in the attached table; or, if an equivalent
810+
index already exists, will be attached to the target table's index,
811+
as if <command>ALTER INDEX ATTACH PARTITION</command> had been executed.
809812
</para>
810813

811814
<para>
@@ -866,7 +869,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
866869
<para>
867870
This form detaches specified partition of the target table. The detached
868871
partition continues to exist as a standalone table, but no longer has any
869-
ties to the table from which it was detached.
872+
ties to the table from which it was detached. Any indexes that were
873+
attached to the target table's indexes are detached.
870874
</para>
871875
</listitem>
872876
</varlistentry>

doc/src/sgml/ref/create_index.sgml

+32-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
24+
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
2525
( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
2626
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
2727
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -151,6 +151,16 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
151151
</listitem>
152152
</varlistentry>
153153

154+
<varlistentry>
155+
<term><literal>ONLY</literal></term>
156+
<listitem>
157+
<para>
158+
Indicates not to recurse creating indexes on partitions, if the
159+
table is partitioned. The default is to recurse.
160+
</para>
161+
</listitem>
162+
</varlistentry>
163+
154164
<varlistentry>
155165
<term><replaceable class="parameter">table_name</replaceable></term>
156166
<listitem>
@@ -545,6 +555,27 @@ Indexes:
545555
linkend="xindex"/>.
546556
</para>
547557

558+
<para>
559+
When <literal>CREATE INDEX</literal> is invoked on a partitioned
560+
table, the default behavior is to recurse to all partitions to ensure
561+
they all have matching indexes.
562+
Each partition is first checked to determine whether an equivalent
563+
index already exists, and if so, that index will become attached as a
564+
partition index to the index being created, which will become its
565+
parent index.
566+
If no matching index exists, a new index will be created and
567+
automatically attached; the name of the new index in each partition
568+
will be determined as if no index name had been specified in the
569+
command.
570+
If the <literal>ONLY</literal> option is specified, no recursion
571+
is done, and the index is marked invalid
572+
(<command>ALTER INDEX ... ATTACH PARTITION</command> turns the index
573+
valid, once all partitions acquire the index.) Note, however, that
574+
any partition that is created in the future using
575+
<command>CREATE TABLE ... PARTITION OF</command> will automatically
576+
contain the index regardless of whether this option was specified.
577+
</para>
578+
548579
<para>
549580
For index methods that support ordered scans (currently, only B-tree),
550581
the optional clauses <literal>ASC</literal>, <literal>DESC</literal>, <literal>NULLS

doc/src/sgml/ref/reindex.sgml

+5
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } <replacea
231231
reindex anything.
232232
</para>
233233

234+
<para>
235+
Reindexing partitioned tables or partitioned indexes is not supported.
236+
Each individual partition can be reindexed separately instead.
237+
</para>
238+
234239
</refsect1>
235240

236241
<refsect1>

src/backend/access/common/reloptions.c

+1
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
993993
options = view_reloptions(datum, false);
994994
break;
995995
case RELKIND_INDEX:
996+
case RELKIND_PARTITIONED_INDEX:
996997
options = index_reloptions(amoptions, datum, false);
997998
break;
998999
case RELKIND_FOREIGN_TABLE:

src/backend/access/heap/heapam.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,8 @@ heap_open(Oid relationId, LOCKMODE lockmode)
12931293

12941294
r = relation_open(relationId, lockmode);
12951295

1296-
if (r->rd_rel->relkind == RELKIND_INDEX)
1296+
if (r->rd_rel->relkind == RELKIND_INDEX ||
1297+
r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
12971298
ereport(ERROR,
12981299
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
12991300
errmsg("\"%s\" is an index",
@@ -1321,7 +1322,8 @@ heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
13211322

13221323
r = relation_openrv(relation, lockmode);
13231324

1324-
if (r->rd_rel->relkind == RELKIND_INDEX)
1325+
if (r->rd_rel->relkind == RELKIND_INDEX ||
1326+
r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
13251327
ereport(ERROR,
13261328
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
13271329
errmsg("\"%s\" is an index",
@@ -1353,7 +1355,8 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
13531355

13541356
if (r)
13551357
{
1356-
if (r->rd_rel->relkind == RELKIND_INDEX)
1358+
if (r->rd_rel->relkind == RELKIND_INDEX ||
1359+
r->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
13571360
ereport(ERROR,
13581361
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
13591362
errmsg("\"%s\" is an index",

src/backend/access/index/indexam.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ index_open(Oid relationId, LOCKMODE lockmode)
154154

155155
r = relation_open(relationId, lockmode);
156156

157-
if (r->rd_rel->relkind != RELKIND_INDEX)
157+
if (r->rd_rel->relkind != RELKIND_INDEX &&
158+
r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
158159
ereport(ERROR,
159160
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
160161
errmsg("\"%s\" is not an index",

src/backend/bootstrap/bootparse.y

+2
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ Boot_DeclareIndexStmt:
321321
DefineIndex(relationId,
322322
stmt,
323323
$4,
324+
InvalidOid,
324325
false,
325326
false,
326327
false,
@@ -365,6 +366,7 @@ Boot_DeclareUniqueIndexStmt:
365366
DefineIndex(relationId,
366367
stmt,
367368
$5,
369+
InvalidOid,
368370
false,
369371
false,
370372
false,

src/backend/catalog/aclchk.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -1824,7 +1824,8 @@ ExecGrant_Relation(InternalGrant *istmt)
18241824
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
18251825

18261826
/* Not sensible to grant on an index */
1827-
if (pg_class_tuple->relkind == RELKIND_INDEX)
1827+
if (pg_class_tuple->relkind == RELKIND_INDEX ||
1828+
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
18281829
ereport(ERROR,
18291830
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
18301831
errmsg("\"%s\" is an index",
@@ -5405,7 +5406,8 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
54055406
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
54065407

54075408
/* Indexes don't have permissions */
5408-
if (pg_class_tuple->relkind == RELKIND_INDEX)
5409+
if (pg_class_tuple->relkind == RELKIND_INDEX ||
5410+
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
54095411
return;
54105412

54115413
/* Composite types don't have permissions either */
@@ -5690,7 +5692,8 @@ removeExtObjInitPriv(Oid objoid, Oid classoid)
56905692
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
56915693

56925694
/* Indexes don't have permissions */
5693-
if (pg_class_tuple->relkind == RELKIND_INDEX)
5695+
if (pg_class_tuple->relkind == RELKIND_INDEX ||
5696+
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
56945697
return;
56955698

56965699
/* Composite types don't have permissions either */

src/backend/catalog/dependency.c

+13-1
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ findDependentObjects(const ObjectAddress *object,
582582
/* FALL THRU */
583583

584584
case DEPENDENCY_INTERNAL:
585+
case DEPENDENCY_INTERNAL_AUTO:
585586

586587
/*
587588
* This object is part of the internal implementation of
@@ -633,6 +634,14 @@ findDependentObjects(const ObjectAddress *object,
633634
* transform this deletion request into a delete of this
634635
* owning object.
635636
*
637+
* For INTERNAL_AUTO dependencies, we don't enforce this;
638+
* in other words, we don't follow the links back to the
639+
* owning object.
640+
*/
641+
if (foundDep->deptype == DEPENDENCY_INTERNAL_AUTO)
642+
break;
643+
644+
/*
636645
* First, release caller's lock on this object and get
637646
* deletion lock on the owning object. (We must release
638647
* caller's lock to avoid deadlock against a concurrent
@@ -675,6 +684,7 @@ findDependentObjects(const ObjectAddress *object,
675684
/* And we're done here. */
676685
systable_endscan(scan);
677686
return;
687+
678688
case DEPENDENCY_PIN:
679689

680690
/*
@@ -762,6 +772,7 @@ findDependentObjects(const ObjectAddress *object,
762772
case DEPENDENCY_AUTO_EXTENSION:
763773
subflags = DEPFLAG_AUTO;
764774
break;
775+
case DEPENDENCY_INTERNAL_AUTO:
765776
case DEPENDENCY_INTERNAL:
766777
subflags = DEPFLAG_INTERNAL;
767778
break;
@@ -1109,7 +1120,8 @@ doDeletion(const ObjectAddress *object, int flags)
11091120
{
11101121
char relKind = get_rel_relkind(object->objectId);
11111122

1112-
if (relKind == RELKIND_INDEX)
1123+
if (relKind == RELKIND_INDEX ||
1124+
relKind == RELKIND_PARTITIONED_INDEX)
11131125
{
11141126
bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY) != 0);
11151127

src/backend/catalog/heap.c

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ heap_create(const char *relname,
294294
case RELKIND_COMPOSITE_TYPE:
295295
case RELKIND_FOREIGN_TABLE:
296296
case RELKIND_PARTITIONED_TABLE:
297+
case RELKIND_PARTITIONED_INDEX:
297298
create_storage = false;
298299

299300
/*

0 commit comments

Comments
 (0)