From 0413a556990ba628a3de8a0b58be020fd9a14ed0 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 16 Feb 2024 11:51:35 +0100 Subject: [PATCH] Improve compression and storage support with inheritance A child table can specify a compression or storage method different from its parents. This was previously an error. (But this was inconsistently enforced because for example the settings could be changed later using ALTER TABLE.) This now also allows an explicit override if multiple parents have different compression or storage settings, which was previously an error that could not be overridden. The compression and storage properties remains unchanged in a child inheriting from parent(s) after its creation, i.e., when using ALTER TABLE ... INHERIT. (This is not changed.) Before this change, the error detail would mention the first pair of conflicting parent compression or storage methods. But with this change it waits till the child specification is considered by which time we may have encountered many such conflicting pairs. Hence the error detail after this change does not include the conflicting compression/storage methods. Those can be obtained from parent definitions if necessary. The code to maintain list of all conflicting methods or even the first conflicting pair does not seem worth the convenience it offers. This change is inline with what we do with conflicting default values. Before this commit, the specified storage method could be stored in ColumnDef::storage (CREATE TABLE ... LIKE) or ColumnDef::storage_name (CREATE TABLE ...). This caused the MergeChildAttribute() and MergeInheritedAttribute() to ignore a storage method specified in the child definition since it looked only at ColumnDef::storage. This commit removes ColumnDef::storage and instead uses ColumnDef::storage_name to save any storage method specification. This is similar to how compression method specification is handled. Author: Ashutosh Bapat Discussion: https://www.postgresql.org/message-id/flat/24656cec-d6ef-4d15-8b5b-e8dfc9c833a7@eisentraut.org --- doc/src/sgml/ref/create_table.sgml | 8 +- src/backend/catalog/pg_type.c | 23 +++ src/backend/commands/tablecmds.c | 138 ++++++++---------- src/backend/nodes/makefuncs.c | 2 +- src/backend/parser/gram.y | 7 +- src/backend/parser/parse_utilcmd.c | 7 +- src/include/catalog/pg_type.h | 2 + src/include/nodes/parsenodes.h | 5 +- src/test/regress/expected/compression.out | 103 ++++++++++++- src/test/regress/expected/compression_1.out | 111 +++++++++++++- .../regress/expected/create_table_like.out | 12 +- src/test/regress/expected/inherit.out | 38 +++++ src/test/regress/sql/compression.sql | 43 +++++- src/test/regress/sql/create_table_like.sql | 2 +- src/test/regress/sql/inherit.sql | 20 +++ 15 files changed, 409 insertions(+), 112 deletions(-) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 4cbaaccaf7..fb0e987387 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -398,7 +398,13 @@ WITH ( MODULUS numeric_literal, REM - Column STORAGE settings are also copied from parent tables. + Column storage and compression settings are inherited from parent + tables. If a column is inherited from multiple tables, the storage + settings or any explicit compression settings for the column must be the + same in all parent tables, else an error is reported. Storage or + compression settings explicitly specified for the new table override any + inherited settings and can also be used to override conflicting + inherited settings. diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index fe47be38d0..ee604cea29 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -974,3 +974,26 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace) return pstrdup(buf); } + +/* + * GetAttributeStorageName + * returns the name corresponding to a typstorage/attstorage enum value. + */ +const char * +GetAttributeStorageName(char c) +{ + switch (c) + { + case TYPSTORAGE_PLAIN: + return "PLAIN"; + case TYPSTORAGE_EXTERNAL: + return "EXTERNAL"; + case TYPSTORAGE_EXTENDED: + return "EXTENDED"; + case TYPSTORAGE_MAIN: + return "MAIN"; + default: + elog(ERROR, "invalid storage type %c", c); + return NULL; + } +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86ea3a228b..237eeeec67 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -350,6 +350,16 @@ typedef struct ForeignTruncateInfo #define child_dependency_type(child_is_partition) \ ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL) +/* + * Bogus property string to track conflict in inherited properties of a column. + * It is currently used for storage and compression specifications, but may be + * used for other string specifications in future. It can be any string which + * does not look like a valid compression or storage method. It is meant to be + * used by MergeAttributes() and its minions. It is not expected to be stored + * on disk. + */ +static const char *conflicting_column_property = "*** conflicting column property ***"; + static void truncate_check_rel(Oid relid, Form_pg_class reltuple); static void truncate_check_perms(Oid relid, Form_pg_class reltuple); static void truncate_check_activity(Relation rel); @@ -360,7 +370,8 @@ static List *MergeAttributes(List *columns, const List *supers, char relpersiste List **supnotnulls); static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr); static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef); -static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef); +static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef, + bool *have_deferred_conflicts); static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition); static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel); static void StoreCatalogInheritance(Oid relationId, List *supers, @@ -620,7 +631,6 @@ static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode); static void index_copy_data(Relation rel, RelFileLocator newrlocator); -static const char *storage_name(char c); static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg); @@ -1363,9 +1373,7 @@ BuildDescForRelation(const List *columns) att->attidentity = entry->identity; att->attgenerated = entry->generated; att->attcompression = GetAttributeCompression(att->atttypid, entry->compression); - if (entry->storage) - att->attstorage = entry->storage; - else if (entry->storage_name) + if (entry->storage_name) att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name); } @@ -2388,28 +2396,6 @@ truncate_check_activity(Relation rel) CheckTableNotInUse(rel, "TRUNCATE"); } -/* - * storage_name - * returns the name corresponding to a typstorage/attstorage enum value - */ -static const char * -storage_name(char c) -{ - switch (c) - { - case TYPSTORAGE_PLAIN: - return "PLAIN"; - case TYPSTORAGE_EXTERNAL: - return "EXTERNAL"; - case TYPSTORAGE_EXTENDED: - return "EXTENDED"; - case TYPSTORAGE_MAIN: - return "MAIN"; - default: - return "???"; - } -} - /*---------- * MergeAttributes * Returns new schema given initial schema and superclasses. @@ -2483,7 +2469,7 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, List *inh_columns = NIL; List *constraints = NIL; List *nnconstraints = NIL; - bool have_bogus_defaults = false; + bool have_deferred_conflicts = false; int child_attno; static Node bogus_marker = {0}; /* marks conflicting defaults */ List *saved_columns = NIL; @@ -2720,11 +2706,10 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, */ newdef = makeColumnDef(attributeName, attribute->atttypid, attribute->atttypmod, attribute->attcollation); - newdef->storage = attribute->attstorage; + newdef->storage_name = GetAttributeStorageName(attribute->attstorage); newdef->generated = attribute->attgenerated; if (CompressionMethodIsValid(attribute->attcompression)) - newdef->compression = - pstrdup(GetCompressionMethodName(attribute->attcompression)); + newdef->compression = GetCompressionMethodName(attribute->attcompression); /* * Regular inheritance children are independent enough not to @@ -2744,7 +2729,8 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, /* * Yes, try to merge the two column definitions. */ - mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef); + mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef, + &have_deferred_conflicts); newattmap->attnums[parent_attno - 1] = exist_attno; @@ -2867,7 +2853,7 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, else if (!equal(def->cooked_default, this_default)) { def->cooked_default = &bogus_marker; - have_bogus_defaults = true; + have_deferred_conflicts = true; } } @@ -3077,10 +3063,10 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, } /* - * If we found any conflicting parent default values, check to make sure - * they were overridden by the child. + * If we found any conflicting parent default values or conflicting parent + * properties, check to make sure they were overridden by the child. */ - if (have_bogus_defaults) + if (have_deferred_conflicts) { foreach(lc, columns) { @@ -3101,6 +3087,20 @@ MergeAttributes(List *columns, const List *supers, char relpersistence, def->colname), errhint("To resolve the conflict, specify a default explicitly."))); } + + if (def->compression == conflicting_column_property) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" inherits conflicting compression methods", + def->colname), + errhint("To resolve the conflict, specify a compression method explicitly."))); + + if (def->storage_name == conflicting_column_property) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" inherits conflicting storage methods", + def->colname), + errhint("To resolve the conflict, specify a storage method explicitly."))); } } @@ -3250,33 +3250,18 @@ MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const inhdef->identity = newdef->identity; /* - * Copy storage parameter + * Child storage specification, if any, overrides inherited storage + * property. */ - if (inhdef->storage == 0) - inhdef->storage = newdef->storage; - else if (newdef->storage != 0 && inhdef->storage != newdef->storage) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" has a storage parameter conflict", - attributeName), - errdetail("%s versus %s", - storage_name(inhdef->storage), - storage_name(newdef->storage)))); + if (newdef->storage_name != NULL) + inhdef->storage_name = newdef->storage_name; /* - * Copy compression parameter + * Child compression specification, if any, overrides inherited + * compression property. */ - if (inhdef->compression == NULL) + if (newdef->compression != NULL) inhdef->compression = newdef->compression; - else if (newdef->compression != NULL) - { - if (strcmp(inhdef->compression, newdef->compression) != 0) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" has a compression method conflict", - attributeName), - errdetail("%s versus %s", inhdef->compression, newdef->compression))); - } /* * Merge of not-null constraints = OR 'em together @@ -3343,6 +3328,10 @@ MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const * 'exist_attno' is the number the existing matching attribute in inh_columns. * 'newdef' is the new parent column/attribute definition to be merged. * + * Output arguments: + * 'have_deferred_conflicts' is set to true if there is a conflict in inherited + * compression properties; remains unchanged otherwise. + * * The matching ColumnDef in 'inh_columns' list is modified and returned. * * Notes: @@ -3356,7 +3345,8 @@ MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, - const ColumnDef *newdef) + const ColumnDef *newdef, + bool *have_deferred_conflicts) { char *attributeName = newdef->colname; ColumnDef *prevdef; @@ -3403,28 +3393,26 @@ MergeInheritedAttribute(List *inh_columns, /* * Copy/check storage parameter */ - if (prevdef->storage == 0) - prevdef->storage = newdef->storage; - else if (prevdef->storage != newdef->storage) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("inherited column \"%s\" has a storage parameter conflict", - attributeName), - errdetail("%s versus %s", - storage_name(prevdef->storage), - storage_name(newdef->storage)))); + if (prevdef->storage_name == NULL) + prevdef->storage_name = newdef->storage_name; + else if (newdef->storage_name != NULL && + strcmp(prevdef->storage_name, newdef->storage_name) != 0) + { + prevdef->storage_name = conflicting_column_property; + *have_deferred_conflicts = true; + } /* * Copy/check compression parameter */ if (prevdef->compression == NULL) prevdef->compression = newdef->compression; - else if (strcmp(prevdef->compression, newdef->compression) != 0) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" has a compression method conflict", - attributeName), - errdetail("%s versus %s", prevdef->compression, newdef->compression))); + else if (newdef->compression != NULL && + strcmp(prevdef->compression, newdef->compression) != 0) + { + prevdef->compression = conflicting_column_property; + *have_deferred_conflicts = true; + } /* * Check for GENERATED conflicts diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index a02332a1ec..86b8dcfe7e 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -500,7 +500,7 @@ makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid) n->is_local = true; n->is_not_null = false; n->is_from_type = false; - n->storage = 0; + n->storage_name = NULL; n->raw_default = NULL; n->cooked_default = NULL; n->collClause = NULL; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 130f7fc7c3..60b31d9f85 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3754,7 +3754,6 @@ columnDef: ColId Typename opt_column_storage opt_column_compression create_gener n->is_local = true; n->is_not_null = false; n->is_from_type = false; - n->storage = 0; n->raw_default = NULL; n->cooked_default = NULL; n->collOid = InvalidOid; @@ -3776,7 +3775,7 @@ columnOptions: ColId ColQualList n->is_local = true; n->is_not_null = false; n->is_from_type = false; - n->storage = 0; + n->storage_name = NULL; n->raw_default = NULL; n->cooked_default = NULL; n->collOid = InvalidOid; @@ -3795,7 +3794,7 @@ columnOptions: ColId ColQualList n->is_local = true; n->is_not_null = false; n->is_from_type = false; - n->storage = 0; + n->storage_name = NULL; n->raw_default = NULL; n->cooked_default = NULL; n->collOid = InvalidOid; @@ -13858,7 +13857,7 @@ TableFuncElement: ColId Typename opt_collate_clause n->is_local = true; n->is_not_null = false; n->is_from_type = false; - n->storage = 0; + n->storage_name = NULL; n->raw_default = NULL; n->cooked_default = NULL; n->collClause = (CollateClause *) $3; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index c7efd8d8ce..8dcf794ca2 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1134,15 +1134,14 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla /* Likewise, copy storage if requested */ if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE) - def->storage = attribute->attstorage; + def->storage_name = GetAttributeStorageName(attribute->attstorage); else - def->storage = 0; + def->storage_name = NULL; /* Likewise, copy compression if requested */ if ((table_like_clause->options & CREATE_TABLE_LIKE_COMPRESSION) != 0 && CompressionMethodIsValid(attribute->attcompression)) - def->compression = - pstrdup(GetCompressionMethodName(attribute->attcompression)); + def->compression = GetCompressionMethodName(attribute->attcompression); else def->compression = NULL; diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index e925969732..38ca117480 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -404,4 +404,6 @@ extern bool moveArrayTypeName(Oid typeOid, const char *typeName, extern char *makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace); +extern const char *GetAttributeStorageName(char storage); + #endif /* PG_TYPE_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 476d55dd24..d5b08ded44 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -712,13 +712,12 @@ typedef struct ColumnDef NodeTag type; char *colname; /* name of column */ TypeName *typeName; /* type of column */ - char *compression; /* compression method for column */ + const char *compression; /* compression method for column */ 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? */ bool is_from_type; /* column definition came from table type */ - char storage; /* attstorage setting, or 0 for default */ - char *storage_name; /* attstorage setting name or NULL for default */ + const char *storage_name; /* attstorage setting name or NULL for default */ Node *raw_default; /* default value (untransformed parse tree) */ Node *cooked_default; /* default value (transformed expr tree) */ char identity; /* attidentity setting */ diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 834b7555cb..c22a71f8bd 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -223,15 +223,102 @@ SELECT pg_column_compression(f1) FROM cmpart2; pglz (1 row) --- test compression with inheritance, error -CREATE TABLE cminh() INHERITS(cmdata, cmdata1); -NOTICE: merging multiple inherited definitions of column "f1" -ERROR: column "f1" has a compression method conflict -DETAIL: pglz versus lz4 -CREATE TABLE cminh(f1 TEXT COMPRESSION lz4) INHERITS(cmdata); +-- test compression with inheritance +CREATE TABLE cmparent1 (f1 TEXT COMPRESSION pglz); +INSERT INTO cmparent1 VALUES ('cmparent1_' || repeat('1234567890', 1000)); +CREATE TABLE cmparent2 (f1 TEXT COMPRESSION lz4); +INSERT INTO cmparent2 VALUES ('cmparent2_' || repeat('1234567890', 1000)); +CREATE TABLE ncmparent (f1 TEXT); -- parent without compression +INSERT INTO ncmparent VALUES ('ncmparent_' || repeat('1234567890', 1000)); +CREATE TABLE cminh1(f1 TEXT) INHERITS(cmparent1); NOTICE: merging column "f1" with inherited definition -ERROR: column "f1" has a compression method conflict -DETAIL: pglz versus lz4 +INSERT INTO cminh1 VALUES ('cminh1_' || repeat('1234567890', 1000)); +CREATE TABLE cminh2(f1 TEXT) INHERITS(ncmparent, cmparent1); +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh2 VALUES ('cminh2_' || repeat('1234567890', 1000)); +CREATE TABLE cminh3(f1 TEXT) INHERITS(cmparent1, ncmparent); +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh3 VALUES ('cminh3_' || repeat('1234567890', 1000)); +-- conflicting compression methods from parents +CREATE TABLE cminh() INHERITS(cmparent1, cmparent2); --error +NOTICE: merging multiple inherited definitions of column "f1" +ERROR: column "f1" inherits conflicting compression methods +HINT: To resolve the conflict, specify a compression method explicitly. +CREATE TABLE cminh(f1 TEXT) INHERITS(cmparent1, cmparent2); --error +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +ERROR: column "f1" inherits conflicting compression methods +HINT: To resolve the conflict, specify a compression method explicitly. +-- child compression specification takes precedence, even if parent's +-- compression conflict +CREATE TABLE cminh4(f1 TEXT COMPRESSION lz4) INHERITS(cmparent1); +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh4 VALUES ('cminh4_' || repeat('1234567890', 1000)); +CREATE TABLE cminh5(f1 TEXT COMPRESSION pglz) INHERITS(cmparent1, cmparent2); +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh5 VALUES ('cminh5_' || repeat('1234567890', 1000)); +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent1; + tableoid | pg_column_compression +-----------+----------------------- + cmparent1 | pglz + cminh1 | pglz + cminh2 | pglz + cminh3 | pglz + cminh4 | lz4 + cminh5 | pglz +(6 rows) + +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent2; + tableoid | pg_column_compression +-----------+----------------------- + cmparent2 | lz4 + cminh5 | pglz +(2 rows) + +SELECT tableoid::regclass, pg_column_compression(f1) FROM ncmparent; + tableoid | pg_column_compression +-----------+----------------------- + ncmparent | pglz + cminh2 | pglz + cminh3 | pglz +(3 rows) + +-- ALTER compression specification in child +ALTER TABLE cminh1 ALTER COLUMN f1 SET COMPRESSION lz4; +INSERT INTO cminh1 VALUES ('cminh1_lz4_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh1; + pg_column_compression +----------------------- + pglz + lz4 +(2 rows) + +-- INHERIT through ALTER TABLE +CREATE TABLE cminh6 (f1 TEXT); +INSERT INTO cminh6 VALUES ('cminh6_' || repeat('1234567890', 1000)); +ALTER TABLE cminh6 INHERIT cmparent1; +INSERT INTO cminh6 VALUES ('cminh6_inh_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh6; + pg_column_compression +----------------------- + pglz + pglz +(2 rows) + +CREATE TABLE cminh7 (f1 TEXT COMPRESSION lz4); +INSERT INTO cminh7 VALUES ('cminh7_' || repeat('1234567890', 1000)); +ALTER TABLE cminh7 INHERIT cmparent1; +INSERT INTO cminh7 VALUES ('cminh7_inh_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh7; + pg_column_compression +----------------------- + lz4 + lz4 +(2 rows) + -- test default_toast_compression GUC SET default_toast_compression = ''; ERROR: invalid value for parameter "default_toast_compression": "" diff --git a/src/test/regress/expected/compression_1.out b/src/test/regress/expected/compression_1.out index ddcd137c49..d70155d5d0 100644 --- a/src/test/regress/expected/compression_1.out +++ b/src/test/regress/expected/compression_1.out @@ -216,13 +216,112 @@ SELECT pg_column_compression(f1) FROM cmpart2; ----------------------- (0 rows) --- test compression with inheritance, error -CREATE TABLE cminh() INHERITS(cmdata, cmdata1); -ERROR: relation "cmdata1" does not exist -CREATE TABLE cminh(f1 TEXT COMPRESSION lz4) INHERITS(cmdata); +-- test compression with inheritance +CREATE TABLE cmparent1 (f1 TEXT COMPRESSION pglz); +INSERT INTO cmparent1 VALUES ('cmparent1_' || repeat('1234567890', 1000)); +CREATE TABLE cmparent2 (f1 TEXT COMPRESSION lz4); +ERROR: compression method lz4 not supported +DETAIL: This functionality requires the server to be built with lz4 support. +INSERT INTO cmparent2 VALUES ('cmparent2_' || repeat('1234567890', 1000)); +ERROR: relation "cmparent2" does not exist +LINE 1: INSERT INTO cmparent2 VALUES ('cmparent2_' || repeat('123456... + ^ +CREATE TABLE ncmparent (f1 TEXT); -- parent without compression +INSERT INTO ncmparent VALUES ('ncmparent_' || repeat('1234567890', 1000)); +CREATE TABLE cminh1(f1 TEXT) INHERITS(cmparent1); NOTICE: merging column "f1" with inherited definition -ERROR: column "f1" has a compression method conflict -DETAIL: pglz versus lz4 +INSERT INTO cminh1 VALUES ('cminh1_' || repeat('1234567890', 1000)); +CREATE TABLE cminh2(f1 TEXT) INHERITS(ncmparent, cmparent1); +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh2 VALUES ('cminh2_' || repeat('1234567890', 1000)); +CREATE TABLE cminh3(f1 TEXT) INHERITS(cmparent1, ncmparent); +NOTICE: merging multiple inherited definitions of column "f1" +NOTICE: merging column "f1" with inherited definition +INSERT INTO cminh3 VALUES ('cminh3_' || repeat('1234567890', 1000)); +-- conflicting compression methods from parents +CREATE TABLE cminh() INHERITS(cmparent1, cmparent2); --error +ERROR: relation "cmparent2" does not exist +CREATE TABLE cminh(f1 TEXT) INHERITS(cmparent1, cmparent2); --error +ERROR: relation "cmparent2" does not exist +-- child compression specification takes precedence, even if parent's +-- compression conflict +CREATE TABLE cminh4(f1 TEXT COMPRESSION lz4) INHERITS(cmparent1); +NOTICE: merging column "f1" with inherited definition +ERROR: compression method lz4 not supported +DETAIL: This functionality requires the server to be built with lz4 support. +INSERT INTO cminh4 VALUES ('cminh4_' || repeat('1234567890', 1000)); +ERROR: relation "cminh4" does not exist +LINE 1: INSERT INTO cminh4 VALUES ('cminh4_' || repeat('1234567890',... + ^ +CREATE TABLE cminh5(f1 TEXT COMPRESSION pglz) INHERITS(cmparent1, cmparent2); +ERROR: relation "cmparent2" does not exist +INSERT INTO cminh5 VALUES ('cminh5_' || repeat('1234567890', 1000)); +ERROR: relation "cminh5" does not exist +LINE 1: INSERT INTO cminh5 VALUES ('cminh5_' || repeat('1234567890',... + ^ +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent1; + tableoid | pg_column_compression +-----------+----------------------- + cmparent1 | pglz + cminh1 | pglz + cminh2 | pglz + cminh3 | pglz +(4 rows) + +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent2; +ERROR: relation "cmparent2" does not exist +LINE 1: ...ableoid::regclass, pg_column_compression(f1) FROM cmparent2; + ^ +SELECT tableoid::regclass, pg_column_compression(f1) FROM ncmparent; + tableoid | pg_column_compression +-----------+----------------------- + ncmparent | pglz + cminh2 | pglz + cminh3 | pglz +(3 rows) + +-- ALTER compression specification in child +ALTER TABLE cminh1 ALTER COLUMN f1 SET COMPRESSION lz4; +ERROR: compression method lz4 not supported +DETAIL: This functionality requires the server to be built with lz4 support. +INSERT INTO cminh1 VALUES ('cminh1_lz4_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh1; + pg_column_compression +----------------------- + pglz + pglz +(2 rows) + +-- INHERIT through ALTER TABLE +CREATE TABLE cminh6 (f1 TEXT); +INSERT INTO cminh6 VALUES ('cminh6_' || repeat('1234567890', 1000)); +ALTER TABLE cminh6 INHERIT cmparent1; +INSERT INTO cminh6 VALUES ('cminh6_inh_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh6; + pg_column_compression +----------------------- + pglz + pglz +(2 rows) + +CREATE TABLE cminh7 (f1 TEXT COMPRESSION lz4); +ERROR: compression method lz4 not supported +DETAIL: This functionality requires the server to be built with lz4 support. +INSERT INTO cminh7 VALUES ('cminh7_' || repeat('1234567890', 1000)); +ERROR: relation "cminh7" does not exist +LINE 1: INSERT INTO cminh7 VALUES ('cminh7_' || repeat('1234567890',... + ^ +ALTER TABLE cminh7 INHERIT cmparent1; +ERROR: relation "cminh7" does not exist +INSERT INTO cminh7 VALUES ('cminh7_inh_' || repeat('1234567890', 1000)); +ERROR: relation "cminh7" does not exist +LINE 1: INSERT INTO cminh7 VALUES ('cminh7_inh_' || repeat('12345678... + ^ +SELECT pg_column_compression(f1) FROM cminh7; +ERROR: relation "cminh7" does not exist +LINE 1: SELECT pg_column_compression(f1) FROM cminh7; + ^ -- test default_toast_compression GUC SET default_toast_compression = ''; ERROR: invalid value for parameter "default_toast_compression": "" diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out index 61956773ff..fb786d35a3 100644 --- a/src/test/regress/expected/create_table_like.out +++ b/src/test/regress/expected/create_table_like.out @@ -445,12 +445,10 @@ SELECT s.stxname, objsubid, description FROM pg_description, pg_statistic_ext s CREATE TABLE inh_error1 () INHERITS (ctlt1, ctlt4); NOTICE: merging multiple inherited definitions of column "a" -ERROR: inherited column "a" has a storage parameter conflict -DETAIL: MAIN versus EXTENDED -CREATE TABLE inh_error2 (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); +ERROR: column "a" inherits conflicting storage methods +HINT: To resolve the conflict, specify a storage method explicitly. +CREATE TABLE ctlt14_inh_like (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); NOTICE: merging column "a" with inherited definition -ERROR: column "a" has a storage parameter conflict -DETAIL: MAIN versus EXTENDED -- Check that LIKE isn't confused by a system catalog of the same name CREATE TABLE pg_attrdef (LIKE ctlt1 INCLUDING ALL); \d+ public.pg_attrdef @@ -493,7 +491,9 @@ Statistics objects: ROLLBACK; DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE; -NOTICE: drop cascades to table inhe +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table inhe +drop cascades to table ctlt14_inh_like -- LIKE must respect NO INHERIT property of constraints CREATE TABLE noinh_con_copy (a int CHECK (a > 0) NO INHERIT); CREATE TABLE noinh_con_copy1 (LIKE noinh_con_copy INCLUDING CONSTRAINTS); diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 130a924228..1e6cc8e9ba 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -3419,3 +3419,41 @@ UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20; ERROR: no partition of relation "errtst_parent" found for row DETAIL: Partition key of the failing row contains (partid) = (30). DROP TABLE errtst_parent; +-- storage and inheritance +CREATE TABLE stparent1 (a TEXT STORAGE plain); +CREATE TABLE stparent2 (a TEXT); +-- child inherits parent's storage properties, if they do not conflict +CREATE TABLE stchild1 (a TEXT) INHERITS (stparent1); +NOTICE: merging column "a" with inherited definition +CREATE TABLE stchild2 (a TEXT) INHERITS (stparent1, stparent2); +NOTICE: merging multiple inherited definitions of column "a" +NOTICE: merging column "a" with inherited definition +ERROR: column "a" inherits conflicting storage methods +HINT: To resolve the conflict, specify a storage method explicitly. +-- child overrides parent's storage properties even if they conflict +CREATE TABLE stchild3 (a TEXT STORAGE main) INHERITS (stparent1); +NOTICE: merging column "a" with inherited definition +CREATE TABLE stchild4 (a TEXT STORAGE main) INHERITS (stparent1, stparent2); +NOTICE: merging multiple inherited definitions of column "a" +NOTICE: merging column "a" with inherited definition +-- child storage properties are not changed when inheriting after creation. +CREATE TABLE stchild5 (a TEXT); +ALTER TABLE stchild5 INHERIT stparent1; +CREATE TABLE stchild6 (a TEXT STORAGE main); +ALTER TABLE stchild6 INHERIT stparent1; +SELECT attrelid::regclass, attname, attstorage FROM pg_attribute + WHERE (attrelid::regclass::name like 'stparent%' + OR attrelid::regclass::name like 'stchild%') + and attname = 'a' + ORDER BY 1, 2; + attrelid | attname | attstorage +-----------+---------+------------ + stparent1 | a | p + stparent2 | a | x + stchild1 | a | p + stchild3 | a | m + stchild4 | a | m + stchild5 | a | x + stchild6 | a | m +(7 rows) + diff --git a/src/test/regress/sql/compression.sql b/src/test/regress/sql/compression.sql index 7179a5002e..ad8e7afd2a 100644 --- a/src/test/regress/sql/compression.sql +++ b/src/test/regress/sql/compression.sql @@ -93,9 +93,46 @@ INSERT INTO cmpart VALUES (repeat('123456789', 4004)); SELECT pg_column_compression(f1) FROM cmpart1; SELECT pg_column_compression(f1) FROM cmpart2; --- test compression with inheritance, error -CREATE TABLE cminh() INHERITS(cmdata, cmdata1); -CREATE TABLE cminh(f1 TEXT COMPRESSION lz4) INHERITS(cmdata); +-- test compression with inheritance +CREATE TABLE cmparent1 (f1 TEXT COMPRESSION pglz); +INSERT INTO cmparent1 VALUES ('cmparent1_' || repeat('1234567890', 1000)); +CREATE TABLE cmparent2 (f1 TEXT COMPRESSION lz4); +INSERT INTO cmparent2 VALUES ('cmparent2_' || repeat('1234567890', 1000)); +CREATE TABLE ncmparent (f1 TEXT); -- parent without compression +INSERT INTO ncmparent VALUES ('ncmparent_' || repeat('1234567890', 1000)); +CREATE TABLE cminh1(f1 TEXT) INHERITS(cmparent1); +INSERT INTO cminh1 VALUES ('cminh1_' || repeat('1234567890', 1000)); +CREATE TABLE cminh2(f1 TEXT) INHERITS(ncmparent, cmparent1); +INSERT INTO cminh2 VALUES ('cminh2_' || repeat('1234567890', 1000)); +CREATE TABLE cminh3(f1 TEXT) INHERITS(cmparent1, ncmparent); +INSERT INTO cminh3 VALUES ('cminh3_' || repeat('1234567890', 1000)); +-- conflicting compression methods from parents +CREATE TABLE cminh() INHERITS(cmparent1, cmparent2); --error +CREATE TABLE cminh(f1 TEXT) INHERITS(cmparent1, cmparent2); --error +-- child compression specification takes precedence, even if parent's +-- compression conflict +CREATE TABLE cminh4(f1 TEXT COMPRESSION lz4) INHERITS(cmparent1); +INSERT INTO cminh4 VALUES ('cminh4_' || repeat('1234567890', 1000)); +CREATE TABLE cminh5(f1 TEXT COMPRESSION pglz) INHERITS(cmparent1, cmparent2); +INSERT INTO cminh5 VALUES ('cminh5_' || repeat('1234567890', 1000)); +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent1; +SELECT tableoid::regclass, pg_column_compression(f1) FROM cmparent2; +SELECT tableoid::regclass, pg_column_compression(f1) FROM ncmparent; +-- ALTER compression specification in child +ALTER TABLE cminh1 ALTER COLUMN f1 SET COMPRESSION lz4; +INSERT INTO cminh1 VALUES ('cminh1_lz4_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh1; +-- INHERIT through ALTER TABLE +CREATE TABLE cminh6 (f1 TEXT); +INSERT INTO cminh6 VALUES ('cminh6_' || repeat('1234567890', 1000)); +ALTER TABLE cminh6 INHERIT cmparent1; +INSERT INTO cminh6 VALUES ('cminh6_inh_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh6; +CREATE TABLE cminh7 (f1 TEXT COMPRESSION lz4); +INSERT INTO cminh7 VALUES ('cminh7_' || repeat('1234567890', 1000)); +ALTER TABLE cminh7 INHERIT cmparent1; +INSERT INTO cminh7 VALUES ('cminh7_inh_' || repeat('1234567890', 1000)); +SELECT pg_column_compression(f1) FROM cminh7; -- test default_toast_compression GUC SET default_toast_compression = ''; diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql index 4929d373a2..9c5758a72b 100644 --- a/src/test/regress/sql/create_table_like.sql +++ b/src/test/regress/sql/create_table_like.sql @@ -168,7 +168,7 @@ SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_clas SELECT s.stxname, objsubid, description FROM pg_description, pg_statistic_ext s WHERE classoid = 'pg_statistic_ext'::regclass AND objoid = s.oid AND s.stxrelid = 'ctlt_all'::regclass ORDER BY s.stxname, objsubid; CREATE TABLE inh_error1 () INHERITS (ctlt1, ctlt4); -CREATE TABLE inh_error2 (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); +CREATE TABLE ctlt14_inh_like (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); -- Check that LIKE isn't confused by a system catalog of the same name CREATE TABLE pg_attrdef (LIKE ctlt1 INCLUDING ALL); diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 0ef9a61bc1..6e629121ae 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -1354,3 +1354,23 @@ UPDATE errtst_parent SET partid = 0, data = data + 10 WHERE partid = 20; UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20; DROP TABLE errtst_parent; + +-- storage and inheritance +CREATE TABLE stparent1 (a TEXT STORAGE plain); +CREATE TABLE stparent2 (a TEXT); +-- child inherits parent's storage properties, if they do not conflict +CREATE TABLE stchild1 (a TEXT) INHERITS (stparent1); +CREATE TABLE stchild2 (a TEXT) INHERITS (stparent1, stparent2); +-- child overrides parent's storage properties even if they conflict +CREATE TABLE stchild3 (a TEXT STORAGE main) INHERITS (stparent1); +CREATE TABLE stchild4 (a TEXT STORAGE main) INHERITS (stparent1, stparent2); +-- child storage properties are not changed when inheriting after creation. +CREATE TABLE stchild5 (a TEXT); +ALTER TABLE stchild5 INHERIT stparent1; +CREATE TABLE stchild6 (a TEXT STORAGE main); +ALTER TABLE stchild6 INHERIT stparent1; +SELECT attrelid::regclass, attname, attstorage FROM pg_attribute + WHERE (attrelid::regclass::name like 'stparent%' + OR attrelid::regclass::name like 'stchild%') + and attname = 'a' + ORDER BY 1, 2;