From 18ac85e9467038ec4cce03162f2d91e2efaa6bc4 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Tue, 5 Dec 2023 17:08:11 +0530
Subject: [PATCH 04/27] A newly created partition inherits indentity property

The partitions of a partitioned table are integral part of the
partitioned table. A partition inherits identity columns from the
partitioned table. An indentity column of a partition shares the
identity space with the corresponding column of the partitioned table.
In other words, the same identity column across all partitions of a
partitioned table share the same identity space.  This is effected by
sharing the same underlying sequence.

When INSERTing directly into a partition, sequence associated with the
topmost partitioned table is used to calculate the value of the
corresponding identity column.

In regular inheritance, Identity columns and their properties in a child
table are independent of those in its parent tables. A child table does
not inherit identity columns or their properties automatically from the
parent.

Ashutosh Bapat
---
 src/backend/commands/tablecmds.c       |  9 +++++++++
 src/backend/rewrite/rewriteHandler.c   | 19 ++++++++++++++++++-
 src/test/regress/expected/identity.out | 15 ++++++++++++++-
 src/test/regress/sql/identity.sql      | 10 +++++++++-
 4 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 18046a0d69..06f6301a57 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2855,6 +2855,15 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
 					def->is_not_null = true;
 				def->storage = attribute->attstorage;
 				def->generated = attribute->attgenerated;
+
+				/*
+				 * Regular inheritance children are independent enough not to
+				 * inherit identity columns. But partitions are integral part
+				 * of a partitioned table and inherit identity column.
+				 */
+				if (is_partition)
+					def->identity = attribute->attidentity;
+
 				if (CompressionMethodIsValid(attribute->attcompression))
 					def->compression =
 						pstrdup(GetCompressionMethodName(attribute->attcompression));
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 41a362310a..d5f1993665 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "catalog/dependency.h"
 #include "catalog/pg_type.h"
+#include "catalog/partition.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
@@ -1234,8 +1235,24 @@ build_column_default(Relation rel, int attrno)
 	if (att_tup->attidentity)
 	{
 		NextValueExpr *nve = makeNode(NextValueExpr);
+		Oid			reloid;
 
-		nve->seqid = getIdentitySequence(RelationGetRelid(rel), attrno, false);
+		/*
+		 * The identity sequence is associated with the topmost partitioned
+		 * table.
+		 */
+		if (rel->rd_rel->relispartition)
+		{
+			List	   *ancestors =
+				get_partition_ancestors(RelationGetRelid(rel));
+
+			reloid = llast_oid(ancestors);
+			list_free(ancestors);
+		}
+		else
+			reloid = RelationGetRelid(rel);
+
+		nve->seqid = getIdentitySequence(reloid, attrno, false);
 		nve->typeId = att_tup->atttypid;
 
 		return (Node *) nve;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 7c6e87e8a5..20803a1d24 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -539,7 +539,20 @@ CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint);
 CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error
 ERROR:  identity columns are not supported on typed tables
 DROP TYPE itest_type CASCADE;
--- table partitions (currently not supported)
+-- table partitions
+-- partitions inherit identity column and share sequence
+CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
+INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
+  tableoid  |     f1     |       f2        | f3 
+------------+------------+-----------------+----
+ pitest1_p1 | 07-02-2016 | from pitest1    |  1
+ pitest1_p1 | 07-03-2016 | from pitest1_p1 |  2
+(2 rows)
+
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index 9b8db2e4a3..3660fa6a28 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -331,8 +331,16 @@ CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
 DROP TYPE itest_type CASCADE;
 
 
--- table partitions (currently not supported)
+-- table partitions
 
+-- partitions inherit identity column and share sequence
+CREATE TABLE pitest1 (f1 date NOT NULL, f2 text, f3 bigint generated always as identity) PARTITION BY RANGE (f1);
+CREATE TABLE pitest1_p1 PARTITION OF pitest1 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+INSERT into pitest1(f1, f2) VALUES ('2016-07-2', 'from pitest1');
+INSERT into pitest1_p1 (f1, f2) VALUES ('2016-07-3', 'from pitest1_p1');
+SELECT tableoid::regclass, f1, f2, f3 FROM pitest1;
+
+-- partition with identity column of its own is not allowed
 CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
 CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
-- 
2.25.1

