v9-0002-alter-table-set-compression.patch

application/octet-stream

Filename: v9-0002-alter-table-set-compression.patch
Type: application/octet-stream
Part: 0
Message: Re: [HACKERS] Custom compression methods

Patch

Format: format-patch
Series: patch v9-0002
Subject: alter table set compression
File+
doc/src/sgml/ref/alter_table.sgml 13 0
src/backend/commands/tablecmds.c 131 0
src/backend/executor/nodeModifyTable.c 1 1
src/backend/parser/gram.y 9 0
src/include/commands/event_trigger.h 1 1
src/include/executor/executor.h 1 0
src/include/nodes/parsenodes.h 2 1
src/test/regress/expected/create_cm.out 15 0
src/test/regress/sql/create_cm.sql 7 0
From 5ef9a179d8973d0fdb182d78e3793d5b3ad8915c Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Tue, 20 Oct 2020 16:34:21 +0530
Subject: [PATCH v9 2/5] alter table set compression

Add support for changing the compression method associated
with a column, forcing a table rewrite.
---
 doc/src/sgml/ref/alter_table.sgml       |  13 +++
 src/backend/commands/tablecmds.c        | 131 ++++++++++++++++++++++++
 src/backend/executor/nodeModifyTable.c  |   2 +-
 src/backend/parser/gram.y               |   9 ++
 src/include/commands/event_trigger.h    |   2 +-
 src/include/executor/executor.h         |   1 +
 src/include/nodes/parsenodes.h          |   3 +-
 src/test/regress/expected/create_cm.out |  15 +++
 src/test/regress/sql/create_cm.sql      |   7 ++
 9 files changed, 180 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index c25ef5abd6..5c88c979af 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -54,6 +54,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+    ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSION <replaceable class="parameter">compression_method</replaceable>
     ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
     ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
     ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -383,6 +384,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <literal>SET COMPRESSION <replaceable class="parameter">compression_method</replaceable></literal>
+    </term>
+    <listitem>
+     <para>
+      This clause adds compression to a column. Compression method can be
+      set from available built-in compression methods.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]</literal></term>
     <listitem>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dc9c6cb1ab..43f79242e8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -530,6 +530,8 @@ static void ATExecGenericOptions(Relation rel, List *options);
 static void ATExecEnableRowSecurity(Relation rel);
 static void ATExecDisableRowSecurity(Relation rel);
 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
+static ObjectAddress ATExecSetCompression(AlteredTableInfo *tab, Relation rel,
+					 const char *column, Node *newValue, LOCKMODE lockmode);
 
 static void index_copy_data(Relation rel, RelFileNode newrnode);
 static const char *storage_name(char c);
@@ -3862,6 +3864,7 @@ AlterTableGetLockLevel(List *cmds)
 				 */
 			case AT_GenericOptions:
 			case AT_AlterColumnGenericOptions:
+			case AT_SetCompression:
 				cmd_lockmode = AccessExclusiveLock;
 				break;
 
@@ -4379,6 +4382,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_DisableRowSecurity:
 		case AT_ForceRowSecurity:
 		case AT_NoForceRowSecurity:
+		case AT_SetCompression:
 			ATSimplePermissions(rel, ATT_TABLE);
 			/* These commands never recurse */
 			/* No command-specific prep needed */
@@ -4782,6 +4786,10 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
 			ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
 			break;
+		case AT_SetCompression:
+			address = ATExecSetCompression(tab, rel, cmd->name, cmd->def,
+										   lockmode);
+			break;
 		default:				/* oops */
 			elog(ERROR, "unrecognized alter table type: %d",
 				 (int) cmd->subtype);
@@ -5406,6 +5414,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				slot_getallattrs(oldslot);
 				ExecClearTuple(newslot);
 
+				if (tab->rewrite & AT_REWRITE_ALTER_COMPRESSION)
+					CompareCompressionMethodAndDecompress(oldslot, newTupDesc);
+
 				/* copy attributes */
 				memcpy(newslot->tts_values, oldslot->tts_values,
 					   sizeof(Datum) * oldslot->tts_nvalid);
@@ -14972,6 +14983,126 @@ ATExecGenericOptions(Relation rel, List *options)
 	heap_freetuple(tuple);
 }
 
