From 2c7910f57adc36928196a4523fa203385b8d1f8c Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Wed, 10 Feb 2021 11:49:23 +0530 Subject: [PATCH v23 3/6] Add support for PRESERVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the compression method can be changed without forcing a table rewrite, by including the old method in the PRESERVE list.  For supporting this the column will maintain the dependency with all the supported compression methods.  So whenever the compression method is altered the dependency is added with the new compression method and the dependency is removed for all the old compression methods which are not given in the preserve list.  If PRESERVE ALL is given then all the dependency is maintained. 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 and Justin Pryzby --- doc/src/sgml/ref/alter_table.sgml | 10 +- src/backend/catalog/pg_depend.c | 7 + src/backend/commands/Makefile | 1 + src/backend/commands/compressioncmds.c | 300 ++++++++++++++++++++++++++++ src/backend/commands/tablecmds.c | 126 ++++++------ src/backend/executor/nodeModifyTable.c | 12 +- 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/pg_dump/pg_dump.c | 101 ++++++++++ src/bin/pg_dump/pg_dump.h | 15 +- src/bin/psql/tab-complete.c | 7 + src/include/commands/defrem.h | 7 + src/include/commands/tablecmds.h | 2 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 16 +- src/test/regress/expected/compression.out | 37 +++- src/test/regress/expected/compression_1.out | 39 +++- src/test/regress/expected/create_index.out | 56 +++--- src/test/regress/sql/compression.sql | 9 + 22 files changed, 739 insertions(+), 108 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 0bd0c1a..c9f443a 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 ] 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 + ALTER [ COLUMN ] column_name SET COMPRESSION compression_method [ PRESERVE (compression_preserve_list) | PRESERVE ALL ] ADD table_constraint [ NOT VALID ] ADD table_constraint_using_index ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] @@ -387,7 +387,7 @@ WITH ( MODULUS numeric_literal, REM - SET COMPRESSION compression_method + SET COMPRESSION compression_method [ PRESERVE (compression_preserve_list) | PRESERVE ALL ] @@ -395,6 +395,12 @@ WITH ( MODULUS numeric_literal, REM methods are pglz and lz4. lz4 is available only if --with-lz4 was used when building PostgreSQL. + The PRESERVE list contains a list of compression + methods used in the column and determines which of them may be kept. + Without PRESERVE or if any of the pre-existing + compression methods are not preserved, the table will be rewritten. If + PRESERVE ALL is specified, then all of the existing + methods will be preserved and the table will not be rewritten. diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 63da243..dd37648 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 e8504f0..a7395ad 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 0000000..4ff75d0 --- /dev/null +++ b/src/backend/commands/compressioncmds.c @@ -0,0 +1,300 @@ +/*------------------------------------------------------------------------- + * + * 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 "commands/tablecmds.h" +#include "miscadmin.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. + * + * We maintain dependency of the attribute on the pg_am row for the current + * compression AM and all the preserved compression AM. So scan pg_depend and + * find the column dependency on the pg_am. Collect the list of access method + * oids on which this attribute has a 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) + 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 + * + * Scan the pg_depend and search the attribute dependency on the pg_am. Remove + * dependency on previous am which is not preserved. The list of non-preserved + * AMs is given in cmoids. + */ +static void +remove_old_dependencies(Oid attrelid, AttrNumber attnum, List *cmoids) +{ + LOCKMODE lock = AccessShareLock; + HeapTuple tup; + Relation rel; + SysScanDesc scan; + ScanKeyData key[3]; + + 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 (list_member_oid(cmoids, depform->refobjid)) + { + Assert(depform->refclassid == AccessMethodRelationId); + CatalogTupleDelete(rel, &tup->t_self); + } + } + + systable_endscan(scan); + table_close(rel, lock); +} + +/* + * Check whether the given compression method oid is supported by + * the target attribute. + */ +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; +} + +/* + * In binary upgrade mode add the dependencies for all the preserved compression + * method. + */ +static void +BinaryUpgradeAddPreserve(Form_pg_attribute att, List *preserve) +{ + ListCell *cell; + + foreach(cell, preserve) + { + char *cmname_p = strVal(lfirst(cell)); + Oid cmoid_p = get_compression_am_oid(cmname_p, false); + + add_column_compression_dependency(att->attrelid, att->attnum, cmoid_p); + } +} + +/* + * 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) + return InvalidOid; + + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("column data type %s does not support compression", + format_type_be(att->atttypid)))); + } + + /* fallback to default compression if it's not specified */ + if (compression == NULL) + return DefaultCompressionOid; + + cmoid = get_compression_am_oid(compression->cmname, false); + +#ifndef HAVE_LIBLZ4 + if (cmoid == LZ4_COMPRESSION_AM_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("not built with lz4 support"))); +#endif + + /* + * 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; + + /* + * In binary upgrade mode, just create a dependency on all preserved + * methods. + */ + if (IsBinaryUpgrade) + { + BinaryUpgradeAddPreserve(att, compression->preserve); + return cmoid; + } + + /* 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); + + 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))); + + /* + * Remove from previous list, also protect from duplicate + * entries in the 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. Also remove the dependency + * on the old compression methods which are no longer preserved. + */ + 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 cb2ad13..fcfe4fd 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -530,7 +530,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 +564,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 +588,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 +867,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; } @@ -936,6 +938,20 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, rel = relation_open(relationId, AccessExclusiveLock); /* + * Add the dependency on the respective compression AM for the relation + * attributes. + */ + 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 * parsetrees; we need to transform them to executable expression trees @@ -2415,16 +2431,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) != 0) + 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++; @@ -2461,7 +2478,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; } @@ -2712,12 +2730,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 */ @@ -4908,7 +4926,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 */ @@ -6414,7 +6433,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; @@ -6589,6 +6609,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 @@ -6737,6 +6758,28 @@ add_column_collation_dependency(Oid relid, int32 attnum, Oid collid) } /* + * Install a dependency for compression on its column. + * + * This is used for identifying all the supported compression methods + * (current and preserved) for a attribute. + * + * If dependency is already there the whole thing is skipped. + */ +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 */ @@ -11867,7 +11910,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)); @@ -11982,6 +12026,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 */ @@ -15086,24 +15135,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); @@ -15136,12 +15182,17 @@ 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); InvokeObjectPostAlterHook(RelationRelationId, @@ -17865,42 +17916,3 @@ ATExecAlterCollationRefreshVersion(Relation rel, List *coll) index_update_collation_versions(rel->rd_id, get_collation_oid(coll, false)); CacheInvalidateRelcache(rel); } - -/* - * resolve column compression specification to an OID. - */ -static Oid -GetAttributeCompression(Form_pg_attribute att, char *compression) -{ - char typstorage = get_typstorage(att->atttypid); - Oid amoid; - - /* - * No compression for the plain/external storage, refer comments atop - * attcompression parameter in pg_attribute.h - */ - if (!IsStorageCompressible(typstorage)) - { - if (compression == NULL) - return InvalidOid; - - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("column data type %s does not support compression", - format_type_be(att->atttypid)))); - } - - /* fallback to default compression if it's not specified */ - if (compression == NULL) - return DefaultCompressionOid; - - amoid = get_compression_am_oid(compression, false); - -#ifndef HAVE_LIBLZ4 - if (amoid == LZ4_COMPRESSION_AM_OID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("not built with lz4 support"))); -#endif - return amoid; -} diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 93f63da..ad5ed09 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -44,6 +44,7 @@ #include "access/tableam.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" @@ -2068,8 +2069,8 @@ CompareCompressionMethodAndDecompress(TupleTableSlot *slot, /* * Loop over all the attributes in the tuple and check if any attribute is - * compressed and its compression method is not same as the target - * atrribute's compression method then decompress it. + * compressed and its compression method is not is not supported by the + * target attribute then we need to decompress */ for (i = 0; i < natts; i++) { @@ -2094,12 +2095,13 @@ CompareCompressionMethodAndDecompress(TupleTableSlot *slot, DatumGetPointer(slot->tts_values[attnum - 1]); /* - * Get the compression method Oid stored in the toast header and - * compare it with the compression method of the target. + * Get the compression method stored in the toast header and if the + * compression method is not supported by the target attribute then + * we need to decompress it. */ cmoid = toast_get_compression_oid(new_value); if (OidIsValid(cmoid) && - targetTupDesc->attrs[i].attcompression != cmoid) + !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 1338e04..6a11f8e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2966,7 +2966,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); @@ -2986,6 +2986,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) { @@ -5675,6 +5687,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 f359200..26a9b85 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2599,7 +2599,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); @@ -2620,6 +2620,16 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b) } 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) { COMPARE_SCALAR_FIELD(contype); @@ -3724,6 +3734,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 0605ef3..b584a58 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2863,7 +2863,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); @@ -2882,6 +2882,16 @@ _outColumnDef(StringInfo str, const ColumnDef *node) } 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) { WRITE_NODE_TYPE("TYPENAME"); @@ -4258,6 +4268,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 30acfe6..9eb2b04 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -596,7 +596,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type hash_partbound %type hash_partbound_elem -%type optColumnCompression +%type optColumnCompression alterColumnCompression +%type compressionClause +%type optCompressionPreserve /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -2309,12 +2311,12 @@ alter_table_cmd: $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] SET (COMPRESSION ) */ - | 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 DROP [COLUMN] IF EXISTS [RESTRICT|CASCADE] */ @@ -3437,7 +3439,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; @@ -3492,13 +3494,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 cf4413d..45f4724 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) != 0 && OidIsValid(attribute->attcompression)) - def->compression = get_am_name(attribute->attcompression); + def->compression = MakeColumnCompression(attribute->attcompression); else def->compression = NULL; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index cccf3c0..075e1cf 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -9037,6 +9037,80 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) } PQclear(res); } + + /* + * Get compression info + */ + if (fout->remoteVersion >= 140000 && dopt->binary_upgrade) + { + int i_amname; + int i_amoid; + int i_curattnum; + int start; + + pg_log_info("finding compression info for table \"%s.%s\"", + tbinfo->dobj.namespace->dobj.name, + tbinfo->dobj.name); + + tbinfo->attcompression = pg_malloc0(tbinfo->numatts * sizeof(AttrCompressionInfo *)); + + resetPQExpBuffer(q); + appendPQExpBuffer(q, + " SELECT attrelid::pg_catalog.regclass AS relname, attname," + " amname, am.oid as amoid, d.objsubid AS curattnum" + " FROM pg_depend d" + " JOIN pg_attribute a ON" + " (classid = 'pg_class'::pg_catalog.regclass::pg_catalog.oid AND a.attrelid = d.objid" + " AND a.attnum = d.objsubid AND d.deptype = 'n'" + " AND d.refclassid = 'pg_am'::pg_catalog.regclass::pg_catalog.oid)" + " JOIN pg_am am ON" + " (d.deptype = 'n' AND d.refobjid = am.oid)" + " WHERE (deptype = 'n' AND d.objid = %d AND a.attcompression != am.oid);", + tbinfo->dobj.catId.oid); + + res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); + ntups = PQntuples(res); + + if (ntups > 0) + { + int j; + int k; + + i_amname = PQfnumber(res, "amname"); + i_amoid = PQfnumber(res, "amoid"); + i_curattnum = PQfnumber(res, "curattnum"); + + start = 0; + + for (j = 0; j < ntups; j++) + { + int attnum = atoi(PQgetvalue(res, j, i_curattnum)); + + if ((j == ntups - 1) || atoi(PQgetvalue(res, j + 1, i_curattnum)) != attnum) + { + AttrCompressionInfo *cminfo = pg_malloc(sizeof(AttrCompressionInfo)); + + cminfo->nitems = j - start + 1; + cminfo->items = pg_malloc(sizeof(AttrCompressionItem *) * cminfo->nitems); + + for (k = start; k < start + cminfo->nitems; k++) + { + AttrCompressionItem *cmitem = pg_malloc0(sizeof(AttrCompressionItem)); + + cmitem->amname = pg_strdup(PQgetvalue(res, k, i_amname)); + cmitem->amoid = atooid(PQgetvalue(res, k, i_amoid)); + + cminfo->items[k - start] = cmitem; + } + + tbinfo->attcompression[attnum - 1] = cminfo; + start = j + 1; /* start from next */ + } + } + } + + PQclear(res); + } } destroyPQExpBuffer(q); @@ -16343,6 +16417,33 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) qualrelname, fmtId(tbinfo->attnames[j]), tbinfo->attfdwoptions[j]); + + /* + * Dump per-column compression options + */ + if (tbinfo->attcompression && tbinfo->attcompression[j]) + { + AttrCompressionInfo *cminfo = tbinfo->attcompression[j]; + + appendPQExpBuffer(q, "ALTER TABLE %s ALTER COLUMN %s\nSET COMPRESSION %s", + qualrelname, fmtId(tbinfo->attnames[j]), tbinfo->attcmnames[j]); + + if (cminfo->nitems > 0) + { + appendPQExpBuffer(q, "\nPRESERVE ("); + for (int i = 0; i < cminfo->nitems; i++) + { + AttrCompressionItem *item = cminfo->items[i]; + + if (i == 0) + appendPQExpBuffer(q, "%s", item->amname); + else + appendPQExpBuffer(q, ", %s", item->amname); + } + appendPQExpBuffer(q, ")"); + } + appendPQExpBuffer(q, ";\n"); + } } if (ftoptions) diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 66a2374..f02d9ce 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -327,7 +327,8 @@ typedef struct _tableInfo bool needs_override; /* has GENERATED ALWAYS AS IDENTITY */ char *amname; /* relation access method */ char **attcmnames; /* per-attribute current compression method */ - + struct _attrCompressionInfo **attcompression; /* per-attribute all + compression data */ /* * Stuff computed only for dumpable tables. */ @@ -356,6 +357,18 @@ typedef struct _attrDefInfo bool separate; /* true if must dump as separate item */ } AttrDefInfo; +typedef struct _attrCompressionItem +{ + Oid amoid; /* attribute compression oid */ + char *amname; /* compression access method name */ +} AttrCompressionItem; + +typedef struct _attrCompressionInfo +{ + int nitems; + AttrCompressionItem **items; +} AttrCompressionInfo; + typedef struct _tableDataInfo { DumpableObject dobj; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index a8d3b33..e34fbdc 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2103,6 +2103,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] 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] 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 e5aea8a..bd53f9b 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -143,6 +143,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/commands/tablecmds.h b/src/include/commands/tablecmds.h index b3d30ac..e6c98e6 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -97,5 +97,7 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg); extern bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint); +extern void add_column_compression_dependency(Oid relid, int32 attnum, + Oid cmoid); #endif /* TABLECMDS_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 20d6f96..24deaad 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -481,6 +481,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 f9a87de..ce0913e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -624,6 +624,20 @@ typedef struct RangeTableSample } RangeTableSample; /* + * ColumnCompression - compression parameters for some attribute + * + * This represents compression information defined using clause: + * .. COMPRESSION PRESERVE + */ +typedef struct ColumnCompression +{ + NodeTag type; + char *cmname; + bool preserve_all; + List *preserve; +} ColumnCompression; + +/* * ColumnDef - column definition (used in various creates) * * If the column has a default value, we may have the value expression @@ -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 4ac4c51..174d447 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -225,12 +225,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/compression_1.out b/src/test/regress/expected/compression_1.out index 2b37f44..6fb6df5 100644 --- a/src/test/regress/expected/compression_1.out +++ b/src/test/regress/expected/compression_1.out @@ -204,12 +204,49 @@ SELECT pg_column_compression(f1) FROM cmpart; ERROR: relation "cmpart" does not exist LINE 1: SELECT pg_column_compression(f1) FROM cmpart; ^ +-- preserve old compression method +ALTER TABLE cmdata ALTER COLUMN f1 SET COMPRESSION pglz PRESERVE (lz4); +ERROR: "lz4" compression access method cannot be preserved +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 +----------------------- + pglz + 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; +ERROR: not built with lz4 support +SELECT pg_column_compression(f1) FROM cmdata; + pg_column_compression +----------------------- + pglz + pglz +(2 rows) + -- check data is ok SELECT length(f1) FROM cmdata; length -------- 10000 -(1 row) + 10040 +(2 rows) SELECT length(f1) FROM cmdata1; ERROR: relation "cmdata1" does not exist diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index ce734f7..b61c627 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 d268d46..fbbdbd7 100644 --- a/src/test/regress/sql/compression.sql +++ b/src/test/regress/sql/compression.sql @@ -94,6 +94,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; -- 1.8.3.1