From 6322032b472e6b1a76e0ca9326974e5774371fb9 Mon Sep 17 00:00:00 2001
From: Alexey Kondratov <kondratov.aleksey@gmail.com>
Date: Mon, 1 Feb 2021 15:20:29 +0300
Subject: [PATCH v10 2/2] Change tablespace of partitioned indexes during
 REINDEX.

There are some doubts about proper locking of partitions
here. AccessExclusiveLock is surely enough, but may be
a reason of deadlock. While ShareUpdateExclusiveLock
or ShareLock may be not sufficient.

Citing Michael:

"Are you sure that this does not represent a risk of deadlocks as EAL
is not taken consistently across all the partitions?  A second issue
here is that this breaks the assumption of REINDEX CONCURRENTLY kicked
on partitioned relations that should use ShareUpdateExclusiveLock for
all its steps.  This would make the first transaction invasive for the
user, but we don't want that."
---
 doc/src/sgml/ref/reindex.sgml             |  4 +--
 src/backend/commands/indexcmds.c          | 36 ++++++++++++++++++++++
 src/test/regress/input/tablespace.source  | 10 ++----
 src/test/regress/output/tablespace.source | 37 +++++++++++++----------
 4 files changed, 61 insertions(+), 26 deletions(-)

diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml
index d84f8c74c8..d77ca8c29b 100644
--- a/doc/src/sgml/ref/reindex.sgml
+++ b/doc/src/sgml/ref/reindex.sgml
@@ -321,9 +321,7 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { IN
    relation, and <literal>TABLESPACE</literal> was specified, then it may have
    moved indexes on some partitions to the new tablespace. Re-running the command
    will reindex all partitions and move previously-unprocessed indexes to the new
-   tablespace. Note that partitioned indexes are not moved to the
-   <replaceable class="parameter">new_tablespace</replaceable>, only leaf
-   partitions holding data get reindexed and moved there.
+   tablespace.
   </para>
 
   <refsect2 id="sql-reindex-concurrently" xreflabel="Rebuilding Indexes Concurrently">
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index c9c059bb58..99a9613f17 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -3018,6 +3018,42 @@ ReindexPartitions(Oid relid, ReindexParams *params, bool isTopLevel)
 		char		partkind = get_rel_relkind(partoid);
 		MemoryContext old_context;
 
+		/*
+		 * Foreign tables and partitioned relations are not themselves
+		 * reindexed - leaf partitions are processed directly.  But any
+		 * tablespace change is recorded in the catalog for partitioned
+		 * relations.
+		 */
+		if (partkind == RELKIND_PARTITIONED_INDEX)
+		{
+			Relation iRel = index_open(partoid, AccessExclusiveLock);
+
+			if (CheckRelationTableSpaceMove(iRel, params->tablespaceOid))
+				SetRelationTableSpace(iRel,
+									  params->tablespaceOid,
+									  InvalidOid);
+			index_close(iRel, NoLock);
+		}
+		else if (partkind == RELKIND_PARTITIONED_TABLE)
+		{
+			Relation rel = table_open(partoid, ShareLock);
+			List	*indexIds = RelationGetIndexList(rel);
+			ListCell *lc;
+
+			table_close(rel, NoLock);
+			foreach (lc, indexIds)
+			{
+				Oid indexid = lfirst_oid(lc);
+				Relation iRel = index_open(indexid, AccessExclusiveLock);
+
+				if (CheckRelationTableSpaceMove(iRel, params->tablespaceOid))
+					SetRelationTableSpace(iRel,
+										  params->tablespaceOid,
+										  InvalidOid);
+				index_close(iRel, NoLock);
+			}
+		}
+
 		/*
 		 * This discards partitioned tables, partitioned indexes and foreign
 		 * tables.
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 0d1d48721b..c8a3098bf6 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -51,14 +51,15 @@ REINDEX (TABLESPACE pg_default) DATABASE regression; -- ok with warning
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
 
--- partitioned indexes are not directly reindexed
+-- partitioned indexes are not directly reindexed, but we still have to set proper
+-- tablespace in pg_class for them
 REINDEX (TABLESPACE regress_tblspace) TABLE regress_tblspace_test_pt; -- ok
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_pt_idx_1; -- ok
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_pt_idx_2; -- ok
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
 
--- indexes for new partitions should be created in the old tablespace
+-- indexes for new partitions should be created in the new tablespace
 CREATE TABLE regress_tblspace_test_pt_2
   PARTITION OF regress_tblspace_test_pt
   FOR VALUES WITH (modulus 2, remainder 1);
@@ -68,11 +69,6 @@ CREATE TABLE regress_tblspace_test_pt_1_1
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
 
--- reindex partitioned table
-REINDEX (TABLESPACE regress_tblspace) TABLE regress_tblspace_test_pt; -- ok
-SELECT relname FROM pg_class
-WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
-
 -- check REINDEX with TABLESPACE change
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_tbl_idx; -- ok
 REINDEX (TABLESPACE regress_tblspace) TABLE regress_tblspace_test_tbl; -- ok
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 5f0b372b43..b7a9016054 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -60,17 +60,22 @@ WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspa
 ---------
 (0 rows)
 
--- partitioned indexes are not directly reindexed
+-- partitioned indexes are not directly reindexed, but we still have to set proper
+-- tablespace in pg_class for them
 REINDEX (TABLESPACE regress_tblspace) TABLE regress_tblspace_test_pt; -- ok
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_pt_idx_1; -- ok
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_pt_idx_2; -- ok
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
- relname 
----------
-(0 rows)
+               relname               
+-------------------------------------
+ regress_tblspace_test_pt_idx_1
+ regress_tblspace_test_pt_idx_2
+ regress_tblspace_test_pt_1_col1_idx
+ regress_tblspace_test_pt_1_col2_idx
+(4 rows)
 
--- indexes for new partitions should be created in the old tablespace
+-- indexes for new partitions should be created in the new tablespace
 CREATE TABLE regress_tblspace_test_pt_2
   PARTITION OF regress_tblspace_test_pt
   FOR VALUES WITH (modulus 2, remainder 1);
@@ -78,22 +83,18 @@ CREATE TABLE regress_tblspace_test_pt_1_1
   PARTITION OF regress_tblspace_test_pt_1
   FOR VALUES WITH (modulus 2, remainder 0);
 SELECT relname FROM pg_class
-WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
- relname 
----------
-(0 rows)
-
--- reindex partitioned table
-REINDEX (TABLESPACE regress_tblspace) TABLE regress_tblspace_test_pt; -- ok
-SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace');
                 relname                
 ---------------------------------------
+ regress_tblspace_test_pt_idx_1
+ regress_tblspace_test_pt_idx_2
  regress_tblspace_test_pt_2_col1_idx
  regress_tblspace_test_pt_2_col2_idx
  regress_tblspace_test_pt_1_1_col1_idx
+ regress_tblspace_test_pt_1_col1_idx
  regress_tblspace_test_pt_1_1_col2_idx
-(4 rows)
+ regress_tblspace_test_pt_1_col2_idx
+(8 rows)
 
 -- check REINDEX with TABLESPACE change
 REINDEX (TABLESPACE regress_tblspace) INDEX regress_tblspace_test_tbl_idx; -- ok
@@ -116,10 +117,14 @@ ORDER BY relname;
 ---------------------------------------
  regress_tblspace_test_pt_1_1_col1_idx
  regress_tblspace_test_pt_1_1_col2_idx
+ regress_tblspace_test_pt_1_col1_idx
+ regress_tblspace_test_pt_1_col2_idx
  regress_tblspace_test_pt_2_col1_idx
  regress_tblspace_test_pt_2_col2_idx
+ regress_tblspace_test_pt_idx_1
+ regress_tblspace_test_pt_idx_2
  regress_tblspace_test_tbl_idx
-(5 rows)
+(9 rows)
 
 CREATE TEMP TABLE old_reindex_info AS SELECT relname, relfilenode FROM pg_class
 WHERE relname IN (SELECT relid::name FROM pg_partition_tree('regress_tblspace_test_pt_idx_1'::regclass) WHERE isleaf = true);
@@ -337,7 +342,7 @@ REINDEX (TABLESPACE pg_default) TABLE testschema.part;
  a      | integer |           |          | 
 Partition key: LIST (a)
 Indexes:
-    "part_a_idx" btree (a), tablespace "regress_tblspace"
+    "part_a_idx" btree (a)
 Number of partitions: 2 (Use \d+ to list them.)
 
 \d testschema.part1
-- 
2.20.1

