From 5ef9a179d8973d0fdb182d78e3793d5b3ad8915c Mon Sep 17 00:00:00 2001 From: Dilip Kumar 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 ] name ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] ) ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } + ALTER [ COLUMN ] column_name SET COMPRESSION compression_method ADD table_constraint [ NOT VALID ] ADD table_constraint_using_index ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] @@ -383,6 +384,18 @@ WITH ( MODULUS numeric_literal, REM + + + SET COMPRESSION compression_method + + + + This clause adds compression to a column. Compression method can be + set from available built-in compression methods. + + + + ADD table_constraint [ NOT VALID ] 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 ALTER [COLUMN] SET (COMPRESSION ) */ + | 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 DROP [COLUMN] IF EXISTS [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