@@ -2320,8 +2320,12 @@ storage_name(char c)
2320
2320
* (3) If conflicting defaults are inherited from different parents
2321
2321
* (and not overridden by the child), an error is raised.
2322
2322
* (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.
2325
2329
*----------
2326
2330
*/
2327
2331
static List *
@@ -2659,7 +2663,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
2659
2663
}
2660
2664
2661
2665
/*
2662
- * Locate default if any
2666
+ * Locate default/generation expression if any
2663
2667
*/
2664
2668
if (attribute->atthasdef)
2665
2669
{
@@ -2923,23 +2927,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
2923
2927
/*
2924
2928
* Check for conflicts related to generated columns.
2925
2929
*
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.)
2934
2941
*/
2935
2942
if (def->generated)
2936
2943
{
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.")));
2943
2944
if (newdef->raw_default && !newdef->generated)
2944
2945
ereport(ERROR,
2945
2946
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
@@ -2951,15 +2952,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
2951
2952
errmsg("column \"%s\" inherits from generated column but specifies identity",
2952
2953
def->colname)));
2953
2954
}
2954
-
2955
- /*
2956
- * If the parent column is not generated, then take whatever
2957
- * the child column definition says.
2958
- */
2959
2955
else
2960
2956
{
2961
2957
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.")));
2963
2963
}
2964
2964
2965
2965
/* If new def has a default, override previous default */
@@ -2994,8 +2994,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
2994
2994
/*
2995
2995
* Now that we have the column definition list for a partition, we can
2996
2996
* 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.
2999
2999
*/
3000
3000
if (is_partition)
3001
3001
{
@@ -3014,6 +3014,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
3014
3014
found = true;
3015
3015
coldef->is_not_null |= restdef->is_not_null;
3016
3016
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
+
3017
3030
/*
3018
3031
* Override the parent's default value for this column
3019
3032
* (coldef->cooked_default) with the partition's local
@@ -3058,7 +3071,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
3058
3071
ereport(ERROR,
3059
3072
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3060
3073
errmsg("column \"%s\" inherits conflicting generation expressions",
3061
- def->colname)));
3074
+ def->colname),
3075
+ errhint("To resolve the conflict, specify a generation expression explicitly.")));
3062
3076
else
3063
3077
ereport(ERROR,
3064
3078
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
@@ -15038,64 +15052,18 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
15038
15052
attributeName)));
15039
15053
15040
15054
/*
15041
- * If parent column is generated, child column must be, too .
15055
+ * Child column must be generated if and only if parent column is .
15042
15056
*/
15043
15057
if (attribute->attgenerated && !childatt->attgenerated)
15044
15058
ereport(ERROR,
15045
15059
(errcode(ERRCODE_DATATYPE_MISMATCH),
15046
15060
errmsg("column \"%s\" in child table must be a generated column",
15047
15061
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)));
15099
15067
15100
15068
/*
15101
15069
* OK, bump the child column's inheritance count. (If we fail
0 commit comments