diff options
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 124 |
1 files changed, 46 insertions, 78 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1db3bd9e2e8..1fbdad4b649 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -2320,8 +2320,12 @@ storage_name(char c) * (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 * @@ -2659,7 +2663,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, } /* - * Locate default if any + * Locate default/generation expression if any */ if (attribute->atthasdef) { @@ -2923,23 +2927,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence, /* * 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), @@ -2951,15 +2952,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence, 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 */ @@ -2994,8 +2994,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, /* * 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) { @@ -3015,6 +3015,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence, 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 * definition (restdef->raw_default), if there's one. It @@ -3058,7 +3071,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, 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), @@ -15038,64 +15052,18 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) 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 |