提交 9685afb0 编写于 作者: T Tom Lane

Add default expressions to INSERTs during planning, not during parse

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.
上级 a9b6691a
......@@ -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.
*/
......
......@@ -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;
}
......@@ -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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册