]> granicus.if.org Git - postgresql/commitdiff
Add default expressions to INSERTs during planning, not during parse
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Nov 2001 20:23:02 +0000 (20:23 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Nov 2001 20:23:02 +0000 (20:23 +0000)
analysis.  This keeps stored rules from prematurely absorbing default
information, which is necessary for ALTER TABLE SET DEFAULT to work
unsurprisingly with rules.  See pgsql-bugs discussion 24-Oct-01.

src/backend/catalog/heap.c
src/backend/optimizer/prep/preptlist.c
src/backend/parser/analyze.c

index f78ac286931a7d32a4d56a314c057ebf9678a35b..f0ec85a7f8621d4caaa8ad05c2985572e5a34521 100644 (file)
@@ -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.
                 */
index 2e335b808b7b6a81b1dab984d8718df662939485..7e23481627c1fcaa3a56e81bc0a37b7b5d8a1367 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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;
+}
index 5b2f4362fb63fe9b58c10793bd28681a8aa8c1f1..29435695bb777b767359583d3f32376af14e6a13 100644 (file)
@@ -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;