</para>
</listitem>
<listitem>
- <para>For inheritance:</para>
+ <para>For inheritance and partitioning:</para>
<itemizedlist>
<listitem>
<para>
- If a parent column is a generated column, a child column must also be
- a generated column using the same expression. In the definition of
- the child column, leave off the <literal>GENERATED</literal> clause,
- as it will be copied from the parent.
+ If a parent column is a generated column, its child column must also
+ be a generated column; however, the child column can have a
+ different generation expression. The generation expression that is
+ actually applied during insert or update of a row is the one
+ associated with the table that the row is physically in.
+ (This is unlike the behavior for column defaults: for those, the
+ default value associated with the table named in the query applies.)
</para>
</listitem>
<listitem>
<para>
- In case of multiple inheritance, if one parent column is a generated
- column, then all parent columns must be generated columns and with the
- same expression.
+ If a parent column is not a generated column, its child column must
+ not be generated either.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For inherited tables, if you write a child column definition without
+ any <literal>GENERATED</literal> clause in <command>CREATE TABLE
+ ... INHERITS</command>, then its <literal>GENERATED</literal> clause
+ will automatically be copied from the parent. <command>ALTER TABLE
+ ... INHERIT</command> will insist that parent and child columns
+ already match as to generation status, but it will not require their
+ generation expressions to match.
</para>
</listitem>
<listitem>
<para>
- If a parent column is not a generated column, a child column may be
- defined to be a generated column or not.
+ Similarly for partitioned tables, if you write a child column
+ definition without any <literal>GENERATED</literal> clause
+ in <command>CREATE TABLE ... PARTITION OF</command>, then
+ its <literal>GENERATED</literal> clause will automatically be copied
+ from the parent. <command>ALTER TABLE ... ADD PARTITION</command>
+ will insist that parent and child columns already match as to
+ generation status, but it will not require their generation
+ expressions to match.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ In case of multiple inheritance, if one parent column is a generated
+ column, then all parent columns must be generated columns. If they
+ do not all have the same generation expression, then the desired
+ expression for the child must be specified explicitly.
</para>
</listitem>
</itemizedlist>
* (3) If conflicting defaults are inherited from different parents
* (and not overridden by the child), an error is raised.
* (4) Otherwise the inherited default is used.
- * Rule (3) is new in Postgres 7.1; in earlier releases you got a
- * rather arbitrary choice of which parent default to use.
+ *
+ * Note that the default-value infrastructure is used for generated
+ * columns' expressions too, so most of the preceding paragraph applies
+ * to generation expressions too. We insist that a child column be
+ * generated if and only if its parent(s) are, but it need not have
+ * the same generation expression.
*----------
*/
static List *
}
/*
- * Locate default if any
+ * Locate default/generation expression if any
*/
if (attribute->atthasdef)
{
/*
* Check for conflicts related to generated columns.
*
- * If the parent column is generated, the child column must be
- * unadorned and will be made a generated column. (We could
- * in theory allow the child column definition specifying the
- * exact same generation expression, but that's a bit
- * complicated to implement and doesn't seem very useful.) We
- * also check that the child column doesn't specify a default
- * value or identity, which matches the rules for a single
- * column in parse_util.c.
+ * If the parent column is generated, the child column will be
+ * made a generated column if it isn't already. If it is a
+ * generated column, we'll take its generation expression in
+ * preference to the parent's. We must check that the child
+ * column doesn't specify a default value or identity, which
+ * matches the rules for a single column in parse_util.c.
+ *
+ * Conversely, if the parent column is not generated, the
+ * child column can't be either. (We used to allow that, but
+ * it results in being able to override the generation
+ * expression via UPDATEs through the parent.)
*/
if (def->generated)
{
- if (newdef->generated)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
- errmsg("child column \"%s\" specifies generation expression",
- def->colname),
- errhint("Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table.")));
if (newdef->raw_default && !newdef->generated)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
errmsg("column \"%s\" inherits from generated column but specifies identity",
def->colname)));
}
-
- /*
- * If the parent column is not generated, then take whatever
- * the child column definition says.
- */
else
{
if (newdef->generated)
- def->generated = newdef->generated;
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+ errmsg("child column \"%s\" specifies generation expression",
+ def->colname),
+ errhint("A child table column cannot be generated unless its parent column is.")));
}
/* If new def has a default, override previous default */
/*
* Now that we have the column definition list for a partition, we can
* check whether the columns referenced in the column constraint specs
- * actually exist. Also, we merge NOT NULL and defaults into each
- * corresponding column definition.
+ * actually exist. Also, we merge parent's NOT NULL constraints and
+ * defaults into each corresponding column definition.
*/
if (is_partition)
{
found = true;
coldef->is_not_null |= restdef->is_not_null;
+ /*
+ * As above, reject generated columns in partitions that
+ * are not generated in the parent.
+ */
+ if (restdef->generated && !coldef->generated)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+ errmsg("child column \"%s\" specifies generation expression",
+ restdef->colname),
+ errhint("A child table column cannot be generated unless its parent column is.")));
+ /* Other way around should have been dealt with above */
+ Assert(!(coldef->generated && !restdef->generated));
+
/*
* Override the parent's default value for this column
* (coldef->cooked_default) with the partition's local
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
errmsg("column \"%s\" inherits conflicting generation expressions",
- def->colname)));
+ def->colname),
+ errhint("To resolve the conflict, specify a generation expression explicitly.")));
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
attributeName)));
/*
- * If parent column is generated, child column must be, too.
+ * Child column must be generated if and only if parent column is.
*/
if (attribute->attgenerated && !childatt->attgenerated)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" in child table must be a generated column",
attributeName)));
-
- /*
- * Check that both generation expressions match.
- *
- * The test we apply is to see whether they reverse-compile to the
- * same source string. This insulates us from issues like whether
- * attributes have the same physical column numbers in parent and
- * child relations. (See also constraints_equivalent().)
- */
- if (attribute->attgenerated && childatt->attgenerated)
- {
- TupleConstr *child_constr = child_rel->rd_att->constr;
- TupleConstr *parent_constr = parent_rel->rd_att->constr;
- char *child_expr = NULL;
- char *parent_expr = NULL;
-
- Assert(child_constr != NULL);
- Assert(parent_constr != NULL);
-
- for (int i = 0; i < child_constr->num_defval; i++)
- {
- if (child_constr->defval[i].adnum == childatt->attnum)
- {
- child_expr =
- TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
- CStringGetTextDatum(child_constr->defval[i].adbin),
- ObjectIdGetDatum(child_rel->rd_id)));
- break;
- }
- }
- Assert(child_expr != NULL);
-
- for (int i = 0; i < parent_constr->num_defval; i++)
- {
- if (parent_constr->defval[i].adnum == attribute->attnum)
- {
- parent_expr =
- TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
- CStringGetTextDatum(parent_constr->defval[i].adbin),
- ObjectIdGetDatum(parent_rel->rd_id)));
- break;
- }
- }
- Assert(parent_expr != NULL);
-
- if (strcmp(child_expr, parent_expr) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("column \"%s\" in child table has a conflicting generation expression",
- attributeName)));
- }
+ if (childatt->attgenerated && !attribute->attgenerated)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" in child table must not be a generated column",
+ attributeName)));
/*
* OK, bump the child column's inheritance count. (If we fail
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("generated columns are not supported on typed tables")));
- if (cxt->partbound)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("generated columns are not supported on partitions")));
-
if (saw_generated)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
* that we'll correctly emit the necessary DEFAULT NULL clause; otherwise
* the backend will apply an inherited default to the column.
*
- * - Detect child columns that have a generation expression when their parents
- * also have one. Generation expressions are always inherited, so there is
- * no need to set them again in child tables, and there is no syntax for it
- * either. Exceptions: If it's a partition or we are in binary upgrade
- * mode, we dump them because in those cases inherited tables are recreated
- * standalone first and then reattached to the parent. (See also the logic
- * in dumpTableSchema().) In that situation, the generation expressions
- * must match the parent, enforced by ALTER TABLE.
+ * - Detect child columns that have a generation expression and all their
+ * parents also have the same generation expression, and if so suppress the
+ * child's expression. The child will inherit the generation expression
+ * automatically, so there's no need to dump it. This improves the dump's
+ * compatibility with pre-v16 servers, which didn't allow the child's
+ * expression to be given explicitly. Exceptions: If it's a partition or
+ * we are in binary upgrade mode, we dump such expressions anyway because
+ * in those cases inherited tables are recreated standalone first and then
+ * reattached to the parent. (See also the logic in dumpTableSchema().)
*
* modifies tblinfo
*/
{
bool foundNotNull; /* Attr was NOT NULL in a parent */
bool foundDefault; /* Found a default in a parent */
- bool foundGenerated; /* Found a generated in a parent */
+ bool foundSameGenerated; /* Found matching GENERATED */
+ bool foundDiffGenerated; /* Found non-matching GENERATED */
/* no point in examining dropped columns */
if (tbinfo->attisdropped[j])
foundNotNull = false;
foundDefault = false;
- foundGenerated = false;
+ foundSameGenerated = false;
+ foundDiffGenerated = false;
for (k = 0; k < numParents; k++)
{
TableInfo *parent = parents[k];
if (inhAttrInd >= 0)
{
foundNotNull |= parent->notnull[inhAttrInd];
- foundDefault |= (parent->attrdefs[inhAttrInd] != NULL && !parent->attgenerated[inhAttrInd]);
- foundGenerated |= parent->attgenerated[inhAttrInd];
+ foundDefault |= (parent->attrdefs[inhAttrInd] != NULL &&
+ !parent->attgenerated[inhAttrInd]);
+ if (parent->attgenerated[inhAttrInd])
+ {
+ /* these pointer nullness checks are just paranoia */
+ if (parent->attrdefs[inhAttrInd] != NULL &&
+ tbinfo->attrdefs[j] != NULL &&
+ strcmp(parent->attrdefs[inhAttrInd]->adef_expr,
+ tbinfo->attrdefs[j]->adef_expr) == 0)
+ foundSameGenerated = true;
+ else
+ foundDiffGenerated = true;
+ }
}
}
tbinfo->attrdefs[j] = attrDef;
}
- /* Remove generation expression from child */
- if (foundGenerated && !tbinfo->ispartition && !dopt->binary_upgrade)
+ /* Remove generation expression from child if possible */
+ if (foundSameGenerated && !foundDiffGenerated &&
+ !tbinfo->ispartition && !dopt->binary_upgrade)
tbinfo->attrdefs[j] = NULL;
}
}
{
/*
* Column generation expressions cannot be dumped separately,
- * because there is no syntax for it. The !shouldPrintColumn
- * case below will be tempted to set them to separate if they
- * are attached to an inherited column without a local
- * definition, but that would be wrong and unnecessary,
- * because generation expressions are always inherited, so
- * there is no need to set them again in child tables, and
- * there is no syntax for it either. By setting separate to
+ * because there is no syntax for it. By setting separate to
* false here we prevent the "default" from being processed as
* its own dumpable object, and flagInhAttrs() will remove it
- * from the table when it detects that it belongs to an
- * inherited column.
+ * from the table if possible (that is, if it can be inherited
+ * from a parent).
*/
attrdefs[j].separate = false;
}
4 | 8
(2 rows)
+-- can't have generated column that is a child of normal column
CREATE TABLE gtest_normal (a int, b int);
-CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal);
+CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal); -- error
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
-\d gtest_normal_child
- Table "public.gtest_normal_child"
- Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+------------------------------------
- a | integer | | |
- b | integer | | | generated always as (a * 2) stored
-Inherits: gtest_normal
-
-INSERT INTO gtest_normal (a) VALUES (1);
-INSERT INTO gtest_normal_child (a) VALUES (2);
-SELECT * FROM gtest_normal;
- a | b
----+---
- 1 |
- 2 | 4
-(2 rows)
-
-CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
-ALTER TABLE gtest_normal_child2 INHERIT gtest_normal;
-INSERT INTO gtest_normal_child2 (a) VALUES (3);
-SELECT * FROM gtest_normal;
- a | b
----+---
- 1 |
- 2 | 4
- 3 | 9
-(3 rows)
-
--- test inheritance mismatches between parent and child
-CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error
-NOTICE: merging column "b" with inherited definition
ERROR: child column "b" specifies generation expression
-HINT: Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table.
+HINT: A child table column cannot be generated unless its parent column is.
+CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED);
+ALTER TABLE gtest_normal_child INHERIT gtest_normal; -- error
+ERROR: column "b" in child table must not be a generated column
+DROP TABLE gtest_normal, gtest_normal_child;
+-- test inheritance mismatches between parent and child
CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error
NOTICE: merging column "b" with inherited definition
ERROR: column "b" inherits from generated column but specifies default
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
NOTICE: merging column "b" with inherited definition
ERROR: column "b" inherits from generated column but specifies identity
+CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- ok, overrides parent
+NOTICE: merging column "b" with inherited definition
+\d+ gtestx
+ Table "public.gtestx"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+-------------------------------------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+ b | integer | | | generated always as (a * 22) stored | plain | |
+ x | integer | | | | plain | |
+Inherits: gtest1
+
CREATE TABLE gtestxx_1 (a int NOT NULL, b int);
ALTER TABLE gtestxx_1 INHERIT gtest1; -- error
ERROR: column "b" in child table must be a generated column
-CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED);
-ALTER TABLE gtestxx_2 INHERIT gtest1; -- error
-ERROR: column "b" in child table has a conflicting generation expression
CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED);
ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
-- test multiple inheritance mismatches
+CREATE TABLE gtesty (x int, b int DEFAULT 55);
+CREATE TABLE gtest1_y () INHERITS (gtest0, gtesty); -- error
+NOTICE: merging multiple inherited definitions of column "b"
+ERROR: inherited column "b" has a generation conflict
+DROP TABLE gtesty;
CREATE TABLE gtesty (x int, b int);
-CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
+CREATE TABLE gtest1_y () INHERITS (gtest1, gtesty); -- error
NOTICE: merging multiple inherited definitions of column "b"
ERROR: inherited column "b" has a generation conflict
DROP TABLE gtesty;
CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED);
-CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
+CREATE TABLE gtest1_y () INHERITS (gtest1, gtesty); -- error
NOTICE: merging multiple inherited definitions of column "b"
ERROR: column "b" inherits conflicting generation expressions
-DROP TABLE gtesty;
-CREATE TABLE gtesty (x int, b int DEFAULT 55);
-CREATE TABLE gtest1_2 () INHERITS (gtest0, gtesty); -- error
+HINT: To resolve the conflict, specify a generation expression explicitly.
+CREATE TABLE gtest1_y (b int GENERATED ALWAYS AS (x + 1) STORED) INHERITS (gtest1, gtesty); -- ok
NOTICE: merging multiple inherited definitions of column "b"
-ERROR: inherited column "b" has a generation conflict
-DROP TABLE gtesty;
+NOTICE: moving and merging column "b" with inherited definition
+DETAIL: User-specified column moved to the position of the inherited column.
+\d gtest1_y
+ Table "public.gtest1_y"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+------------------------------------
+ a | integer | | not null |
+ b | integer | | | generated always as (x + 1) stored
+ x | integer | | |
+Inherits: gtest1,
+ gtesty
+
-- test correct handling of GENERATED column that's only in child
CREATE TABLE gtestp (f1 int);
CREATE TABLE gtestc (f2 int GENERATED ALWAYS AS (f1+1) STORED) INHERITS(gtestp);
CREATE TABLE gtest28 OF gtest_type (f1 WITH OPTIONS GENERATED ALWAYS AS (f2 *2) STORED);
ERROR: generated columns are not supported on typed tables
DROP TYPE gtest_type CASCADE;
--- table partitions (currently not supported)
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
+-- partitioning cases
+CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint) PARTITION BY RANGE (f1);
CREATE TABLE gtest_child PARTITION OF gtest_parent (
f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
-ERROR: generated columns are not supported on partitions
-DROP TABLE gtest_parent;
--- partitioned table
+ERROR: child column "f3" specifies generation expression
+HINT: A child table column cannot be generated unless its parent column is.
+CREATE TABLE gtest_child (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
+ERROR: column "f3" in child table must not be a generated column
+DROP TABLE gtest_parent, gtest_child;
CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f1);
-CREATE TABLE gtest_child PARTITION OF gtest_parent FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE gtest_child PARTITION OF gtest_parent
+ FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- inherits gen expr
+CREATE TABLE gtest_child2 PARTITION OF gtest_parent (
+ f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 22) STORED -- overrides gen expr
+) FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+ERROR: column "f3" in child table must be a generated column
+DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 33) STORED);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+\d gtest_child
+ Table "public.gtest_child"
+ Column | Type | Collation | Nullable | Default
+--------+--------+-----------+----------+-------------------------------------
+ f1 | date | | not null |
+ f2 | bigint | | |
+ f3 | bigint | | | generated always as (f2 * 2) stored
+Partition of: gtest_parent FOR VALUES FROM ('07-01-2016') TO ('08-01-2016')
+
+\d gtest_child2
+ Table "public.gtest_child2"
+ Column | Type | Collation | Nullable | Default
+--------+--------+-----------+----------+--------------------------------------
+ f1 | date | | not null |
+ f2 | bigint | | |
+ f3 | bigint | | | generated always as (f2 * 22) stored
+Partition of: gtest_parent FOR VALUES FROM ('08-01-2016') TO ('09-01-2016')
+
+\d gtest_child3
+ Table "public.gtest_child3"
+ Column | Type | Collation | Nullable | Default
+--------+--------+-----------+----------+--------------------------------------
+ f1 | date | | not null |
+ f2 | bigint | | |
+ f3 | bigint | | | generated always as (f2 * 33) stored
+Partition of: gtest_parent FOR VALUES FROM ('09-01-2016') TO ('10-01-2016')
+
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
SELECT * FROM gtest_parent;
f1 | f2 | f3
07-15-2016 | 1 | 2
(1 row)
-DROP TABLE gtest_parent;
+-- we leave these tables around for purposes of testing dump/reload/upgrade
-- generated columns in partition key (not allowed)
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
+CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
ERROR: cannot use generated column in partition key
LINE 1: ...ENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
^
DETAIL: Column "f3" is a generated column.
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
+CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
ERROR: cannot use generated column in partition key
LINE 1: ...ED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
^
SELECT * FROM gtest1_1;
SELECT * FROM gtest1;
+-- can't have generated column that is a child of normal column
CREATE TABLE gtest_normal (a int, b int);
-CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal);
-\d gtest_normal_child
-INSERT INTO gtest_normal (a) VALUES (1);
-INSERT INTO gtest_normal_child (a) VALUES (2);
-SELECT * FROM gtest_normal;
-
-CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
-ALTER TABLE gtest_normal_child2 INHERIT gtest_normal;
-INSERT INTO gtest_normal_child2 (a) VALUES (3);
-SELECT * FROM gtest_normal;
+CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal); -- error
+CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED);
+ALTER TABLE gtest_normal_child INHERIT gtest_normal; -- error
+DROP TABLE gtest_normal, gtest_normal_child;
-- test inheritance mismatches between parent and child
-CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error
CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
+CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- ok, overrides parent
+\d+ gtestx
CREATE TABLE gtestxx_1 (a int NOT NULL, b int);
ALTER TABLE gtestxx_1 INHERIT gtest1; -- error
-CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED);
-ALTER TABLE gtestxx_2 INHERIT gtest1; -- error
CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED);
ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
-- test multiple inheritance mismatches
-CREATE TABLE gtesty (x int, b int);
-CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
+CREATE TABLE gtesty (x int, b int DEFAULT 55);
+CREATE TABLE gtest1_y () INHERITS (gtest0, gtesty); -- error
DROP TABLE gtesty;
-CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED);
-CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
+CREATE TABLE gtesty (x int, b int);
+CREATE TABLE gtest1_y () INHERITS (gtest1, gtesty); -- error
DROP TABLE gtesty;
-CREATE TABLE gtesty (x int, b int DEFAULT 55);
-CREATE TABLE gtest1_2 () INHERITS (gtest0, gtesty); -- error
-DROP TABLE gtesty;
+CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED);
+CREATE TABLE gtest1_y () INHERITS (gtest1, gtesty); -- error
+CREATE TABLE gtest1_y (b int GENERATED ALWAYS AS (x + 1) STORED) INHERITS (gtest1, gtesty); -- ok
+\d gtest1_y
-- test correct handling of GENERATED column that's only in child
CREATE TABLE gtestp (f1 int);
CREATE TABLE gtest28 OF gtest_type (f1 WITH OPTIONS GENERATED ALWAYS AS (f2 *2) STORED);
DROP TYPE gtest_type CASCADE;
--- table partitions (currently not supported)
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
+-- partitioning cases
+CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint) PARTITION BY RANGE (f1);
CREATE TABLE gtest_child PARTITION OF gtest_parent (
f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
-DROP TABLE gtest_parent;
+CREATE TABLE gtest_child (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
+DROP TABLE gtest_parent, gtest_child;
--- partitioned table
CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f1);
-CREATE TABLE gtest_child PARTITION OF gtest_parent FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
+CREATE TABLE gtest_child PARTITION OF gtest_parent
+ FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- inherits gen expr
+CREATE TABLE gtest_child2 PARTITION OF gtest_parent (
+ f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 22) STORED -- overrides gen expr
+) FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 33) STORED);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+\d gtest_child
+\d gtest_child2
+\d gtest_child3
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
SELECT * FROM gtest_parent;
SELECT * FROM gtest_child;
-DROP TABLE gtest_parent;
+-- we leave these tables around for purposes of testing dump/reload/upgrade
-- generated columns in partition key (not allowed)
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
-CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
+CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3);
+CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3));
-- ALTER TABLE ... ADD COLUMN
CREATE TABLE gtest25 (a int PRIMARY KEY);