Code review for UPDATE tab SET col = DEFAULT patch ... whack it around

so it has some chance of working in rules ...
This commit is contained in:
Tom Lane 2003-07-03 16:34:26 +00:00
parent 7b1885bf98
commit 455891bf96
15 changed files with 186 additions and 114 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/insert.sgml,v 1.22 2003/04/26 23:56:51 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/insert.sgml,v 1.23 2003/07/03 16:32:03 tgl Exp $
PostgreSQL documentation
-->
@ -33,7 +33,7 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
<para>
The columns in the target list may be listed in any order.
Each column not present in the target list will be inserted
using a default value, either a declared default value
using a default value, either its declared default value
or null.
</para>
@ -77,7 +77,7 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
<term><literal>DEFAULT VALUES</literal></term>
<listitem>
<para>
All columns will be filled their default values.
All columns will be filled with their default values.
</para>
</listitem>
</varlistentry>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/update.sgml,v 1.22 2003/06/25 04:19:24 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/update.sgml,v 1.23 2003/07/03 16:32:12 tgl Exp $
PostgreSQL documentation
-->
@ -28,7 +28,8 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<para>
<command>UPDATE</command> changes the values of the specified
columns in all rows that satisfy the condition. Only the columns to
be modified need appear as columns in the statement.
be modified need be mentioned in the statement; columns not explicitly
<literal>SET</> retain their previous values.
</para>
<para>
@ -41,8 +42,9 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<para>
You must have the <literal>UPDATE</literal> privilege on the table
to update it, as well as the <literal>SELECT</literal>
privilege to any table whose values are read in the <replaceable
class="parameter">condition</replaceable>.
privilege to any table whose values are read in the
<replaceable class="parameter">expression</replaceable>s or
<replaceable class="parameter">condition</replaceable>.
</para>
</refsect1>
@ -72,7 +74,8 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<term><replaceable class="PARAMETER">expression</replaceable></term>
<listitem>
<para>
An expression or value to assign to the column.
An expression to assign to the column. The expression may use the
old values of this and other columns in the table.
</para>
</listitem>
</varlistentry>
@ -81,7 +84,8 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<term><literal>DEFAULT</literal></term>
<listitem>
<para>
This column will be filled with its default value.
Set the column to its default value (which will be NULL if no
specific default expression has been assigned to it).
</para>
</listitem>
</varlistentry>
@ -91,7 +95,7 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<listitem>
<para>
A list of table expressions, allowing columns from other tables
to appear in the <literal>WHERE</> condition.
to appear in the <literal>WHERE</> condition and the update expressions.
</para>
</listitem>
</varlistentry>
@ -100,9 +104,9 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
<term><replaceable class="PARAMETER">condition</replaceable></term>
<listitem>
<para>
A value expression that returns a value of type
<type>boolean</type> that determines the rows which are to be
updated.
An expression that returns a value of type <type>boolean</type>.
Only rows for which this expression returns <literal>true</>
will be updated.
</para>
</listitem>
</varlistentry>
@ -135,9 +139,20 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
column <structfield>kind</> of the table <literal>films</literal>:
<programlisting>
UPDATE filme SET kind = 'Dramatic' WHERE kind = 'Drama';
UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';
</programlisting>
</para>
<para>
Adjust temperature entries and reset precipitation to its default
value in one row of the table <literal>weather</literal>:
<programlisting>
UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
WHERE city = 'San Francisco' AND date = '2003-07-03';
</programlisting>
</para>
</refsect1>
<refsect1>

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.258 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.259 2003/07/03 16:32:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1041,6 +1041,20 @@ _copyCoerceToDomainValue(CoerceToDomainValue *from)
return newnode;
}
/*
* _copySetToDefault
*/
static SetToDefault *
_copySetToDefault(SetToDefault *from)
{
SetToDefault *newnode = makeNode(SetToDefault);
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
return newnode;
}
/*
* _copyTargetEntry
*/
@ -1669,14 +1683,6 @@ _copyFuncWithArgs(FuncWithArgs *from)
return newnode;
}
static SetToDefault *
_copySetToDefault(SetToDefault *from)
{
SetToDefault *newnode = makeNode(SetToDefault);
return newnode;
}
static DeclareCursorStmt *
_copyDeclareCursorStmt(DeclareCursorStmt *from)
{
@ -2607,6 +2613,9 @@ copyObject(void *from)
case T_CoerceToDomainValue:
retval = _copyCoerceToDomainValue(from);
break;
case T_SetToDefault:
retval = _copySetToDefault(from);
break;
case T_TargetEntry:
retval = _copyTargetEntry(from);
break;
@ -2955,9 +2964,6 @@ copyObject(void *from)
case T_FuncWithArgs:
retval = _copyFuncWithArgs(from);
break;
case T_SetToDefault:
retval = _copySetToDefault(from);
break;
default:
elog(ERROR, "copyObject: don't know how to copy node type %d",

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.201 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.202 2003/07/03 16:32:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -485,6 +485,15 @@ _equalCoerceToDomainValue(CoerceToDomainValue *a, CoerceToDomainValue *b)
return true;
}
static bool
_equalSetToDefault(SetToDefault *a, SetToDefault *b)
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
return true;
}
static bool
_equalTargetEntry(TargetEntry *a, TargetEntry *b)
{
@ -740,12 +749,6 @@ _equalFuncWithArgs(FuncWithArgs *a, FuncWithArgs *b)
return true;
}
static bool
_equalSetToDefault(SetToDefault *a, SetToDefault *b)
{
return true;
}
static bool
_equalDeclareCursorStmt(DeclareCursorStmt *a, DeclareCursorStmt *b)
{
@ -1727,6 +1730,9 @@ equal(void *a, void *b)
case T_CoerceToDomainValue:
retval = _equalCoerceToDomainValue(a, b);
break;
case T_SetToDefault:
retval = _equalSetToDefault(a, b);
break;
case T_TargetEntry:
retval = _equalTargetEntry(a, b);
break;
@ -2073,9 +2079,6 @@ equal(void *a, void *b)
case T_FuncWithArgs:
retval = _equalFuncWithArgs(a, b);
break;
case T_SetToDefault:
retval = _equalSetToDefault(a, b);
break;
default:
elog(WARNING, "equal: don't know whether nodes of type %d are equal",

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.211 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.212 2003/07/03 16:32:38 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -849,6 +849,15 @@ _outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node)
WRITE_INT_FIELD(typeMod);
}
static void
_outSetToDefault(StringInfo str, SetToDefault *node)
{
WRITE_NODE_TYPE("SETTODEFAULT");
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
}
static void
_outTargetEntry(StringInfo str, TargetEntry *node)
{
@ -1685,6 +1694,9 @@ _outNode(StringInfo str, void *obj)
case T_CoerceToDomainValue:
_outCoerceToDomainValue(str, obj);
break;
case T_SetToDefault:
_outSetToDefault(str, obj);
break;
case T_TargetEntry:
_outTargetEntry(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.157 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.158 2003/07/03 16:32:39 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@ -760,6 +760,20 @@ _readCoerceToDomainValue(void)
READ_DONE();
}
/*
* _readSetToDefault
*/
static SetToDefault *
_readSetToDefault(void)
{
READ_LOCALS(SetToDefault);
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
READ_DONE();
}
/*
* _readTargetEntry
*/
@ -1005,6 +1019,8 @@ parseNodeString(void)
return_value = _readCoerceToDomain();
else if (MATCH("COERCETODOMAINVALUE", 19))
return_value = _readCoerceToDomainValue();
else if (MATCH("SETTODEFAULT", 12))
return_value = _readSetToDefault();
else if (MATCH("TARGETENTRY", 11))
return_value = _readTargetEntry();
else if (MATCH("RANGETBLREF", 11))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.144 2003/07/01 19:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.145 2003/07/03 16:33:07 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -2157,6 +2157,7 @@ expression_tree_walker(Node *node,
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
case T_SetToDefault:
case T_RangeTblRef:
/* primitive node types with no subnodes */
break;
@ -2514,6 +2515,7 @@ expression_tree_mutator(Node *node,
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
case T_SetToDefault:
case T_RangeTblRef:
/* primitive node types with no subnodes */
return (Node *) copyObject(node);

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.424 2003/07/01 00:04:31 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.425 2003/07/03 16:33:37 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -6999,15 +6999,14 @@ update_target_el:
$$ = makeNode(ResTarget);
$$->name = $1;
$$->indirection = $2;
$$->val = (Node *)$4;
$$->val = (Node *) $4;
}
| ColId opt_indirection '=' DEFAULT
{
SetToDefault *def = makeNode(SetToDefault);
$$ = makeNode(ResTarget);
$$->name = $1;
$$->indirection = NULL;
$$->val = (Node *)def;
$$->indirection = $2;
$$->val = (Node *) makeNode(SetToDefault);
}
;
@ -7021,11 +7020,10 @@ insert_target_el:
target_el { $$ = $1; }
| DEFAULT
{
SetToDefault *def = makeNode(SetToDefault);
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *)def;
$$->indirection = NIL;
$$->val = (Node *) makeNode(SetToDefault);
}
;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.154 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.155 2003/07/03 16:34:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -914,6 +914,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_RelabelType:
case T_CoerceToDomain:
case T_CoerceToDomainValue:
case T_SetToDefault:
{
result = (Node *) expr;
break;
@ -1291,6 +1292,9 @@ exprType(Node *expr)
case T_CoerceToDomainValue:
type = ((CoerceToDomainValue *) expr)->typeId;
break;
case T_SetToDefault:
type = ((SetToDefault *) expr)->typeId;
break;
case T_RangeVar:
/*
* If someone uses a bare relation name in an expression,
@ -1420,6 +1424,8 @@ exprTypmod(Node *expr)
return ((CoerceToDomain *) expr)->resulttypmod;
case T_CoerceToDomainValue:
return ((CoerceToDomainValue *) expr)->typeMod;
case T_SetToDefault:
return ((SetToDefault *) expr)->typeMod;
default:
break;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.105 2003/06/27 17:04:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.106 2003/07/03 16:34:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -178,24 +178,9 @@ transformTargetList(ParseState *pstate, List *targetlist)
false));
}
}
else if (IsA(res->val, SetToDefault))
{
/*
* If this is a DEFAULT element, we make a standard entry using
* the default for the target expression. rewriteTargetList will
* substitute the columns default for this expression.
*/
p_target = lappend(p_target,
makeTargetEntry(makeResdom((AttrNumber) pstate->p_next_resno++,
UNKNOWNOID,
-1,
res->name,
false),
(Expr *) res->val));
}
else
{
/* Everything else but ColumnRef and SetToDefault */
/* Everything else but ColumnRef */
p_target = lappend(p_target,
transformTargetEntry(pstate,
res->val,
@ -330,7 +315,6 @@ updateTargetListEntry(ParseState *pstate,
Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */
int32 attrtypmod;
bool isDefault = false;
Resdom *resnode = tle->resdom;
Relation rd = pstate->p_target_relation;
@ -340,15 +324,25 @@ updateTargetListEntry(ParseState *pstate,
attrtype = attnumTypeId(rd, attrno);
attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
/* The type of the default column is equivalent to that of the column */
if (tle->expr != NULL && IsA(tle->expr, SetToDefault))
/*
* If the expression is a DEFAULT placeholder, insert the attribute's
* type/typmod into it so that exprType will report the right things.
* (We expect that the eventually substituted default expression will
* in fact have this type and typmod.) Also, reject trying to update
* an array element with DEFAULT, since there can't be any default for
* individual elements of a column.
*/
if (tle->expr && IsA(tle->expr, SetToDefault))
{
type_id = attrtype;
isDefault = true;
SetToDefault *def = (SetToDefault *) tle->expr;
def->typeId = attrtype;
def->typeMod = attrtypmod;
if (indirection)
elog(ERROR, "cannot set an array element to DEFAULT");
}
/* Otherwise the expression holds the type */
else
/* Now we can use exprType() safely. */
type_id = exprType((Node *) tle->expr);
/*
@ -401,7 +395,7 @@ updateTargetListEntry(ParseState *pstate,
* coercion. But accept InvalidOid, which indicates the source is
* a NULL constant. (XXX is that still true?)
*/
if (!isDefault && type_id != InvalidOid)
if (type_id != InvalidOid)
{
tle->expr = (Expr *)
coerce_to_target_type(pstate,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.121 2003/06/25 04:19:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.122 2003/07/03 16:34:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -250,7 +250,9 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
* attributes that have defaults and are not assigned to in the given tlist.
* (We do not insert anything for default-less attributes, however. The
* planner will later insert NULLs for them, but there's no reason to slow
* down rewriter processing with extra tlist nodes.)
* down rewriter processing with extra tlist nodes.) Also, for both INSERT
* and UPDATE, replace explicit DEFAULT specifications with column default
* expressions.
*
* 2. Merge multiple entries for the same target attribute, or declare error
* if we can't. Presently, multiple entries are only allowed for UPDATE of
@ -307,40 +309,49 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
{
Assert(strcmp(resdom->resname,
NameStr(att_tup->attname)) == 0);
if (old_tle->expr != NULL && IsA(old_tle->expr, SetToDefault))
{
/* Set to the default value of the column, as requested */
Node *new_expr;
new_expr = build_column_default(target_relation, attrno);
new_tle = makeTargetEntry(makeResdom(attrno,
att_tup->atttypid,
att_tup->atttypmod,
pstrdup(NameStr(att_tup->attname)),
false),
(Expr *) new_expr);
}
else
/* Normal Case */
new_tle = process_matched_tle(old_tle, new_tle);
/* keep scanning to detect multiple assignments to attr */
}
}
if (new_tle == NULL && commandType == CMD_INSERT)
{
/*
* Didn't find a matching tlist entry; if it's an INSERT, look
* for a default value, and add a tlist entry computing the
* default if we find one.
* Handle the two cases where we need to insert a default expression:
* it's an INSERT and there's no tlist entry for the column, or the
* tlist entry is a DEFAULT placeholder node.
*/
if ((new_tle == NULL && commandType == CMD_INSERT) ||
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
{
Node *new_expr;
new_expr = build_column_default(target_relation, attrno);
/*
* If there is no default (ie, default is effectively NULL),
* we can omit the tlist entry in the INSERT case, since the
* planner can insert a NULL for itself, and there's no point
* in spending any more rewriter cycles on the entry. But in the
* UPDATE case we've got to explicitly set the column to NULL.
*/
if (!new_expr)
{
if (commandType == CMD_INSERT)
new_tle = NULL;
else
{
new_expr = (Node *) makeConst(att_tup->atttypid,
att_tup->attlen,
(Datum) 0,
true, /* isnull */
att_tup->attbyval);
/* this is to catch a NOT NULL domain constraint */
new_expr = coerce_to_domain(new_expr,
InvalidOid,
att_tup->atttypid,
COERCE_IMPLICIT_CAST);
}
}
if (new_expr)
new_tle = makeTargetEntry(makeResdom(attrno,
att_tup->atttypid,

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.143 2003/06/29 00:33:44 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.144 2003/07/03 16:34:25 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -2606,6 +2606,10 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfo(buf, "VALUE");
break;
case T_SetToDefault:
appendStringInfo(buf, "DEFAULT");
break;
default:
elog(ERROR, "get_rule_expr: unknown node type %d", nodeTag(node));
break;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.143 2003/06/29 00:33:44 tgl Exp $
* $Id: nodes.h,v 1.144 2003/07/03 16:34:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -120,6 +120,7 @@ typedef enum NodeTag
T_BooleanTest,
T_CoerceToDomain,
T_CoerceToDomainValue,
T_SetToDefault,
T_TargetEntry,
T_RangeTblRef,
T_JoinExpr,
@ -279,7 +280,6 @@ typedef enum NodeTag
T_PrivGrantee,
T_FuncWithArgs,
T_PrivTarget,
T_SetToDefault,
T_CreateOpClassItem,
T_CompositeTypeStmt,
T_InhRelation,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.242 2003/06/29 00:33:44 tgl Exp $
* $Id: parsenodes.h,v 1.243 2003/07/03 16:34:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -278,14 +278,6 @@ typedef struct ResTarget
* assign */
} ResTarget;
/*
* Empty node used as a marker for Default Columns
*/
typedef struct SetToDefault
{
NodeTag type;
} SetToDefault;
/*
* SortGroupBy - for ORDER BY clause
*/

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.86 2003/06/29 00:33:44 tgl Exp $
* $Id: primnodes.h,v 1.87 2003/07/03 16:34:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -674,6 +674,19 @@ typedef struct CoerceToDomainValue
int32 typeMod; /* typemod for substituted value */
} CoerceToDomainValue;
/*
* Placeholder node for a DEFAULT marker in an INSERT or UPDATE command.
*
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
*/
typedef struct SetToDefault
{
Expr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
} SetToDefault;
/*
* TargetEntry -