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;