Commit a6642b3a authored by Michael Paquier's avatar Michael Paquier

Add support for partitioned tables and indexes in REINDEX

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
parent a547e686
......@@ -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;
......
......@@ -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
-- ===================================================================
......
......@@ -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>
......@@ -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>
......@@ -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">
......
......@@ -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"
......@@ -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));
/*
......@@ -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;
......
This diff is collapsed.
......@@ -926,10 +926,12 @@ standard_ProcessUtility(PlannedStmt *pstmt,
switch (stmt->kind)
{
case REINDEX_OBJECT_INDEX:
ReindexIndex(stmt->relation, stmt->options);
ReindexIndex(stmt->relation, stmt->options,
isTopLevel);
break;
case REINDEX_OBJECT_TABLE:
ReindexTable(stmt->relation, stmt->options);
ReindexTable(stmt->relation, stmt->options,
isTopLevel);
break;
case REINDEX_OBJECT_SCHEMA:
case REINDEX_OBJECT_SYSTEM:
......
......@@ -34,8 +34,8 @@ extern ObjectAddress DefineIndex(Oid relationId,
bool check_not_in_use,
bool skip_build,
bool quiet);
extern void ReindexIndex(RangeVar *indexRelation, int options);
extern Oid ReindexTable(RangeVar *relation, int options);
extern void ReindexIndex(RangeVar *indexRelation, int options, bool isTopLevel);
extern Oid ReindexTable(RangeVar *relation, int options, bool isTopLevel);
extern void ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
int options);
extern char *makeObjectName(const char *name1, const char *name2,
......
Parsed test spec with 3 sessions
starting permutation: begin1 lockexcl1 reindex2 drop3 end1
step begin1: BEGIN;
step lockexcl1: LOCK reind_conc_parent IN ACCESS EXCLUSIVE MODE;
step reindex2: REINDEX TABLE reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockexcl1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockexcl1: LOCK reind_conc_parent IN ACCESS EXCLUSIVE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockshare1 reindex2 drop3 end1
step begin1: BEGIN;
step lockshare1: LOCK reind_conc_parent IN SHARE MODE;
step reindex2: REINDEX TABLE reind_conc_parent;
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step drop3: <... completed>
starting permutation: begin1 lockshare1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockshare1: LOCK reind_conc_parent IN SHARE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockupdate1 reindex2 drop3 end1
step begin1: BEGIN;
step lockupdate1: LOCK reind_conc_parent IN SHARE UPDATE EXCLUSIVE MODE;
step reindex2: REINDEX TABLE reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockupdate1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockupdate1: LOCK reind_conc_parent IN SHARE UPDATE EXCLUSIVE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockpartexcl1 reindex2 drop3 end1
step begin1: BEGIN;
step lockpartexcl1: LOCK reind_conc_10_20 IN ACCESS EXCLUSIVE MODE;
step reindex2: REINDEX TABLE reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockpartexcl1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockpartexcl1: LOCK reind_conc_10_20 IN ACCESS EXCLUSIVE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockpartshare1 reindex2 drop3 end1
step begin1: BEGIN;
step lockpartshare1: LOCK reind_conc_10_20 IN SHARE MODE;
step reindex2: REINDEX TABLE reind_conc_parent;
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step drop3: <... completed>
starting permutation: begin1 lockpartshare1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockpartshare1: LOCK reind_conc_10_20 IN SHARE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockpartupdate1 reindex2 drop3 end1
step begin1: BEGIN;
step lockpartupdate1: LOCK reind_conc_10_20 IN SHARE UPDATE EXCLUSIVE MODE;
step reindex2: REINDEX TABLE reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex2: <... completed>
step drop3: <... completed>
starting permutation: begin1 lockpartupdate1 reindex_conc2 drop3 end1
step begin1: BEGIN;
step lockpartupdate1: LOCK reind_conc_10_20 IN SHARE UPDATE EXCLUSIVE MODE;
step reindex_conc2: REINDEX TABLE CONCURRENTLY reind_conc_parent; <waiting ...>
step drop3: DROP TABLE reind_conc_10_20; <waiting ...>
step end1: COMMIT;
step reindex_conc2: <... completed>
step drop3: <... completed>
......@@ -50,6 +50,7 @@ test: lock-committed-keyupdate
test: update-locked-tuple
test: reindex-concurrently
test: reindex-schema
test: reindex-partitions
test: propagate-lock-delete
test: tuplelock-conflict
test: tuplelock-update
......
# REINDEX with partitioned tables
#
# Ensure that concurrent and non-concurrent operations work correctly when
# a REINDEX is performed on a partitioned table or index.
# In the cases dealt with here, partition leaves are dropped in parallel of
# a REINDEX. DROP TABLE gets blocked by the first transaction of REINDEX
# building the list of partitions, so it will finish executing once REINDEX
# is done.
setup
{
CREATE TABLE reind_conc_parent (id int) PARTITION BY RANGE (id);
CREATE TABLE reind_conc_0_10 PARTITION OF reind_conc_parent
FOR VALUES FROM (0) TO (10);
CREATE TABLE reind_conc_10_20 PARTITION OF reind_conc_parent
FOR VALUES FROM (10) TO (20);
INSERT INTO reind_conc_parent VALUES (generate_series(0, 19));
}
teardown
{
DROP TABLE reind_conc_parent;
}
session "s1"
step "begin1" { BEGIN; }
step "lockexcl1" { LOCK reind_conc_parent IN ACCESS EXCLUSIVE MODE; }
step "lockshare1" { LOCK reind_conc_parent IN SHARE MODE; }
step "lockupdate1" { LOCK reind_conc_parent IN SHARE UPDATE EXCLUSIVE MODE; }
step "lockpartexcl1" { LOCK reind_conc_10_20 IN ACCESS EXCLUSIVE MODE; }
step "lockpartshare1" { LOCK reind_conc_10_20 IN SHARE MODE; }
step "lockpartupdate1" { LOCK reind_conc_10_20 IN SHARE UPDATE EXCLUSIVE MODE; }
step "end1" { COMMIT; }
session "s2"
step "reindex2" { REINDEX TABLE reind_conc_parent; }
step "reindex_conc2" { REINDEX TABLE CONCURRENTLY reind_conc_parent; }
session "s3"
step "drop3" { DROP TABLE reind_conc_10_20; }
# An existing partition leaf is dropped after reindex is done when the
# parent is locked.
permutation "begin1" "lockexcl1" "reindex2" "drop3" "end1"
permutation "begin1" "lockexcl1" "reindex_conc2" "drop3" "end1"
permutation "begin1" "lockshare1" "reindex2" "drop3" "end1"
permutation "begin1" "lockshare1" "reindex_conc2" "drop3" "end1"
permutation "begin1" "lockupdate1" "reindex2" "drop3" "end1"
permutation "begin1" "lockupdate1" "reindex_conc2" "drop3" "end1"
# An existing partition leaf is dropped after reindex is done when this
# leaf is locked.
permutation "begin1" "lockpartexcl1" "reindex2" "drop3" "end1"
permutation "begin1" "lockpartexcl1" "reindex_conc2" "drop3" "end1"
permutation "begin1" "lockpartshare1" "reindex2" "drop3" "end1"
permutation "begin1" "lockpartshare1" "reindex_conc2" "drop3" "end1"
permutation "begin1" "lockpartupdate1" "reindex2" "drop3" "end1"
permutation "begin1" "lockpartupdate1" "reindex_conc2" "drop3" "end1"
......@@ -2194,18 +2194,6 @@ SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_ind
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
(5 rows)
-- REINDEX fails for partitioned indexes
REINDEX INDEX concur_reindex_part_index_10;
ERROR: REINDEX is not yet implemented for partitioned indexes
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10;
ERROR: REINDEX is not yet implemented for partitioned indexes
-- REINDEX is a no-op for partitioned tables
REINDEX TABLE concur_reindex_part_10;
WARNING: REINDEX of partitioned tables is not yet implemented, skipping "concur_reindex_part_10"
NOTICE: table "concur_reindex_part_10" has no indexes to reindex
REINDEX TABLE CONCURRENTLY concur_reindex_part_10;
WARNING: REINDEX of partitioned tables is not yet implemented, skipping "concur_reindex_part_10"
NOTICE: table "concur_reindex_part_10" has no indexes that can be reindexed concurrently
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
ORDER BY relid, level;
relid | parentrelid | level
......@@ -2318,6 +2306,152 @@ SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_ind
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
(5 rows)
-- REINDEX for partitioned indexes
-- REINDEX TABLE fails for partitioned indexes
-- Top-most parent index
REINDEX TABLE concur_reindex_part_index; -- error
ERROR: "concur_reindex_part_index" is not a table or materialized view
REINDEX TABLE CONCURRENTLY concur_reindex_part_index; -- error
ERROR: "concur_reindex_part_index" is not a table or materialized view
-- Partitioned index with no leaves
REINDEX TABLE concur_reindex_part_index_10; -- error
ERROR: "concur_reindex_part_index_10" is not a table or materialized view
REINDEX TABLE CONCURRENTLY concur_reindex_part_index_10; -- error
ERROR: "concur_reindex_part_index_10" is not a table or materialized view
-- Cannot run in a transaction block
BEGIN;
REINDEX INDEX concur_reindex_part_index;
ERROR: REINDEX INDEX cannot run inside a transaction block
CONTEXT: while reindexing partitioned index "public.concur_reindex_part_index"
ROLLBACK;
-- Helper functions to track changes of relfilenodes in a partition tree.
-- Create a table tracking the relfilenode state.
CREATE OR REPLACE FUNCTION create_relfilenode_part(relname text, indname text)
RETURNS VOID AS
$func$
BEGIN
EXECUTE format('
CREATE TABLE %I AS
SELECT oid, relname, relfilenode, relkind, reltoastrelid
FROM pg_class
WHERE oid IN
(SELECT relid FROM pg_partition_tree(''%I''));',
relname, indname);
END
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION compare_relfilenode_part(tabname text)
RETURNS TABLE (relname name, relkind "char", state text) AS
$func$
BEGIN
RETURN QUERY EXECUTE
format(
'SELECT b.relname,
b.relkind,
CASE WHEN a.relfilenode = b.relfilenode THEN ''relfilenode is unchanged''
ELSE ''relfilenode has changed'' END
-- Do not join with OID here as CONCURRENTLY changes it.
FROM %I b JOIN pg_class a ON b.relname = a.relname
ORDER BY 1;', tabname);
END
$func$ LANGUAGE plpgsql;
-- Check that expected relfilenodes are changed, non-concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
create_relfilenode_part
-------------------------
(1 row)
REINDEX INDEX concur_reindex_part_index;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
relname | relkind | state
-------------------------------+---------+--------------------------
concur_reindex_part_index | I | relfilenode is unchanged
concur_reindex_part_index_0 | I | relfilenode is unchanged
concur_reindex_part_index_0_1 | i | relfilenode has changed
concur_reindex_part_index_0_2 | i | relfilenode has changed
concur_reindex_part_index_10 | I | relfilenode is unchanged
(5 rows)
DROP TABLE reindex_index_status;
-- concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
create_relfilenode_part
-------------------------
(1 row)
REINDEX INDEX CONCURRENTLY concur_reindex_part_index;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
relname | relkind | state
-------------------------------+---------+--------------------------
concur_reindex_part_index | I | relfilenode is unchanged
concur_reindex_part_index_0 | I | relfilenode is unchanged
concur_reindex_part_index_0_1 | i | relfilenode has changed
concur_reindex_part_index_0_2 | i | relfilenode has changed
concur_reindex_part_index_10 | I | relfilenode is unchanged
(5 rows)
DROP TABLE reindex_index_status;
-- REINDEX for partitioned tables
-- REINDEX INDEX fails for partitioned tables
-- Top-most parent
REINDEX INDEX concur_reindex_part; -- error
ERROR: "concur_reindex_part" is not an index
REINDEX INDEX CONCURRENTLY concur_reindex_part; -- error
ERROR: "concur_reindex_part" is not an index
-- Partitioned with no leaves
REINDEX INDEX concur_reindex_part_10; -- error
ERROR: "concur_reindex_part_10" is not an index
REINDEX INDEX CONCURRENTLY concur_reindex_part_10; -- error
ERROR: "concur_reindex_part_10" is not an index
-- Cannot run in a transaction block
BEGIN;
REINDEX TABLE concur_reindex_part;
ERROR: REINDEX TABLE cannot run inside a transaction block
CONTEXT: while reindexing partitioned table "public.concur_reindex_part"
ROLLBACK;
-- Check that expected relfilenodes are changed, non-concurrent case.
-- Note that the partition tree changes of the *indexes* need to be checked.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
create_relfilenode_part
-------------------------
(1 row)
REINDEX TABLE concur_reindex_part;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
relname | relkind | state
-------------------------------+---------+--------------------------
concur_reindex_part_index | I | relfilenode is unchanged
concur_reindex_part_index_0 | I | relfilenode is unchanged
concur_reindex_part_index_0_1 | i | relfilenode has changed
concur_reindex_part_index_0_2 | i | relfilenode has changed
concur_reindex_part_index_10 | I | relfilenode is unchanged
(5 rows)
DROP TABLE reindex_index_status;
-- concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
create_relfilenode_part
-------------------------
(1 row)
REINDEX TABLE CONCURRENTLY concur_reindex_part;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
relname | relkind | state
-------------------------------+---------+--------------------------
concur_reindex_part_index | I | relfilenode is unchanged
concur_reindex_part_index_0 | I | relfilenode is unchanged
concur_reindex_part_index_0_1 | i | relfilenode has changed
concur_reindex_part_index_0_2 | i | relfilenode has changed
concur_reindex_part_index_10 | I | relfilenode is unchanged
(5 rows)
DROP TABLE reindex_index_status;
DROP FUNCTION create_relfilenode_part;
DROP FUNCTION compare_relfilenode_part;
-- Cleanup of partition tree used for REINDEX test.
DROP TABLE concur_reindex_part;
-- Check errors
-- Cannot run inside a transaction block
......
......@@ -903,12 +903,6 @@ CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1);
ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2;
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
ORDER BY relid, level;
-- REINDEX fails for partitioned indexes
REINDEX INDEX concur_reindex_part_index_10;
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10;
-- REINDEX is a no-op for partitioned tables
REINDEX TABLE concur_reindex_part_10;
REINDEX TABLE CONCURRENTLY concur_reindex_part_10;
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
ORDER BY relid, level;
-- REINDEX should preserve dependencies of partition tree.
......@@ -948,6 +942,88 @@ WHERE classid = 'pg_class'::regclass AND
ORDER BY 1, 2;
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
ORDER BY relid, level;
-- REINDEX for partitioned indexes
-- REINDEX TABLE fails for partitioned indexes
-- Top-most parent index
REINDEX TABLE concur_reindex_part_index; -- error
REINDEX TABLE CONCURRENTLY concur_reindex_part_index; -- error
-- Partitioned index with no leaves
REINDEX TABLE concur_reindex_part_index_10; -- error
REINDEX TABLE CONCURRENTLY concur_reindex_part_index_10; -- error
-- Cannot run in a transaction block
BEGIN;
REINDEX INDEX concur_reindex_part_index;
ROLLBACK;
-- Helper functions to track changes of relfilenodes in a partition tree.
-- Create a table tracking the relfilenode state.
CREATE OR REPLACE FUNCTION create_relfilenode_part(relname text, indname text)
RETURNS VOID AS
$func$
BEGIN
EXECUTE format('
CREATE TABLE %I AS
SELECT oid, relname, relfilenode, relkind, reltoastrelid
FROM pg_class
WHERE oid IN
(SELECT relid FROM pg_partition_tree(''%I''));',
relname, indname);
END
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION compare_relfilenode_part(tabname text)
RETURNS TABLE (relname name, relkind "char", state text) AS
$func$
BEGIN
RETURN QUERY EXECUTE
format(
'SELECT b.relname,
b.relkind,
CASE WHEN a.relfilenode = b.relfilenode THEN ''relfilenode is unchanged''
ELSE ''relfilenode has changed'' END
-- Do not join with OID here as CONCURRENTLY changes it.
FROM %I b JOIN pg_class a ON b.relname = a.relname
ORDER BY 1;', tabname);
END
$func$ LANGUAGE plpgsql;
-- Check that expected relfilenodes are changed, non-concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
REINDEX INDEX concur_reindex_part_index;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
DROP TABLE reindex_index_status;
-- concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
REINDEX INDEX CONCURRENTLY concur_reindex_part_index;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
DROP TABLE reindex_index_status;
-- REINDEX for partitioned tables
-- REINDEX INDEX fails for partitioned tables
-- Top-most parent
REINDEX INDEX concur_reindex_part; -- error
REINDEX INDEX CONCURRENTLY concur_reindex_part; -- error
-- Partitioned with no leaves
REINDEX INDEX concur_reindex_part_10; -- error
REINDEX INDEX CONCURRENTLY concur_reindex_part_10; -- error
-- Cannot run in a transaction block
BEGIN;
REINDEX TABLE concur_reindex_part;
ROLLBACK;
-- Check that expected relfilenodes are changed, non-concurrent case.
-- Note that the partition tree changes of the *indexes* need to be checked.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
REINDEX TABLE concur_reindex_part;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
DROP TABLE reindex_index_status;
-- concurrent case.
SELECT create_relfilenode_part('reindex_index_status', 'concur_reindex_part_index');
REINDEX TABLE CONCURRENTLY concur_reindex_part;
SELECT * FROM compare_relfilenode_part('reindex_index_status');
DROP TABLE reindex_index_status;
DROP FUNCTION create_relfilenode_part;
DROP FUNCTION compare_relfilenode_part;
-- Cleanup of partition tree used for REINDEX test.
DROP TABLE concur_reindex_part;
-- Check errors
......
......@@ -2038,6 +2038,7 @@ RegProcedure
Regis
RegisNode
RegisteredBgWorker
ReindexErrorInfo
ReindexObjectType
ReindexStmt
ReindexType
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment