v16-0003-Add-support-for-PRESERVE.patch
application/octet-stream
Filename: v16-0003-Add-support-for-PRESERVE.patch
Type: application/octet-stream
Part: 4
Patch
Format: format-patch
Series: patch v16-0003
Subject: Add support for PRESERVE
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/alter_table.sgml | 8 | 3 |
| src/backend/catalog/objectaddress.c | 1 | 0 |
| src/backend/catalog/pg_depend.c | 7 | 0 |
| src/backend/commands/compressioncmds.c | 231 | 0 |
| src/backend/commands/Makefile | 1 | 0 |
| src/backend/commands/tablecmds.c | 70 | 47 |
| src/backend/executor/nodeModifyTable.c | 7 | 3 |
| src/backend/nodes/copyfuncs.c | 16 | 1 |
| src/backend/nodes/equalfuncs.c | 14 | 1 |
| src/backend/nodes/outfuncs.c | 14 | 1 |
| src/backend/parser/gram.y | 42 | 10 |
| src/backend/parser/parse_utilcmd.c | 1 | 1 |
| src/bin/psql/tab-complete.c | 7 | 0 |
| src/include/commands/defrem.h | 7 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/parsenodes.h | 15 | 1 |
| src/test/regress/expected/compression.out | 36 | 1 |
| src/test/regress/expected/create_index.out | 30 | 26 |
| src/test/regress/sql/compression.sql | 9 | 0 |
From 8a79522520466b409f26e9d18247c276b7b09a78 Mon Sep 17 00:00:00 2001
From: dilipkumar <dilipbalaut@gmail.com>
Date: Mon, 7 Dec 2020 11:55:39 +0530
Subject: [PATCH v16 3/6] Add support for PRESERVE
Now the compression method can be changed without forcing a table
rewrite, by including the old method in the PRESERVE list.
Dilip Kumar based on the patches from Ildus Kurbangaliev.
Design input from Tomas Vondra and Robert Haas
Reviewed by Robert Haas, Tomas Vondra, Alexander Korotkov
Discussions:
https://www.postgresql.org/message-id/20171213151818.75a20259@postgrespro.ru
https://www.postgresql.org/message-id/CA%2BTgmoaKDW1Oi9V%3Djc9hOGyf77NbkNEABuqgHD1Cq%3D%3D1QsOcxg%40mail.gmail.com
https://www.postgresql.org/message-id/CA%2BTgmobSDVgUage9qQ5P_%3DF_9jaMkCgyKxUQGtFQU7oN4kX-AA%40mail.gmail.com
https://www.postgresql.org/message-id/20201005160355.byp74sh3ejsv7wrj%40development
https://www.postgresql.org/message-id/CAFiTN-tzTTT2oqWdRGLv1dvvS5MC1W%2BLE%2B3bqWPJUZj4GnHOJg%40mail.gmail.com
---
doc/src/sgml/ref/alter_table.sgml | 11 +-
src/backend/catalog/objectaddress.c | 1 +
src/backend/catalog/pg_depend.c | 7 +
src/backend/commands/Makefile | 1 +
src/backend/commands/compressioncmds.c | 231 +++++++++++++++++++++
src/backend/commands/tablecmds.c | 117 ++++++-----
src/backend/executor/nodeModifyTable.c | 10 +-
src/backend/nodes/copyfuncs.c | 17 +-
src/backend/nodes/equalfuncs.c | 15 +-
src/backend/nodes/outfuncs.c | 15 +-
src/backend/parser/gram.y | 52 ++++-
src/backend/parser/parse_utilcmd.c | 2 +-
src/bin/psql/tab-complete.c | 7 +
src/include/commands/defrem.h | 7 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/parsenodes.h | 16 +-
src/test/regress/expected/compression.out | 37 +++-
src/test/regress/expected/create_index.out | 56 ++---
src/test/regress/sql/compression.sql | 9 +
19 files changed, 517 insertions(+), 95 deletions(-)
create mode 100644 src/backend/commands/compressioncmds.c
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index bfa5c36fb7..2fb9cfaadc 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -54,7 +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>
+ ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET COMPRESSION <replaceable class="parameter">compression_method</replaceable> [ PRESERVE (<replaceable class="parameter">compression_preserve_list</replaceable>) | PRESERVE ALL ]
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 ]
@@ -386,13 +386,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<varlistentry>
<term>
- <literal>SET COMPRESSION <replaceable class="parameter">compression_method</replaceable></literal>
+ <literal>SET COMPRESSION <replaceable class="parameter">compression_method</replaceable> [ PRESERVE (<replaceable class="parameter">compression_preserve_list</replaceable>) | PRESERVE ALL ]</literal>
</term>
<listitem>
<para>
This clause adds compression to a column. Compression method can be set
from available built-in compression methods. The available built-in
- methods are <literal>pglz</literal> and <literal>lz4</literal>.
+ methods are <literal>pglz</literal> and <literal>lz4</literal>. The
+ PRESERVE list contains list of compression methods used on the column and
+ determines which of them should be kept on the column. Without PRESERVE or
+ if all the previous compression methods are not preserved then the table
+ will be rewritten. If PRESERVE ALL is specified then all the previous
+ methods will be preserved and the table will not be rewritten.
</para>
</listitem>
</varlistentry>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a5eccdffd0..636364995a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -15,6 +15,7 @@
#include "postgres.h"
+#include "access/compressamapi.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 429791694f..3c7aea0df4 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
+#include "catalog/pg_am.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_collation.h"
@@ -125,6 +126,12 @@ recordMultipleDependencies(const ObjectAddress *depender,
if (referenced->objectId == DEFAULT_COLLATION_OID)
ignore_systempin = true;
}
+ /*
+ * Record the dependency on compression access method for handling
+ * preserve.
+ */
+ if (referenced->classId == AccessMethodRelationId)
+ ignore_systempin = true;
}
else
Assert(!version);
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index e8504f0ae4..a7395ad77d 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -21,6 +21,7 @@ OBJS = \
cluster.o \
collationcmds.o \
comment.o \
+ compressioncmds.o \
constraint.o \
conversioncmds.o \
copy.o \
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
new file mode 100644
index 0000000000..91eb59ca17
--- /dev/null
+++ b/src/backend/commands/compressioncmds.c
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * compressioncmds.c
+ * Routines for SQL commands for attribute compression methods
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/compressioncmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/compressamapi.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_depend.h"
+#include "commands/defrem.h"
+#include "nodes/parsenodes.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+
+/*
+ * get list of all supported compression methods for the given attribute.
+ *
+ * If oldcmoids list is passed then it will delete the attribute dependency
+ * on the compression methods passed in the oldcmoids, otherwise it will
+ * return the list of all the compression method on which the attribute has
+ * dependency.
+ */
+static List *
+lookup_attribute_compression(Oid attrelid, AttrNumber attnum, List *oldcmoids)
+{
+ LOCKMODE lock = AccessShareLock;
+ HeapTuple tup;
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key[3];
+ List *cmoids = NIL;
+
+ rel = table_open(DependRelationId, lock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(attrelid));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum((int32) attnum));
+
+ scan = systable_beginscan(rel, DependDependerIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == AccessMethodRelationId)
+ {
+ if (oldcmoids && list_member_oid(oldcmoids, depform->refobjid))
+ CatalogTupleDelete(rel, &tup->t_self);
+ else if (oldcmoids == NULL)
+ cmoids = list_append_unique_oid(cmoids, depform->refobjid);
+ }
+ }
+
+ systable_endscan(scan);
+ table_close(rel, lock);
+
+ return cmoids;
+}
+
+/*
+ * Remove the attribute dependency on the old compression methods given in the
+ * cmoids list.
+ */
+static void
+remove_old_dependencies(Oid attrelid, AttrNumber attnum, List *cmoids)
+{
+ lookup_attribute_compression(attrelid, attnum, cmoids);
+}
+
+/*
+ * Check whether the given compression method oid is supported by
+ * the target attribue.
+ */
+bool
+IsCompressionSupported(Form_pg_attribute att, Oid cmoid)
+{
+ List *cmoids = NIL;
+
+ /* Check whether it is same as the current compression oid */
+ if (cmoid == att->attcompression)
+ return true;
+
+ /* Check the oid in all preserved compresion methods */
+ cmoids = lookup_attribute_compression(att->attrelid, att->attnum, NULL);
+ if (list_member_oid(cmoids, cmoid))
+ return true;
+ else
+ return false;
+}
+
+/*
+ * Get the compression method oid based on the compression method name. When
+ * compression is not specified returns default attribute compression. It is
+ * possible case for CREATE TABLE and ADD COLUMN commands where COMPRESSION
+ * syntax is optional.
+ *
+ * For ALTER command, check all the supported compression methods for the
+ * attribute and if the preserve list is not passed or some of the old
+ * compression methods are not given in the preserved list then delete
+ * dependency from the old compression methods and force the table rewrite.
+ */
+Oid
+GetAttributeCompression(Form_pg_attribute att, ColumnCompression *compression,
+ bool *need_rewrite)
+{
+ Oid cmoid;
+ char typstorage = get_typstorage(att->atttypid);
+ ListCell *cell;
+
+ /*
+ * No compression for the plain/external storage, refer comments atop
+ * attcompression parameter in pg_attribute.h
+ */
+ if (!IsStorageCompressible(typstorage))
+ {
+ if (compression != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("column data type %s does not support compression",
+ format_type_be(att->atttypid))));
+ return InvalidOid;
+ }
+
+ /* fallback to default compression if it's not specified */
+ if (compression == NULL)
+ return DefaultCompressionOid;
+
+ cmoid = get_compression_am_oid(compression->cmname, false);
+
+ /*
+ * Determine if the column needs rewrite or not. Rewrite conditions: SET
+ * COMPRESSION without PRESERVE - SET COMPRESSION with PRESERVE but not
+ * with full list of previous access methods.
+ */
+ if (need_rewrite != NULL)
+ {
+ List *previous_cmoids = NIL;
+
+ *need_rewrite = false;
+
+ /* If we have preserved all then rewrite is not required */
+ if (compression->preserve_all)
+ return cmoid;
+
+ previous_cmoids =
+ lookup_attribute_compression(att->attrelid, att->attnum, NULL);
+
+ if (compression->preserve != NIL)
+ {
+ foreach(cell, compression->preserve)
+ {
+ char *cmname_p = strVal(lfirst(cell));
+ Oid cmoid_p = get_compression_am_oid(cmname_p, false);
+
+ if (!list_member_oid(previous_cmoids, cmoid_p))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" compression access method cannot be preserved", cmname_p),
+ errhint("use \"pg_column_compression\" function for list of compression methods")));
+
+ /*
+ * Remove from previous list, also protect from multiple
+ * mentions of one access method in PRESERVE list
+ */
+ previous_cmoids = list_delete_oid(previous_cmoids, cmoid_p);
+ }
+ }
+
+ /* delete the current cmoid from the list */
+ previous_cmoids = list_delete_oid(previous_cmoids, cmoid);
+
+ /*
+ * If the list of previous Oids is not empty after deletions then
+ * we need to rewrite tuples in the table.
+ */
+ if (list_length(previous_cmoids) != 0)
+ {
+ remove_old_dependencies(att->attrelid, att->attnum,
+ previous_cmoids);
+ *need_rewrite = true;
+ }
+
+ /* Cleanup */
+ list_free(previous_cmoids);
+ }
+
+ return cmoid;
+}
+
+/*
+ * Construct ColumnCompression node from the compression method oid.
+ */
+ColumnCompression *
+MakeColumnCompression(Oid attcompression)
+{
+ ColumnCompression *node;
+
+ if (!OidIsValid(attcompression))
+ return NULL;
+
+ node = makeNode(ColumnCompression);
+ node->cmname = get_am_name(attcompression);
+
+ return node;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8944ebc586..5907d7b2b8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -390,6 +390,7 @@ static bool check_for_column_name_collision(Relation rel, const char *colname,
bool if_not_exists);
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
+static void add_column_compression_dependency(Oid relid, int32 attnum, Oid cmoid);
static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
static void ATPrepSetNotNull(List **wqueue, Relation rel,
@@ -530,7 +531,9 @@ 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);
+ const char *column,
+ ColumnCompression *compression,
+ LOCKMODE lockmode);
static void index_copy_data(Relation rel, RelFileNode newrnode);
static const char *storage_name(char c);
@@ -562,7 +565,6 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
static List *GetParentedForeignKeyRefs(Relation partition);
static void ATDetachCheckNoForeignKeyRefs(Relation partition);
static void ATExecAlterCollationRefreshVersion(Relation rel, List *coll);
-static Oid GetAttributeCompression(Form_pg_attribute att, char *compression);
/* ----------------------------------------------------------------
* DefineRelation
@@ -587,6 +589,7 @@ ObjectAddress
DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ObjectAddress *typaddress, const char *queryString)
{
+ int i;
char relname[NAMEDATALEN];
Oid namespaceId;
Oid relationId;
@@ -865,7 +868,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
relkind == RELKIND_PARTITIONED_TABLE ||
relkind == RELKIND_MATVIEW)
attr->attcompression =
- GetAttributeCompression(attr, colDef->compression);
+ GetAttributeCompression(attr, colDef->compression, NULL);
else
attr->attcompression = InvalidOid;
}
@@ -935,6 +938,20 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
rel = relation_open(relationId, AccessExclusiveLock);
+ /*
+ * Specify relation Oid in attribute compression tuples, and create
+ * dependencies.
+ */
+ for (i = 0; i < (RelationGetDescr(rel))->natts; i++)
+ {
+ Form_pg_attribute attr;
+
+ attr = TupleDescAttr(RelationGetDescr(rel), i);
+ if (OidIsValid(attr->attcompression))
+ add_column_compression_dependency(attr->attrelid, attr->attnum,
+ attr->attcompression);
+ }
+
/*
* Now add any newly specified column default and generation expressions
* to the new relation. These are passed to us in the form of raw
@@ -2413,16 +2430,17 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
/* Copy/check compression parameter */
if (OidIsValid(attribute->attcompression))
{
- char *compression = get_am_name(attribute->attcompression);
+ ColumnCompression *compression =
+ MakeColumnCompression(attribute->attcompression);
if (!def->compression)
def->compression = compression;
- else if (strcmp(def->compression, compression))
+ else if (strcmp(def->compression->cmname, compression->cmname))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" has a compression method conflict",
attributeName),
- errdetail("%s versus %s", def->compression, compression)));
+ errdetail("%s versus %s", def->compression->cmname, compression->cmname)));
}
def->inhcount++;
@@ -2459,7 +2477,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->collOid = attribute->attcollation;
def->constraints = NIL;
def->location = -1;
- def->compression = get_am_name(attribute->attcompression);
+ def->compression = MakeColumnCompression(
+ attribute->attcompression);
inhSchema = lappend(inhSchema, def);
newattmap->attnums[parent_attno - 1] = ++child_attno;
}
@@ -2710,12 +2729,12 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->compression = newdef->compression;
else if (newdef->compression)
{
- if (strcmp(def->compression, newdef->compression))
+ if (strcmp(def->compression->cmname, newdef->compression->cmname))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" has a compression method conflict",
attributeName),
- errdetail("%s versus %s", def->compression, newdef->compression)));
+ errdetail("%s versus %s", def->compression->cmname, newdef->compression->cmname)));
}
/* Mark the column as locally defined */
@@ -4800,7 +4819,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
ATExecAlterCollationRefreshVersion(rel, cmd->object);
break;
case AT_SetCompression:
- address = ATExecSetCompression(tab, rel, cmd->name, cmd->def,
+ address = ATExecSetCompression(tab, rel, cmd->name,
+ (ColumnCompression *) cmd->def,
lockmode);
break;
default: /* oops */
@@ -6294,7 +6314,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
if (rel->rd_rel->relkind == RELKIND_RELATION ||
rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
attribute.attcompression = GetAttributeCompression(&attribute,
- colDef->compression);
+ colDef->compression,
+ NULL);
else
attribute.attcompression = InvalidOid;
@@ -6469,6 +6490,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
+ add_column_compression_dependency(myrelid, newattnum, attribute.attcompression);
/*
* Propagate to children as appropriate. Unlike most other ALTER
@@ -6616,6 +6638,28 @@ add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
}
}
+/*
+ * Install a dependency for compression on its column.
+ *
+ * This is used to determine connection between column and builtin
+ * compression in ALTER SET COMPRESSION command.
+ *
+ * If dependency is already there the whole thing is skipped.
+ */
+static void
+add_column_compression_dependency(Oid relid, int32 attnum, Oid cmoid)
+{
+ ObjectAddress acref,
+ attref;
+
+ Assert(relid > 0 && attnum > 0);
+
+ ObjectAddressSet(acref, AccessMethodRelationId, cmoid);
+ ObjectAddressSubSet(attref, RelationRelationId, relid, attnum);
+
+ recordMultipleDependencies(&attref, &acref, 1, DEPENDENCY_NORMAL, true);
+}
+
/*
* ALTER TABLE ALTER COLUMN DROP NOT NULL
*/
@@ -11751,7 +11795,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
foundDep->refobjid == attTup->attcollation) &&
!(foundDep->refclassid == RelationRelationId &&
foundDep->refobjid == RelationGetRelid(rel) &&
- foundDep->refobjsubid != 0)
+ foundDep->refobjsubid != 0) &&
+ foundDep->refclassid != AccessMethodRelationId
)
elog(ERROR, "found unexpected dependency for column: %s",
getObjectDescription(&foundObject, false));
@@ -11866,6 +11911,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
+ /* Create dependency for new attribute compression */
+ if (OidIsValid(attTup->attcompression))
+ add_column_compression_dependency(RelationGetRelid(rel), attnum,
+ attTup->attcompression);
+
/*
* Drop any pg_statistic entry for the column, since it's now wrong type
*/
@@ -15044,24 +15094,21 @@ static ObjectAddress
ATExecSetCompression(AlteredTableInfo *tab,
Relation rel,
const char *column,
- Node *newValue,
+ ColumnCompression *compression,
LOCKMODE lockmode)
{
Relation attrel;
HeapTuple tuple;
Form_pg_attribute atttableform;
AttrNumber attnum;
- char *compression;
char typstorage;
Oid cmoid;
+ bool need_rewrite;
Datum values[Natts_pg_attribute];
bool nulls[Natts_pg_attribute];
bool replace[Natts_pg_attribute];
ObjectAddress address;
- Assert(IsA(newValue, String));
- compression = strVal(newValue);
-
attrel = table_open(AttributeRelationId, RowExclusiveLock);
tuple = SearchSysCacheAttName(RelationGetRelid(rel), column);
@@ -15094,11 +15141,16 @@ ATExecSetCompression(AlteredTableInfo *tab,
memset(replace, false, sizeof(replace));
/* Get the attribute compression method. */
- cmoid = GetAttributeCompression(atttableform, compression);
+ cmoid = GetAttributeCompression(atttableform, compression, &need_rewrite);
if (atttableform->attcompression != cmoid)
+ add_column_compression_dependency(atttableform->attrelid,
+ atttableform->attnum, cmoid);
+ if (need_rewrite)
tab->rewrite |= AT_REWRITE_ALTER_COMPRESSION;
+ atttableform->attcompression = cmoid;
+
atttableform->attcompression = cmoid;
CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
@@ -17823,32 +17875,3 @@ ATExecAlterCollationRefreshVersion(Relation rel, List *coll)
index_update_collation_versions(rel->rd_id, get_collation_oid(coll, false));
CacheInvalidateRelcache(rel);
}
-
-/*
- * Get compression method Oid for the attribute.
- */
-static Oid
-GetAttributeCompression(Form_pg_attribute att, char *compression)
-{
- char typstorage = get_typstorage(att->atttypid);
-
- /*
- * No compression for the plain/external storage, refer comments atop
- * attcompression parameter in pg_attribute.h
- */
- if (!IsStorageCompressible(typstorage))
- {
- if (compression != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("column data type %s does not support compression",
- format_type_be(att->atttypid))));
- return InvalidOid;
- }
-
- /* fallback to default compression if it's not specified */
- if (compression == NULL)
- return DefaultCompressionOid;
-
- return get_compression_am_oid(compression, false);
-}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 64a396a218..53a4e3131c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -45,6 +45,7 @@
#include "access/toast_internals.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "commands/defrem.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
@@ -1933,6 +1934,7 @@ CompareCompressionMethodAndDecompress(TupleTableSlot *slot,
int natts = slot->tts_tupleDescriptor->natts;
bool isnull = false;
bool decompressed_any = false;
+ Oid cmoid = InvalidOid;
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
if (natts == 0)
@@ -1963,10 +1965,12 @@ CompareCompressionMethodAndDecompress(TupleTableSlot *slot,
/*
* Get the compression method stored in the toast header and
- * compare with the compression method of the target.
+ * compare with the compression method of the target attribute. If
+ * the target compression method is not same then we need to
+ * decompress it.
*/
- if (targetTupDesc->attrs[i].attcompression !=
- CompressionIdToOid(TOAST_COMPRESS_METHOD(new_value)))
+ cmoid = CompressionIdToOid(TOAST_COMPRESS_METHOD(new_value));
+ if (!IsCompressionSupported(&targetTupDesc->attrs[i], cmoid))
{
new_value = detoast_attr(new_value);
slot->tts_values[attnum - 1] = PointerGetDatum(new_value);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7d63cee5a5..321da2accd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2932,7 +2932,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_STRING_FIELD(colname);
COPY_NODE_FIELD(typeName);
- COPY_STRING_FIELD(compression);
+ COPY_NODE_FIELD(compression);
COPY_SCALAR_FIELD(inhcount);
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
@@ -2952,6 +2952,18 @@ _copyColumnDef(const ColumnDef *from)
return newnode;
}
+static ColumnCompression *
+_copyColumnCompression(const ColumnCompression *from)
+{
+ ColumnCompression *newnode = makeNode(ColumnCompression);
+
+ COPY_STRING_FIELD(cmname);
+ COPY_SCALAR_FIELD(preserve_all);
+ COPY_NODE_FIELD(preserve);
+
+ return newnode;
+}
+
static Constraint *
_copyConstraint(const Constraint *from)
{
@@ -5623,6 +5635,9 @@ copyObjectImpl(const void *from)
case T_ColumnDef:
retval = _copyColumnDef(from);
break;
+ case T_ColumnCompression:
+ retval = _copyColumnCompression(from);
+ break;
case T_Constraint:
retval = _copyConstraint(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index de26dce6d6..ade226efdc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2586,7 +2586,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
{
COMPARE_STRING_FIELD(colname);
COMPARE_NODE_FIELD(typeName);
- COMPARE_STRING_FIELD(compression);
+ COMPARE_NODE_FIELD(compression);
COMPARE_SCALAR_FIELD(inhcount);
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
@@ -2606,6 +2606,16 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
return true;
}
+static bool
+_equalColumnCompression(const ColumnCompression *a, const ColumnCompression *b)
+{
+ COMPARE_STRING_FIELD(cmname);
+ COMPARE_SCALAR_FIELD(preserve_all);
+ COMPARE_NODE_FIELD(preserve);
+
+ return true;
+}
+
static bool
_equalConstraint(const Constraint *a, const Constraint *b)
{
@@ -3678,6 +3688,9 @@ equal(const void *a, const void *b)
case T_ColumnDef:
retval = _equalColumnDef(a, b);
break;
+ case T_ColumnCompression:
+ retval = _equalColumnCompression(a, b);
+ break;
case T_Constraint:
retval = _equalConstraint(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 55b35e6f13..2e60bb750e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2853,7 +2853,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_STRING_FIELD(colname);
WRITE_NODE_FIELD(typeName);
- WRITE_STRING_FIELD(compression);
+ WRITE_NODE_FIELD(compression);
WRITE_INT_FIELD(inhcount);
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
@@ -2871,6 +2871,16 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outColumnCompression(StringInfo str, const ColumnCompression *node)
+{
+ WRITE_NODE_TYPE("COLUMNCOMPRESSION");
+
+ WRITE_STRING_FIELD(cmname);
+ WRITE_BOOL_FIELD(preserve_all);
+ WRITE_NODE_FIELD(preserve);
+}
+
static void
_outTypeName(StringInfo str, const TypeName *node)
{
@@ -4215,6 +4225,9 @@ outNode(StringInfo str, const void *obj)
case T_ColumnDef:
_outColumnDef(str, obj);
break;
+ case T_ColumnCompression:
+ _outColumnCompression(str, obj);
+ break;
case T_TypeName:
_outTypeName(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9683062216..22bd0e87a3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -593,7 +593,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound
%type <defelt> hash_partbound_elem
-%type <str> optColumnCompression
+%type <node> optColumnCompression alterColumnCompression
+%type <str> compressionClause
+%type <list> optCompressionPreserve
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -2258,12 +2260,12 @@ alter_table_cmd:
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET (COMPRESSION <cm>) */
- | ALTER opt_column ColId SET optColumnCompression
+ | ALTER opt_column ColId SET alterColumnCompression
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_SetCompression;
n->name = $3;
- n->def = (Node *) makeString($5);;
+ n->def = $5;
$$ = (Node *)n;
}
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
@@ -3386,7 +3388,7 @@ columnDef: ColId Typename optColumnCompression create_generic_options ColQualLis
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typeName = $2;
- n->compression = $3;
+ n->compression = (ColumnCompression *) $3;
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
@@ -3441,13 +3443,43 @@ columnOptions: ColId ColQualList
}
;
+optCompressionPreserve:
+ PRESERVE '(' name_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+compressionClause:
+ COMPRESSION name { $$ = pstrdup($2); }
+ ;
+
optColumnCompression:
- COMPRESSION name
- {
- $$ = $2;
- }
- | /*EMPTY*/ { $$ = NULL; }
- ;
+ compressionClause
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->cmname = $1;
+ n->preserve = NIL;
+ $$ = (Node *) n;
+ }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+alterColumnCompression:
+ compressionClause optCompressionPreserve
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->cmname = $1;
+ n->preserve = (List *) $2;
+ $$ = (Node *) n;
+ }
+ | compressionClause PRESERVE ALL
+ {
+ ColumnCompression *n = makeNode(ColumnCompression);
+ n->cmname = $1;
+ n->preserve_all = true;
+ n->preserve = NIL;
+ $$ = (Node *) n;
+ }
+ ;
ColQualList:
ColQualList ColConstraint { $$ = lappend($1, $2); }
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e5184933b7..4bbbf6370a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1086,7 +1086,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
/* Likewise, copy compression if requested */
if (table_like_clause->options & CREATE_TABLE_LIKE_COMPRESSION
&& OidIsValid(attribute->attcompression))
- def->compression = get_am_name(attribute->attcompression);
+ def->compression = MakeColumnCompression(attribute->attcompression);
else
def->compression = NULL;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 1da16ebc0a..8178a5124b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2098,6 +2098,13 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
COMPLETE_WITH("n_distinct", "n_distinct_inherited");
+ /* ALTER TABLE ALTER [COLUMN] <foo> SET COMPRESSION */
+ else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION", MatchAny) ||
+ Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION", MatchAny))
+ COMPLETE_WITH("PRESERVE");
+ else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION", MatchAny, "PRESERVE") ||
+ Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION", MatchAny, "PRESERVE"))
+ COMPLETE_WITH("( ", "ALL");
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 2d76a85336..d1880dfc5e 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -147,6 +147,13 @@ extern Oid get_compression_am_oid(const char *amname, bool missing_ok);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
+/* commands/compressioncmds.c */
+extern Oid GetAttributeCompression(Form_pg_attribute att,
+ ColumnCompression *compression,
+ bool *need_rewrite);
+extern ColumnCompression *MakeColumnCompression(Oid atttcompression);
+extern bool IsCompressionSupported(Form_pg_attribute att, Oid cmoid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c8d1f20b3b..885ff5ec5c 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -478,6 +478,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
+ T_ColumnCompression,
T_VacuumRelation,
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index af79aa6534..c8135debbb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -623,6 +623,20 @@ typedef struct RangeTableSample
int location; /* method name location, or -1 if unknown */
} RangeTableSample;
+/*
+ * ColumnCompression - compression parameters for some attribute
+ *
+ * This represents compression information defined using clause:
+ * .. COMPRESSION <compression method> PRESERVE <compression methods>
+ */
+typedef struct ColumnCompression
+{
+ NodeTag type;
+ char *cmname;
+ bool preserve_all;
+ List *preserve;
+} ColumnCompression;
+
/*
* ColumnDef - column definition (used in various creates)
*
@@ -646,7 +660,7 @@ typedef struct ColumnDef
NodeTag type;
char *colname; /* name of column */
TypeName *typeName; /* type of column */
- char *compression; /* compression method for column */
+ ColumnCompression *compression; /* column compression */
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out
index 06ac4161ae..9d87ec4ad7 100644
--- a/src/test/regress/expected/compression.out
+++ b/src/test/regress/expected/compression.out
@@ -201,12 +201,47 @@ SELECT pg_column_compression(f1) FROM cmpart;
lz4
(2 rows)
+-- preserve old compression method
+ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION pglz PRESERVE (lz4);
+INSERT INTO cmdata VALUES (repeat('1234567890',1004));
+\d+ cmdata
+ Table "public.cmdata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | text | | | | extended | pglz | |
+Indexes:
+ "idx" btree (f1)
+
+SELECT pg_column_compression(f1) FROM cmdata;
+ pg_column_compression
+-----------------------
+ lz4
+ pglz
+(2 rows)
+
+\d+ cmdata
+ Table "public.cmdata"
+ Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1 | text | | | | extended | pglz | |
+Indexes:
+ "idx" btree (f1)
+
+ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION lz4 PRESERVE ALL;
+SELECT pg_column_compression(f1) FROM cmdata;
+ pg_column_compression
+-----------------------
+ lz4
+ pglz
+(2 rows)
+
-- check data is ok
SELECT length(f1) FROM cmdata;
length
--------
10000
-(1 row)
+ 10040
+(2 rows)
SELECT length(f1) FROM cmdata1;
length
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index fc6afab58a..d8300bdcdd 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2066,19 +2066,21 @@ WHERE classid = 'pg_class'::regclass AND
'concur_reindex_ind4'::regclass,
'concur_reindex_matview'::regclass)
ORDER BY 1, 2;
- obj | objref | deptype
-------------------------------------------+------------------------------------------------------------+---------
- index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
- index concur_reindex_ind2 | collation "default" | n
- index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
- index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
- index concur_reindex_ind3 | table concur_reindex_tab | a
- index concur_reindex_ind4 | collation "default" | n
- index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
- index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
- materialized view concur_reindex_matview | schema public | n
- table concur_reindex_tab | schema public | n
-(10 rows)
+ obj | objref | deptype
+-------------------------------------------------------+------------------------------------------------------------+---------
+ column c2 of materialized view concur_reindex_matview | access method pglz | n
+ column c2 of table concur_reindex_tab | access method pglz | n
+ index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
+ index concur_reindex_ind2 | collation "default" | n
+ index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
+ index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
+ index concur_reindex_ind3 | table concur_reindex_tab | a
+ index concur_reindex_ind4 | collation "default" | n
+ index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
+ index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
+ materialized view concur_reindex_matview | schema public | n
+ table concur_reindex_tab | schema public | n
+(12 rows)
REINDEX INDEX CONCURRENTLY concur_reindex_ind1;
REINDEX TABLE CONCURRENTLY concur_reindex_tab;
@@ -2095,19 +2097,21 @@ WHERE classid = 'pg_class'::regclass AND
'concur_reindex_ind4'::regclass,
'concur_reindex_matview'::regclass)
ORDER BY 1, 2;
- obj | objref | deptype
-------------------------------------------+------------------------------------------------------------+---------
- index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
- index concur_reindex_ind2 | collation "default" | n
- index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
- index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
- index concur_reindex_ind3 | table concur_reindex_tab | a
- index concur_reindex_ind4 | collation "default" | n
- index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
- index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
- materialized view concur_reindex_matview | schema public | n
- table concur_reindex_tab | schema public | n
-(10 rows)
+ obj | objref | deptype
+-------------------------------------------------------+------------------------------------------------------------+---------
+ column c2 of materialized view concur_reindex_matview | access method pglz | n
+ column c2 of table concur_reindex_tab | access method pglz | n
+ index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
+ index concur_reindex_ind2 | collation "default" | n
+ index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
+ index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
+ index concur_reindex_ind3 | table concur_reindex_tab | a
+ index concur_reindex_ind4 | collation "default" | n
+ index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
+ index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
+ materialized view concur_reindex_matview | schema public | n
+ table concur_reindex_tab | schema public | n
+(12 rows)
-- Check that comments are preserved
CREATE TABLE testcomment (i int);
diff --git a/src/test/regress/sql/compression.sql b/src/test/regress/sql/compression.sql
index aa8e7492df..9a9c3d95e7 100644
--- a/src/test/regress/sql/compression.sql
+++ b/src/test/regress/sql/compression.sql
@@ -86,6 +86,15 @@ ALTER TABLE cmpart1 ALTER COLUMN f1 SET COMPRESSION pglz;
ALTER TABLE cmpart2 ALTER COLUMN f1 SET COMPRESSION lz4;
SELECT pg_column_compression(f1) FROM cmpart;
+-- preserve old compression method
+ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION pglz PRESERVE (lz4);
+INSERT INTO cmdata VALUES (repeat('1234567890',1004));
+\d+ cmdata
+SELECT pg_column_compression(f1) FROM cmdata;
+\d+ cmdata
+ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION lz4 PRESERVE ALL;
+SELECT pg_column_compression(f1) FROM cmdata;
+
-- check data is ok
SELECT length(f1) FROM cmdata;
SELECT length(f1) FROM cmdata1;
--
2.23.0