From 93cf9663834c39b8228ded84b4c16e70dd477501 Mon Sep 17 00:00:00 2001
From: Takayuki Tsunakawa <tsunakawa.takay@fujitsu.com>
Date: Wed, 2 Dec 2020 17:04:12 +0900
Subject: [PATCH v2] Make ALTER TABLE SET LOGGED/UNLOGGED on a partitioned
 table recurse

---
 src/backend/commands/tablecmds.c          | 12 ++++
 src/test/regress/expected/alter_table.out | 92 +++++++++++++++++++++++++++++++
 src/test/regress/sql/alter_table.sql      | 37 +++++++++++++
 3 files changed, 141 insertions(+)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a29c14b..1392736 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4246,6 +4246,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 				tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
 				tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
 			}
+			if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+				ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
 			pass = AT_PASS_MISC;
 			break;
 		case AT_SetUnLogged:	/* SET UNLOGGED */
@@ -4261,6 +4263,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 				tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
 				tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
 			}
+			if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+				ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
 			pass = AT_PASS_MISC;
 			break;
 		case AT_DropOids:		/* SET WITHOUT OIDS */
@@ -5713,6 +5717,14 @@ ATSimpleRecursion(List **wqueue, Relation rel,
 				continue;
 			/* find_all_inheritors already got lock */
 			childrel = relation_open(childrelid, NoLock);
+			/* Skip foreign partitions when SET LOGGED/UNLOGGED */
+			if ((cmd->subtype == AT_SetLogged ||
+				 cmd->subtype == AT_SetUnLogged) &&
+				childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+			{
+				relation_close(childrel, NoLock);
+				continue;
+			}
 			CheckTableNotInUse(childrel, "ALTER TABLE");
 			ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
 			relation_close(childrel, NoLock);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 0ce6ee4..e9e21a4 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3472,6 +3472,98 @@ ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
 DROP TABLE logged3;
 DROP TABLE logged2;
 DROP TABLE logged1;
+-- set partitioned table logged/unlogged
+CREATE TABLE part_logged_parent (key integer PRIMARY KEY, val text)
+	PARTITION BY RANGE (key);
+CREATE TABLE part_logged_child1 PARTITION OF part_logged_parent
+	FOR VALUES FROM (1) TO (10);
+CREATE TABLE part_logged_child2 PARTITION OF part_logged_parent
+	FOR VALUES FROM (11) TO (20);
+ALTER TABLE part_logged_parent SET UNLOGGED; -- children become unlogged
+CREATE TABLE part_logged_child3 (LIKE part_logged_parent);
+ALTER TABLE part_logged_parent ATTACH PARTITION part_logged_child3
+	FOR VALUES FROM (21) TO (30); -- child3 remains logged
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+              relname               | relkind | relpersistence 
+------------------------------------+---------+----------------
+ part_logged_child1                 | r       | u
+ part_logged_child1_pkey            | i       | u
+ part_logged_child2                 | r       | u
+ part_logged_child2_pkey            | i       | u
+ part_logged_child3                 | r       | p
+ part_logged_child3_pkey            | i       | p
+ part_logged_parent                 | p       | p
+ part_logged_parent_pkey            | I       | p
+ toast index for part_logged_child1 | i       | u
+ toast index for part_logged_child2 | i       | u
+ toast index for part_logged_child3 | i       | p
+ toast table for part_logged_child1 | t       | u
+ toast table for part_logged_child2 | t       | u
+ toast table for part_logged_child3 | t       | p
+(14 rows)
+
+ALTER TABLE part_logged_parent SET LOGGED; -- children become logged
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+              relname               | relkind | relpersistence 
+------------------------------------+---------+----------------
+ part_logged_child1                 | r       | p
+ part_logged_child1_pkey            | i       | p
+ part_logged_child2                 | r       | p
+ part_logged_child2_pkey            | i       | p
+ part_logged_child3                 | r       | p
+ part_logged_child3_pkey            | i       | p
+ part_logged_parent                 | p       | p
+ part_logged_parent_pkey            | I       | p
+ toast index for part_logged_child1 | i       | p
+ toast index for part_logged_child2 | i       | p
+ toast index for part_logged_child3 | i       | p
+ toast table for part_logged_child1 | t       | p
+ toast table for part_logged_child2 | t       | p
+ toast table for part_logged_child3 | t       | p
+(14 rows)
+
+CREATE TABLE logged_fk_child (key integer PRIMARY KEY, fk integer REFERENCES part_logged_child2);
+ALTER TABLE part_logged_parent SET UNLOGGED; -- fails because logged table references a child
+ERROR:  could not change table "part_logged_child2" to unlogged because it references logged table "logged_fk_child"
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+              relname               | relkind | relpersistence 
+------------------------------------+---------+----------------
+ part_logged_child1                 | r       | p
+ part_logged_child1_pkey            | i       | p
+ part_logged_child2                 | r       | p
+ part_logged_child2_pkey            | i       | p
+ part_logged_child3                 | r       | p
+ part_logged_child3_pkey            | i       | p
+ part_logged_parent                 | p       | p
+ part_logged_parent_pkey            | I       | p
+ toast index for part_logged_child1 | i       | p
+ toast index for part_logged_child2 | i       | p
+ toast index for part_logged_child3 | i       | p
+ toast table for part_logged_child1 | t       | p
+ toast table for part_logged_child2 | t       | p
+ toast table for part_logged_child3 | t       | p
+(14 rows)
+
+DROP TABLE logged_fk_child;
+DROP TABLE part_logged_parent;
 -- test ADD COLUMN IF NOT EXISTS
 CREATE TABLE test_add_column(c1 integer);
 \d test_add_column
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 4cc55d8..18c0a69 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2188,6 +2188,43 @@ ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
 DROP TABLE logged3;
 DROP TABLE logged2;
 DROP TABLE logged1;
+-- set partitioned table logged/unlogged
+CREATE TABLE part_logged_parent (key integer PRIMARY KEY, val text)
+	PARTITION BY RANGE (key);
+CREATE TABLE part_logged_child1 PARTITION OF part_logged_parent
+	FOR VALUES FROM (1) TO (10);
+CREATE TABLE part_logged_child2 PARTITION OF part_logged_parent
+	FOR VALUES FROM (11) TO (20);
+ALTER TABLE part_logged_parent SET UNLOGGED; -- children become unlogged
+CREATE TABLE part_logged_child3 (LIKE part_logged_parent);
+ALTER TABLE part_logged_parent ATTACH PARTITION part_logged_child3
+	FOR VALUES FROM (21) TO (30); -- child3 remains logged
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+ALTER TABLE part_logged_parent SET LOGGED; -- children become logged
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+CREATE TABLE logged_fk_child (key integer PRIMARY KEY, fk integer REFERENCES part_logged_child2);
+ALTER TABLE part_logged_parent SET UNLOGGED; -- fails because logged table references a child
+-- check relpersistence
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast table for ' || r.relname, t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^part_logged'
+UNION ALL
+SELECT 'toast index for ' || r.relname, ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^part_logged'
+ORDER BY relname;
+DROP TABLE logged_fk_child;
+DROP TABLE part_logged_parent;
 
 -- test ADD COLUMN IF NOT EXISTS
 CREATE TABLE test_add_column(c1 integer);
-- 
2.10.1

