diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 263e5024b6..e5856d9bfb 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -1,5 +1,5 @@ @@ -459,6 +459,22 @@ ALTER TABLE table ALTER COLUMN anycol TYPE anytype; data. + + The USING option of ALTER TYPE can actually + specify any expression involving the old values of the row; that is, it + can refer to other columns as well as the one being converted. This allows + very general conversions to be done with the ALTER TYPE + syntax. Because of this flexibility, the USING + expression is not applied to the column's default value (if any); the + result might not be a constant expression as required for a default. + This means that when there is no implicit or assignment cast from old to + new type, ALTER TYPE may fail to convert the default even + though a USING clause is supplied. In such cases, + drop the default with DROP DEFAULT, perform the ALTER + TYPE, and then use SET DEFAULT to add a suitable new + default. + + If a table has any descendant tables, it is not permitted to add, rename, or change the type of a column in the parent table without doing diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 463d2506c2..3616275858 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.136 2004/10/21 21:33:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.137 2004/10/22 17:20:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4738,13 +4738,20 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, * changing the column type, because build_column_default itself will * try to coerce, and will not issue the error message we want if it * fails.) + * + * We remove any implicit coercion steps at the top level of the old + * default expression; this has been agreed to satisfy the principle + * of least surprise. (The conversion to the new column type should + * act like it started from what the user sees as the stored expression, + * and the implicit coercions aren't going to be shown.) */ if (attTup->atthasdef) { defaultexpr = build_column_default(rel, attnum); Assert(defaultexpr); + defaultexpr = strip_implicit_coercions(defaultexpr); defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */ - defaultexpr, exprType(defaultexpr), + defaultexpr, exprType(defaultexpr), targettype, typename->typmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index b6ac7786aa..0f7d8dc31f 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.182 2004/10/07 18:38:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.183 2004/10/22 17:20:05 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1021,6 +1021,41 @@ CommuteClause(OpExpr *clause) lsecond(clause->args) = temp; } +/* + * strip_implicit_coercions: remove implicit coercions at top level of tree + * + * Note: there isn't any useful thing we can do with a RowExpr here, so + * just return it unchanged, even if it's marked as an implicit coercion. + */ +Node * +strip_implicit_coercions(Node *node) +{ + if (node == NULL) + return NULL; + if (IsA(node, FuncExpr)) + { + FuncExpr *f = (FuncExpr *) node; + + if (f->funcformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions(linitial(f->args)); + } + else if (IsA(node, RelabelType)) + { + RelabelType *r = (RelabelType *) node; + + if (r->relabelformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) r->arg); + } + else if (IsA(node, CoerceToDomain)) + { + CoerceToDomain *c = (CoerceToDomain *) node; + + if (c->coercionformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } + return node; +} + /* * set_coercionform_dontcare: set all CoercionForm fields to COERCE_DONTCARE * diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index d3fec6c444..0f452755e5 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.75 2004/08/29 04:13:07 momjian Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.76 2004/10/22 17:20:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -63,6 +63,8 @@ extern bool has_distinct_on_clause(Query *query); extern int NumRelids(Node *clause); extern void CommuteClause(OpExpr *clause); +extern Node *strip_implicit_coercions(Node *node); + extern void set_coercionform_dontcare(Node *node); extern Node *eval_const_expressions(Node *node);