diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/catalog/heap.c | 6 | ||||
-rw-r--r-- | src/backend/optimizer/prep/preptlist.c | 229 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 61 |
3 files changed, 161 insertions, 135 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index f78ac286931..f0ec85a7f86 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.179 2001/10/25 05:49:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.180 2001/11/02 20:23:02 tgl Exp $ * * * INTERFACE ROUTINES @@ -1651,9 +1651,9 @@ AddRelationRawConstraints(Relation rel, * column's type. We store the expression without coercion, * however, to avoid premature coercion in cases like * - * CREATE TABLE tbl (fld datetime DEFAULT 'now'); + * CREATE TABLE tbl (fld datetime DEFAULT 'now'::text); * - * NB: this should match the code in updateTargetListEntry() that + * NB: this should match the code in optimizer/prep/preptlist.c that * will actually do the coercion, to ensure we don't accept an * unusable default expression. */ diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 2e335b808b7..7e23481627c 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.44 2001/10/25 05:49:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.45 2001/11/02 20:23:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,10 @@ #include "nodes/makefuncs.h" #include "optimizer/prep.h" #include "parser/parsetree.h" +#include "parser/parse_coerce.h" +#include "parser/parse_expr.h" +#include "parser/parse_target.h" +#include "utils/builtins.h" #include "utils/lsyscache.h" @@ -35,6 +39,7 @@ static List *expand_targetlist(List *tlist, int command_type, static TargetEntry *process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle, int attrno); +static Node *build_column_default(Relation rel, int attrno); /* @@ -168,6 +173,7 @@ expand_targetlist(List *tlist, int command_type, { new_tle = process_matched_tle(old_tle, new_tle, attrno); tlistentry_used[old_tlist_index] = true; + /* keep scanning to detect multiple assignments to attr */ } old_tlist_index++; } @@ -177,97 +183,44 @@ expand_targetlist(List *tlist, int command_type, /* * Didn't find a matching tlist entry, so make one. * - * For INSERT, generate a constant of the default value for the - * attribute type, or NULL if no default value. + * For INSERT, generate an appropriate default value. * * For UPDATE, generate a Var reference to the existing value of * the attribute, so that it gets copied to the new tuple. */ Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; + Node *new_expr; switch (command_type) { case CMD_INSERT: - { - bool hasdefault; - Datum typedefault; - int16 typlen; - bool typbyval; - Const *def_const; - - if (att_tup->attisset) - { - /* - * Set attributes are represented as OIDs no - * matter what the set element type is, and - * the element type's default is irrelevant - * too. - */ - hasdefault = false; - typedefault = (Datum) 0; - typlen = sizeof(Oid); - typbyval = true; - } - else - { -#ifdef _DROP_COLUMN_HACK__ - if (COLUMN_IS_DROPPED(att_tup)) - { - hasdefault = false; - typedefault = (Datum) 0; - } - else -#endif /* _DROP_COLUMN_HACK__ */ - hasdefault = get_typdefault(atttype, - &typedefault); - - get_typlenbyval(atttype, &typlen, &typbyval); - } - - def_const = makeConst(atttype, - typlen, - typedefault, - !hasdefault, - typbyval, - false, /* not a set */ - false); - - new_tle = makeTargetEntry(makeResdom(attrno, - atttype, - -1, - pstrdup(attrname), - false), - (Node *) def_const); - break; - } + new_expr = build_column_default(rel, attrno); + break; case CMD_UPDATE: - { - Var *temp_var; - #ifdef _DROP_COLUMN_HACK__ - if (COLUMN_IS_DROPPED(att_tup)) - temp_var = (Var *) makeNullConst(atttype); - else + if (COLUMN_IS_DROPPED(att_tup)) + new_expr = (Node *) makeNullConst(atttype); + else #endif /* _DROP_COLUMN_HACK__ */ - temp_var = makeVar(result_relation, - attrno, - atttype, - atttypmod, - 0); - - new_tle = makeTargetEntry(makeResdom(attrno, - atttype, - atttypmod, - pstrdup(attrname), - false), - (Node *) temp_var); - break; - } + new_expr = (Node *) makeVar(result_relation, + attrno, + atttype, + atttypmod, + 0); + break; default: elog(ERROR, "expand_targetlist: unexpected command_type"); + new_expr = NULL; /* keep compiler quiet */ break; } + + new_tle = makeTargetEntry(makeResdom(attrno, + atttype, + atttypmod, + pstrdup(attrname), + false), + new_expr); } new_tlist = lappend(new_tlist, new_tle); @@ -385,3 +338,129 @@ process_matched_tle(TargetEntry *src_tle, resdom->resno = attrno; return makeTargetEntry(resdom, (Node *) newexpr); } + + +/* + * Make an expression tree for the default value for a column. + * + * This is used to fill in missing attributes in an INSERT targetlist. + * We look first to see if the column has a default value expression. + * If not, generate a constant of the default value for the attribute type, + * or a NULL if the type has no default value either. + */ +static Node * +build_column_default(Relation rel, int attrno) +{ + TupleDesc rd_att = rel->rd_att; + Form_pg_attribute att_tup = rd_att->attrs[attrno - 1]; + Oid atttype = att_tup->atttypid; + int32 atttypmod = att_tup->atttypmod; + bool hasdefault; + Datum typedefault; + int16 typlen; + bool typbyval; + Node *expr; + + /* + * Scan to see if relation has a default for this column. + */ + if (rd_att->constr && rd_att->constr->num_defval > 0) + { + AttrDefault *defval = rd_att->constr->defval; + int ndef = rd_att->constr->num_defval; + + while (--ndef >= 0) + { + if (attrno == defval[ndef].adnum) + { + Oid type_id; + + /* + * Found it, convert string representation to node tree. + */ + expr = stringToNode(defval[ndef].adbin); + + /* + * Make sure the value is coerced to the target column type + * (might not be right type yet if it's not a constant!) + * This should match the parser's processing of non-defaulted + * expressions --- see updateTargetListEntry(). + */ + type_id = exprType(expr); + + if (type_id != atttype) + { + expr = CoerceTargetExpr(NULL, expr, type_id, + atttype, atttypmod); + /* + * This really shouldn't fail; should have checked the + * default's type when it was created ... + */ + if (expr == NULL) + elog(ERROR, "Column \"%s\" is of type %s" + " but default expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + NameStr(att_tup->attname), + format_type_be(atttype), + format_type_be(type_id)); + } + + /* + * If the column is a fixed-length type, it may need a + * length coercion as well as a type coercion. + */ + expr = coerce_type_typmod(NULL, expr, + atttype, atttypmod); + return expr; + } + } + } + + /* + * No per-column default, so look for a default for the type itself. + * If there isn't one, we generate a NULL constant of the correct type. + */ + if (att_tup->attisset) + { + /* + * Set attributes are represented as OIDs no matter what the set + * element type is, and the element type's default is irrelevant too. + */ + hasdefault = false; + typedefault = (Datum) 0; + typlen = sizeof(Oid); + typbyval = true; + } + else + { +#ifdef _DROP_COLUMN_HACK__ + if (COLUMN_IS_DROPPED(att_tup)) + { + hasdefault = false; + typedefault = (Datum) 0; + } + else +#endif /* _DROP_COLUMN_HACK__ */ + hasdefault = get_typdefault(atttype, &typedefault); + + get_typlenbyval(atttype, &typlen, &typbyval); + } + + expr = (Node *) makeConst(atttype, + typlen, + typedefault, + !hasdefault, + typbyval, + false, /* not a set */ + false); + + /* + * If the column is a fixed-length type, it may need a length coercion + * as well as a type coercion. But NULLs don't need that. + */ + if (hasdefault) + expr = coerce_type_typmod(NULL, expr, + atttype, atttypmod); + + return expr; +} diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 5b2f4362fb6..29435695bb7 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.206 2001/10/31 04:49:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.207 2001/11/02 20:23:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -345,9 +345,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; List *attnos; - int numuseratts; List *tl; - TupleDesc rd_att; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; @@ -488,7 +486,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * Prepare columns for assignment to target table. */ - numuseratts = 0; attnos = attrnos; foreach(tl, qry->targetList) { @@ -501,65 +498,15 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) id = (Ident *) lfirst(icolumns); updateTargetListEntry(pstate, tle, id->name, lfirsti(attnos), id->indirection); - numuseratts++; icolumns = lnext(icolumns); attnos = lnext(attnos); } /* - * It is possible that the targetlist has fewer entries than were in - * the columns list. We do not consider this an error (perhaps we - * should, if the columns list was explictly given?). We must - * truncate the attrnos list to only include the attrs actually - * provided, else we will fail to apply defaults for them below. + * XXX It is possible that the targetlist has fewer entries than were in + * the columns list. We do not consider this an error. Perhaps we + * should, if the columns list was explicitly given? */ - if (icolumns != NIL) - attrnos = ltruncate(numuseratts, attrnos); - - /* - * Add targetlist items to assign DEFAULT values to any columns that - * have defaults and were not assigned to by the user. - * - * XXX wouldn't it make more sense to do this further downstream, after - * the rule rewriter? As is, altering a column default will not - * change the behavior of INSERTs in already-defined rules. - */ - rd_att = pstate->p_target_relation->rd_att; - if (rd_att->constr && rd_att->constr->num_defval > 0) - { - Form_pg_attribute *att = rd_att->attrs; - AttrDefault *defval = rd_att->constr->defval; - int ndef = rd_att->constr->num_defval; - - while (--ndef >= 0) - { - AttrNumber attrno = defval[ndef].adnum; - Form_pg_attribute thisatt = att[attrno - 1]; - TargetEntry *te; - - if (intMember((int) attrno, attrnos)) - continue; /* there was a user-specified value */ - - /* - * No user-supplied value, so add a targetentry with DEFAULT - * expr and correct data for the target column. - */ - te = makeTargetEntry(makeResdom(attrno, - thisatt->atttypid, - thisatt->atttypmod, - pstrdup(NameStr(thisatt->attname)), - false), - stringToNode(defval[ndef].adbin)); - qry->targetList = lappend(qry->targetList, te); - - /* - * Make sure the value is coerced to the target column type - * (might not be right type if it's not a constant!) - */ - updateTargetListEntry(pstate, te, te->resdom->resname, attrno, - NIL); - } - } /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; |