@@ -2320,8 +2320,12 @@ storage_name(char c)
23202320 * (3) If conflicting defaults are inherited from different parents
23212321 * (and not overridden by the child), an error is raised.
23222322 * (4) Otherwise the inherited default is used.
2323- * Rule (3) is new in Postgres 7.1; in earlier releases you got a
2324- * rather arbitrary choice of which parent default to use.
2323+ *
2324+ * Note that the default-value infrastructure is used for generated
2325+ * columns' expressions too, so most of the preceding paragraph applies
2326+ * to generation expressions too. We insist that a child column be
2327+ * generated if and only if its parent(s) are, but it need not have
2328+ * the same generation expression.
23252329 *----------
23262330 */
23272331static List *
@@ -2659,7 +2663,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
26592663 }
26602664
26612665 /*
2662- * Locate default if any
2666+ * Locate default/generation expression if any
26632667 */
26642668 if (attribute->atthasdef)
26652669 {
@@ -2923,23 +2927,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
29232927 /*
29242928 * Check for conflicts related to generated columns.
29252929 *
2926- * If the parent column is generated, the child column must be
2927- * unadorned and will be made a generated column. (We could
2928- * in theory allow the child column definition specifying the
2929- * exact same generation expression, but that's a bit
2930- * complicated to implement and doesn't seem very useful.) We
2931- * also check that the child column doesn't specify a default
2932- * value or identity, which matches the rules for a single
2933- * column in parse_util.c.
2930+ * If the parent column is generated, the child column will be
2931+ * made a generated column if it isn't already. If it is a
2932+ * generated column, we'll take its generation expression in
2933+ * preference to the parent's. We must check that the child
2934+ * column doesn't specify a default value or identity, which
2935+ * matches the rules for a single column in parse_util.c.
2936+ *
2937+ * Conversely, if the parent column is not generated, the
2938+ * child column can't be either. (We used to allow that, but
2939+ * it results in being able to override the generation
2940+ * expression via UPDATEs through the parent.)
29342941 */
29352942 if (def->generated)
29362943 {
2937- if (newdef->generated)
2938- ereport(ERROR,
2939- (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2940- errmsg("child column \"%s\" specifies generation expression",
2941- def->colname),
2942- errhint("Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table.")));
29432944 if (newdef->raw_default && !newdef->generated)
29442945 ereport(ERROR,
29452946 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
@@ -2951,15 +2952,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
29512952 errmsg("column \"%s\" inherits from generated column but specifies identity",
29522953 def->colname)));
29532954 }
2954-
2955- /*
2956- * If the parent column is not generated, then take whatever
2957- * the child column definition says.
2958- */
29592955 else
29602956 {
29612957 if (newdef->generated)
2962- def->generated = newdef->generated;
2958+ ereport(ERROR,
2959+ (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2960+ errmsg("child column \"%s\" specifies generation expression",
2961+ def->colname),
2962+ errhint("A child table column cannot be generated unless its parent column is.")));
29632963 }
29642964
29652965 /* If new def has a default, override previous default */
@@ -2994,8 +2994,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
29942994 /*
29952995 * Now that we have the column definition list for a partition, we can
29962996 * check whether the columns referenced in the column constraint specs
2997- * actually exist. Also, we merge NOT NULL and defaults into each
2998- * corresponding column definition.
2997+ * actually exist. Also, we merge parent's NOT NULL constraints and
2998+ * defaults into each corresponding column definition.
29992999 */
30003000 if (is_partition)
30013001 {
@@ -3014,6 +3014,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
30143014 found = true;
30153015 coldef->is_not_null |= restdef->is_not_null;
30163016
3017+ /*
3018+ * As above, reject generated columns in partitions that
3019+ * are not generated in the parent.
3020+ */
3021+ if (restdef->generated && !coldef->generated)
3022+ ereport(ERROR,
3023+ (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3024+ errmsg("child column \"%s\" specifies generation expression",
3025+ restdef->colname),
3026+ errhint("A child table column cannot be generated unless its parent column is.")));
3027+ /* Other way around should have been dealt with above */
3028+ Assert(!(coldef->generated && !restdef->generated));
3029+
30173030 /*
30183031 * Override the parent's default value for this column
30193032 * (coldef->cooked_default) with the partition's local
@@ -3058,7 +3071,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
30583071 ereport(ERROR,
30593072 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
30603073 errmsg("column \"%s\" inherits conflicting generation expressions",
3061- def->colname)));
3074+ def->colname),
3075+ errhint("To resolve the conflict, specify a generation expression explicitly.")));
30623076 else
30633077 ereport(ERROR,
30643078 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
@@ -15038,64 +15052,18 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
1503815052 attributeName)));
1503915053
1504015054 /*
15041- * If parent column is generated, child column must be, too .
15055+ * Child column must be generated if and only if parent column is .
1504215056 */
1504315057 if (attribute->attgenerated && !childatt->attgenerated)
1504415058 ereport(ERROR,
1504515059 (errcode(ERRCODE_DATATYPE_MISMATCH),
1504615060 errmsg("column \"%s\" in child table must be a generated column",
1504715061 attributeName)));
15048-
15049- /*
15050- * Check that both generation expressions match.
15051- *
15052- * The test we apply is to see whether they reverse-compile to the
15053- * same source string. This insulates us from issues like whether
15054- * attributes have the same physical column numbers in parent and
15055- * child relations. (See also constraints_equivalent().)
15056- */
15057- if (attribute->attgenerated && childatt->attgenerated)
15058- {
15059- TupleConstr *child_constr = child_rel->rd_att->constr;
15060- TupleConstr *parent_constr = parent_rel->rd_att->constr;
15061- char *child_expr = NULL;
15062- char *parent_expr = NULL;
15063-
15064- Assert(child_constr != NULL);
15065- Assert(parent_constr != NULL);
15066-
15067- for (int i = 0; i < child_constr->num_defval; i++)
15068- {
15069- if (child_constr->defval[i].adnum == childatt->attnum)
15070- {
15071- child_expr =
15072- TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
15073- CStringGetTextDatum(child_constr->defval[i].adbin),
15074- ObjectIdGetDatum(child_rel->rd_id)));
15075- break;
15076- }
15077- }
15078- Assert(child_expr != NULL);
15079-
15080- for (int i = 0; i < parent_constr->num_defval; i++)
15081- {
15082- if (parent_constr->defval[i].adnum == attribute->attnum)
15083- {
15084- parent_expr =
15085- TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
15086- CStringGetTextDatum(parent_constr->defval[i].adbin),
15087- ObjectIdGetDatum(parent_rel->rd_id)));
15088- break;
15089- }
15090- }
15091- Assert(parent_expr != NULL);
15092-
15093- if (strcmp(child_expr, parent_expr) != 0)
15094- ereport(ERROR,
15095- (errcode(ERRCODE_DATATYPE_MISMATCH),
15096- errmsg("column \"%s\" in child table has a conflicting generation expression",
15097- attributeName)));
15098- }
15062+ if (childatt->attgenerated && !attribute->attgenerated)
15063+ ereport(ERROR,
15064+ (errcode(ERRCODE_DATATYPE_MISMATCH),
15065+ errmsg("column \"%s\" in child table must not be a generated column",
15066+ attributeName)));
1509915067
1510015068 /*
1510115069 * OK, bump the child column's inheritance count. (If we fail
0 commit comments