+static ObjectAddress
+ATExecSetCompression(AlteredTableInfo *tab,
+					 Relation rel,
+					 const char *column,
+					 Node *newValue,
+					 LOCKMODE lockmode)
+{
+	Relation	attrel;
+	HeapTuple	atttuple;
+	Form_pg_attribute atttableform;
+	AttrNumber	attnum;
+	char	   *compression;
+	Oid			cmoid;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	ObjectAddress address;
+	ListCell   *lc;
+
+	Assert(IsA(newValue, String));
+	compression = strVal(newValue);
+
+	attrel = table_open(AttributeRelationId, RowExclusiveLock);
+
+	atttuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
+	if (!HeapTupleIsValid(atttuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_COLUMN),
+				 errmsg("column \"%s\" of relation \"%s\" does not exist",
+						column, RelationGetRelationName(rel))));
+
+	/* Prevent them from altering a system attribute */
+	atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+	attnum = atttableform->attnum;
+	if (attnum <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot alter system column \"%s\"", column)));
+
+	/* Prevent from altering untoastable attributes with PLAIN storage */
+	if (atttableform->attstorage == 'p' && !TypeIsToastable(atttableform->atttypid))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("column data type %s does not support compression",
+						format_type_be(atttableform->atttypid))));
+
+	/* Initialize buffers for new tuple values */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+	memset(replace, false, sizeof(replace));
+
+	/* Get the attribute compression method. */
+	cmoid = GetAttributeCompressionMethod(atttableform, compression);
+
+	if (atttableform->attcompression != cmoid)
+		tab->rewrite |= AT_REWRITE_ALTER_COMPRESSION;
+
+	atttableform->attcompression = cmoid;
+	CatalogTupleUpdate(attrel, &atttuple->t_self, atttuple);
+
+	InvokeObjectPostAlterHook(RelationRelationId,
+							  RelationGetRelid(rel),
+							  atttableform->attnum);
+
+	ReleaseSysCache(atttuple);
+
+	/* Apply the change to indexes as well */
+	foreach (lc, RelationGetIndexList(rel))
+	{
+		Oid indexoid = lfirst_oid(lc);
+		Relation indrel;
+		AttrNumber indattnum = 0;
+
+		indrel = index_open(indexoid, lockmode);
+
+		for (int i = 0; i < indrel->rd_index->indnatts; i++)
+		{
+			if (indrel->rd_index->indkey.values[i] == attnum)
+			{
+				indattnum = i + 1;
+				break;
+			}
+		}
+
+		if (indattnum == 0)
+		{
+			index_close(indrel, lockmode);
+			continue;
+		}
+
+		atttuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
+
+		if (HeapTupleIsValid(atttuple))
+		{
+			atttableform = (Form_pg_attribute) GETSTRUCT(atttuple);
+			atttableform->attcompression = cmoid;
+
+			CatalogTupleUpdate(attrel, &atttuple->t_self, atttuple);
+
+			InvokeObjectPostAlterHook(RelationRelationId,
+									  RelationGetRelid(rel),
+									  atttableform->attnum);
+
+			heap_freetuple(atttuple);
+		}
+
+		index_close(indrel, lockmode);
+	}
+
+	table_close(attrel, RowExclusiveLock);
+
+	/* make changes visible */
+	CommandCounterIncrement();
+
+	ObjectAddressSubSet(address, RelationRelationId,
+						RelationGetRelid(rel), atttableform->attnum);
+	return address;
+}
+
+
 /*
  * Preparation phase for SET LOGGED/UNLOGGED
  *
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 42f1a1ae9d..643ba631ff 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1927,7 +1927,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
  * of the compressed value is not supported in the target attribute the
  * decompress the value.
  */
-static void
+void
 CompareCompressionMethodAndDecompress(TupleTableSlot *slot,
 									  TupleDesc targetTupDesc)
 {
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 712c564858..289c8c97ee 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2266,6 +2266,15 @@ alter_table_cmd:
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
+			/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (COMPRESSION <cm>) */
+			| ALTER opt_column ColId SET optColumnCompression
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_SetCompression;
+					n->name = $3;
+					n->def = (Node *) makeString($5);;
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
 			| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
 				{
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 407fd6a978..281509e432 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -32,7 +32,7 @@ typedef struct EventTriggerData
 #define AT_REWRITE_ALTER_PERSISTENCE	0x01
 #define AT_REWRITE_DEFAULT_VAL			0x02
 #define AT_REWRITE_COLUMN_REWRITE		0x04
-
+#define AT_REWRITE_ALTER_COMPRESSION	0x08
 /*
  * EventTriggerData is the node type that is passed as fmgr "context" info
  * when a function is called by the event trigger manager.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index b7978cd22e..717e1b1593 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -612,5 +612,6 @@ extern void CheckCmdReplicaIdentity(Relation rel, CmdType cmd);
 
 extern void CheckSubscriptionRelkind(char relkind, const char *nspname,
 									 const char *relname);
+extern void CompareCompressionMethodAndDecompress(TupleTableSlot *slot, TupleDesc targetTupDesc);
 
 #endif							/* EXECUTOR_H  */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 051fef925f..4656bb7e58 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1851,7 +1851,8 @@ typedef enum AlterTableType
 	AT_DetachPartition,			/* DETACH PARTITION */
 	AT_AddIdentity,				/* ADD IDENTITY */
 	AT_SetIdentity,				/* SET identity column options */
-	AT_DropIdentity				/* DROP IDENTITY */
+	AT_DropIdentity,			/* DROP IDENTITY */
+	AT_SetCompression			/* SET COMPRESSION */
 } AlterTableType;
 
 typedef struct ReplicaIdentityStmt
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
index 418c7de06c..3de5fab8c9 100644
--- a/src/test/regress/expected/create_cm.out
+++ b/src/test/regress/expected/create_cm.out
@@ -79,4 +79,19 @@ SELECT length(f1) FROM lz4test;
   24096
 (2 rows)
 
+-- alter compression method with rewrite
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION lz4;
+\d+ cmmove2
+                                        Table "public.cmmove2"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended | lz4         |              | 
+
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION lz4;
+\d+ cmmove2
+                                        Table "public.cmmove2"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended | lz4         |              | 
+
 DROP TABLE cmmove1, cmmove2, cmmove3, lz4test;
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
index 2ac2da8da8..8b75a0640d 100644
--- a/src/test/regress/sql/create_cm.sql
+++ b/src/test/regress/sql/create_cm.sql
@@ -40,4 +40,11 @@ INSERT INTO lz4test VALUES(repeat('1234567890',1004));
 INSERT INTO lz4test VALUES(repeat('1234567890 one two three',1004));
 SELECT length(f1) FROM lz4test;
 
+-- alter compression method with rewrite
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION lz4;
+\d+ cmmove2
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION lz4;
+\d+ cmmove2
+
+
 DROP TABLE cmmove1, cmmove2, cmmove3, lz4test;
-- 
2.23.0