Skip to content

Commit

Permalink
Add support for partitioned tables and indexes in REINDEX
Browse files Browse the repository at this point in the history
Until now, REINDEX was not able to work with partitioned tables and
indexes, forcing users to reindex partitions one by one.  This extends
REINDEX INDEX and REINDEX TABLE so as they can accept a partitioned
index and table in input, respectively, to reindex all the partitions
assigned to them with physical storage (foreign tables, partitioned
tables and indexes are then discarded).

This shares some logic with schema and database REINDEX as each
partition gets processed in its own transaction after building a list of
relations to work on.  This choice has the advantage to minimize the
number of invalid indexes to one partition with REINDEX CONCURRENTLY in
the event a cancellation or failure in-flight, as the only indexes
handled at once in a single REINDEX CONCURRENTLY loop are the ones from
the partition being working on.

Isolation tests are added to emulate some cases I bumped into while
developing this feature, particularly with the concurrent drop of a
leaf partition reindexed.  However, this is rather limited as LOCK would
cause REINDEX to block in the first transaction building the list of
partitions.

Per its multi-transaction nature, this new flavor cannot run in a
transaction block, similarly to REINDEX SCHEMA, SYSTEM and DATABASE.

Author: Justin Pryzby, Michael Paquier
Reviewed-by: Anastasia Lubennikova
Discussion: https://postgr.es/m/db12e897-73ff-467e-94cb-4af03705435f.adger.lj@alibaba-inc.com
  • Loading branch information
michaelpq committed Sep 8, 2020
1 parent a547e68 commit a6642b3
Show file tree
Hide file tree
Showing 13 changed files with 656 additions and 92 deletions.
21 changes: 21 additions & 0 deletions contrib/postgres_fdw/expected/postgres_fdw.out
Original file line number Diff line number Diff line change
Expand Up @@ -4044,6 +4044,27 @@ SELECT f_test(100);

DROP FUNCTION f_test(int);
-- ===================================================================
-- REINDEX
-- ===================================================================
-- remote table is not created here
CREATE FOREIGN TABLE reindex_foreign (c1 int, c2 int)
SERVER loopback2 OPTIONS (table_name 'reindex_local');
REINDEX TABLE reindex_foreign; -- error
ERROR: "reindex_foreign" is not a table or materialized view
REINDEX TABLE CONCURRENTLY reindex_foreign; -- error
ERROR: "reindex_foreign" is not a table or materialized view
DROP FOREIGN TABLE reindex_foreign;
-- partitions and foreign tables
CREATE TABLE reind_fdw_parent (c1 int) PARTITION BY RANGE (c1);
CREATE TABLE reind_fdw_0_10 PARTITION OF reind_fdw_parent
FOR VALUES FROM (0) TO (10);
CREATE FOREIGN TABLE reind_fdw_10_20 PARTITION OF reind_fdw_parent
FOR VALUES FROM (10) TO (20)
SERVER loopback OPTIONS (table_name 'reind_local_10_20');
REINDEX TABLE reind_fdw_parent; -- ok
REINDEX TABLE CONCURRENTLY reind_fdw_parent; -- ok
DROP TABLE reind_fdw_parent;
-- ===================================================================
-- conversion error
-- ===================================================================
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
Expand Down
20 changes: 20 additions & 0 deletions contrib/postgres_fdw/sql/postgres_fdw.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,26 @@ $$ LANGUAGE plpgsql;
SELECT f_test(100);
DROP FUNCTION f_test(int);

-- ===================================================================
-- REINDEX
-- ===================================================================
-- remote table is not created here
CREATE FOREIGN TABLE reindex_foreign (c1 int, c2 int)
SERVER loopback2 OPTIONS (table_name 'reindex_local');
REINDEX TABLE reindex_foreign; -- error
REINDEX TABLE CONCURRENTLY reindex_foreign; -- error
DROP FOREIGN TABLE reindex_foreign;
-- partitions and foreign tables
CREATE TABLE reind_fdw_parent (c1 int) PARTITION BY RANGE (c1);
CREATE TABLE reind_fdw_0_10 PARTITION OF reind_fdw_parent
FOR VALUES FROM (0) TO (10);
CREATE FOREIGN TABLE reind_fdw_10_20 PARTITION OF reind_fdw_parent
FOR VALUES FROM (10) TO (20)
SERVER loopback OPTIONS (table_name 'reind_local_10_20');
REINDEX TABLE reind_fdw_parent; -- ok
REINDEX TABLE CONCURRENTLY reind_fdw_parent; -- ok
DROP TABLE reind_fdw_parent;

-- ===================================================================
-- conversion error
-- ===================================================================
Expand Down
13 changes: 10 additions & 3 deletions doc/src/sgml/ref/reindex.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { IN
<term><literal>INDEX</literal></term>
<listitem>
<para>
Recreate the specified index.
Recreate the specified index. This form of <command>REINDEX</command>
cannot be executed inside a transaction block when used with a
partitioned index.
</para>
</listitem>
</varlistentry>
Expand All @@ -99,6 +101,8 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { IN
<para>
Recreate all indexes of the specified table. If the table has a
secondary <quote>TOAST</quote> table, that is reindexed as well.
This form of <command>REINDEX</command> cannot be executed inside a
transaction block when used with a partitioned table.
</para>
</listitem>
</varlistentry>
Expand Down Expand Up @@ -259,8 +263,11 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { IN
</para>

<para>
Reindexing partitioned tables or partitioned indexes is not supported.
Each individual partition can be reindexed separately instead.
Reindexing partitioned indexes or partitioned tables is supported
with <command>REINDEX INDEX</command> or <command>REINDEX TABLE</command>,
respectively. Each partition of the specified partitioned relation is
reindexed in a separate transaction. Those commands cannot be used inside
a transaction block when working on a partitioned table or index.
</para>

<refsect2 id="sql-reindex-concurrently" xreflabel="Rebuilding Indexes Concurrently">
Expand Down
25 changes: 10 additions & 15 deletions src/backend/catalog/index.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
Expand Down Expand Up @@ -3486,11 +3487,12 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
iRel->rd_rel->relam);

/*
* The case of reindexing partitioned tables and indexes is handled
* differently by upper layers, so this case shouldn't arise.
* Partitioned indexes should never get processed here, as they have no
* physical storage.
*/
if (iRel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
elog(ERROR, "unsupported relation kind for index \"%s\"",
elog(ERROR, "cannot reindex partitioned index \"%s.%s\"",
get_namespace_name(RelationGetNamespace(iRel)),
RelationGetRelationName(iRel));

/*
Expand Down Expand Up @@ -3707,20 +3709,13 @@ reindex_relation(Oid relid, int flags, int options)
return false;

/*
* This may be useful when implemented someday; but that day is not today.
* For now, avoid erroring out when called in a multi-table context
* (REINDEX SCHEMA) and happen to come across a partitioned table. The
* partitions may be reindexed on their own anyway.
* Partitioned tables should never get processed here, as they have no
* physical storage.
*/
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
ereport(WARNING,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("REINDEX of partitioned tables is not yet implemented, skipping \"%s\"",
RelationGetRelationName(rel))));
table_close(rel, ShareLock);
return false;
}
elog(ERROR, "cannot reindex partitioned table \"%s.%s\"",
get_namespace_name(RelationGetNamespace(rel)),
RelationGetRelationName(rel));

toast_relid = rel->rd_rel->reltoastrelid;

Expand Down

0 comments on commit a6642b3

Please sign in to comment.