Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b9ae55f

Browse files
committed
Undo not-so-hot decision to postpone insertion of default values into
INSERT statements to the planner. Taking it out of the parser was right (so that defaults don't get into stored rules), but it has to happen before rewrite rule expansion, else references to NEW.field behave incorrectly. Accordingly, add a step to the rewriter to insert defaults just before rewrite-rule expansion.
1 parent c9d70e2 commit b9ae55f

File tree

2 files changed

+348
-265
lines changed

2 files changed

+348
-265
lines changed

src/backend/optimizer/prep/preptlist.c

Lines changed: 49 additions & 263 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.51 2002/04/02 08:51:51 inoue Exp $
18+
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.52 2002/04/05 05:47:05 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -27,19 +27,10 @@
2727
#include "nodes/makefuncs.h"
2828
#include "optimizer/prep.h"
2929
#include "parser/parsetree.h"
30-
#include "parser/parse_coerce.h"
31-
#include "parser/parse_expr.h"
32-
#include "parser/parse_target.h"
33-
#include "utils/builtins.h"
34-
#include "utils/lsyscache.h"
3530

3631

3732
static List *expand_targetlist(List *tlist, int command_type,
3833
Index result_relation, List *range_table);
39-
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
40-
TargetEntry *prior_tle,
41-
int attrno);
42-
static Node *build_column_default(Relation rel, int attrno);
4334

4435

4536
/*
@@ -119,31 +110,25 @@ preprocess_targetlist(List *tlist,
119110
/*
120111
* expand_targetlist
121112
* Given a target list as generated by the parser and a result relation,
122-
* add targetlist entries for any missing attributes, and order the
123-
* non-junk attributes in proper field order.
113+
* add targetlist entries for any missing attributes, and ensure the
114+
* non-junk attributes appear in proper field order.
115+
*
116+
* NOTE: if you are tempted to put more processing here, consider whether
117+
* it shouldn't go in the rewriter's rewriteTargetList() instead.
124118
*/
125119
static List *
126120
expand_targetlist(List *tlist, int command_type,
127121
Index result_relation, List *range_table)
128122
{
129-
int old_tlist_len = length(tlist);
130123
List *new_tlist = NIL;
131-
bool *tlistentry_used;
132124
Relation rel;
133125
int attrno,
134-
numattrs,
135-
old_tlist_index;
136-
List *temp;
126+
numattrs;
137127

138128
/*
139-
* Keep a map of which tlist items we have transferred to new list.
129+
* The rewriter should have already ensured that the TLEs are in
130+
* correct order; but we have to insert TLEs for any missing attributes.
140131
*
141-
* +1 here just keeps palloc from complaining if old_tlist_len==0.
142-
*/
143-
tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
144-
memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool));
145-
146-
/*
147132
* Scan the tuple description in the relation's relcache entry to make
148133
* sure we have all the user attributes in the right order.
149134
*/
@@ -154,36 +139,29 @@ expand_targetlist(List *tlist, int command_type,
154139
for (attrno = 1; attrno <= numattrs; attrno++)
155140
{
156141
Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
157-
char *attrname = NameStr(att_tup->attname);
158142
TargetEntry *new_tle = NULL;
159143

160-
/*
161-
* We match targetlist entries to attributes using the resname.
162-
* Junk attributes are not candidates to be matched.
163-
*/
164-
old_tlist_index = 0;
165-
foreach(temp, tlist)
144+
if (tlist != NIL)
166145
{
167-
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
146+
TargetEntry *old_tle = (TargetEntry *) lfirst(tlist);
168147
Resdom *resdom = old_tle->resdom;
169148

170-
if (!tlistentry_used[old_tlist_index] &&
171-
!resdom->resjunk &&
172-
strcmp(resdom->resname, attrname) == 0)
149+
if (!resdom->resjunk && resdom->resno == attrno)
173150
{
174-
new_tle = process_matched_tle(old_tle, new_tle, attrno);
175-
tlistentry_used[old_tlist_index] = true;
176-
/* keep scanning to detect multiple assignments to attr */
151+
Assert(strcmp(resdom->resname,
152+
NameStr(att_tup->attname)) == 0);
153+
new_tle = old_tle;
154+
tlist = lnext(tlist);
177155
}
178-
old_tlist_index++;
179156
}
180157

181158
if (new_tle == NULL)
182159
{
183160
/*
184161
* Didn't find a matching tlist entry, so make one.
185162
*
186-
* For INSERT, generate an appropriate default value.
163+
* For INSERT, generate a NULL constant. (We assume the
164+
* rewriter would have inserted any available default value.)
187165
*
188166
* For UPDATE, generate a Var reference to the existing value of
189167
* the attribute, so that it gets copied to the new tuple.
@@ -195,14 +173,20 @@ expand_targetlist(List *tlist, int command_type,
195173
switch (command_type)
196174
{
197175
case CMD_INSERT:
198-
new_expr = build_column_default(rel, attrno);
176+
new_expr = (Node *) makeConst(atttype,
177+
att_tup->attlen,
178+
(Datum) 0,
179+
true, /* isnull */
180+
att_tup->attbyval,
181+
false, /* not a set */
182+
false);
199183
break;
200184
case CMD_UPDATE:
201-
new_expr = (Node *) makeVar(result_relation,
202-
attrno,
203-
atttype,
204-
atttypmod,
205-
0);
185+
new_expr = (Node *) makeVar(result_relation,
186+
attrno,
187+
atttype,
188+
atttypmod,
189+
0);
206190
break;
207191
default:
208192
elog(ERROR, "expand_targetlist: unexpected command_type");
@@ -213,7 +197,7 @@ expand_targetlist(List *tlist, int command_type,
213197
new_tle = makeTargetEntry(makeResdom(attrno,
214198
atttype,
215199
atttypmod,
216-
pstrdup(attrname),
200+
pstrdup(NameStr(att_tup->attname)),
217201
false),
218202
new_expr);
219203
}
@@ -222,230 +206,32 @@ expand_targetlist(List *tlist, int command_type,
222206
}
223207

224208
/*
225-
* Copy all unprocessed tlist entries to the end of the new tlist,
226-
* making sure they are marked resjunk = true. Typical junk entries
227-
* include ORDER BY or GROUP BY expressions (are these actually
228-
* possible in an INSERT or UPDATE?), system attribute references,
229-
* etc.
209+
* The remaining tlist entries should be resjunk; append them all to
210+
* the end of the new tlist, making sure they have resnos higher than
211+
* the last real attribute. (Note: although the rewriter already did
212+
* such renumbering, we have to do it again here in case we are doing
213+
* an UPDATE in an inheritance child table with more columns.)
230214
*/
231-
old_tlist_index = 0;
232-
foreach(temp, tlist)
215+
while (tlist)
233216
{
234-
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
217+
TargetEntry *old_tle = (TargetEntry *) lfirst(tlist);
218+
Resdom *resdom = old_tle->resdom;
235219

236-
if (!tlistentry_used[old_tlist_index])
220+
if (!resdom->resjunk)
221+
elog(ERROR, "expand_targetlist: targetlist is not sorted correctly");
222+
/* Get the resno right, but don't copy unnecessarily */
223+
if (resdom->resno != attrno)
237224
{
238-
Resdom *resdom = old_tle->resdom;
239-
240-
if (!resdom->resjunk)
241-
elog(ERROR, "Unexpected assignment to attribute \"%s\"",
242-
resdom->resname);
243-
/* Get the resno right, but don't copy unnecessarily */
244-
if (resdom->resno != attrno)
245-
{
246-
resdom = (Resdom *) copyObject((Node *) resdom);
247-
resdom->resno = attrno;
248-
old_tle = makeTargetEntry(resdom, old_tle->expr);
249-
}
250-
new_tlist = lappend(new_tlist, old_tle);
251-
attrno++;
225+
resdom = (Resdom *) copyObject((Node *) resdom);
226+
resdom->resno = attrno;
227+
old_tle = makeTargetEntry(resdom, old_tle->expr);
252228
}
253-
old_tlist_index++;
229+
new_tlist = lappend(new_tlist, old_tle);
230+
attrno++;
231+
tlist = lnext(tlist);
254232
}
255233

256234
heap_close(rel, AccessShareLock);
257235

258-
pfree(tlistentry_used);
259-
260236
return new_tlist;
261237
}
262-
263-
264-
/*
265-
* Convert a matched TLE from the original tlist into a correct new TLE.
266-
*
267-
* This routine checks for multiple assignments to the same target attribute,
268-
* such as "UPDATE table SET foo = 42, foo = 43". This is OK only if they
269-
* are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43".
270-
* If so, we need to merge the operations into a single assignment op.
271-
* Essentially, the expression we want to produce in this case is like
272-
* foo = array_set(array_set(foo, 2, 42), 4, 43)
273-
*/
274-
static TargetEntry *
275-
process_matched_tle(TargetEntry *src_tle,
276-
TargetEntry *prior_tle,
277-
int attrno)
278-
{
279-
Resdom *resdom = src_tle->resdom;
280-
Node *priorbottom;
281-
ArrayRef *newexpr;
282-
283-
if (prior_tle == NULL)
284-
{
285-
/*
286-
* Normal case where this is the first assignment to the
287-
* attribute.
288-
*
289-
* We can recycle the old TLE+resdom if right resno; else make a new
290-
* one to avoid modifying the old tlist structure. (Is preserving
291-
* old tlist actually necessary? Not sure, be safe.)
292-
*/
293-
if (resdom->resno == attrno)
294-
return src_tle;
295-
resdom = (Resdom *) copyObject((Node *) resdom);
296-
resdom->resno = attrno;
297-
return makeTargetEntry(resdom, src_tle->expr);
298-
}
299-
300-
/*
301-
* Multiple assignments to same attribute. Allow only if all are
302-
* array-assign operators with same bottom array object.
303-
*/
304-
if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
305-
((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
306-
prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
307-
((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
308-
((ArrayRef *) src_tle->expr)->refelemtype !=
309-
((ArrayRef *) prior_tle->expr)->refelemtype)
310-
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
311-
resdom->resname);
312-
313-
/*
314-
* Prior TLE could be a nest of ArrayRefs if we do this more than
315-
* once.
316-
*/
317-
priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
318-
while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
319-
((ArrayRef *) priorbottom)->refassgnexpr != NULL)
320-
priorbottom = ((ArrayRef *) priorbottom)->refexpr;
321-
if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
322-
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
323-
resdom->resname);
324-
325-
/*
326-
* Looks OK to nest 'em.
327-
*/
328-
newexpr = makeNode(ArrayRef);
329-
memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
330-
newexpr->refexpr = prior_tle->expr;
331-
332-
resdom = (Resdom *) copyObject((Node *) resdom);
333-
resdom->resno = attrno;
334-
return makeTargetEntry(resdom, (Node *) newexpr);
335-
}
336-
337-
338-
/*
339-
* Make an expression tree for the default value for a column.
340-
*
341-
* This is used to fill in missing attributes in an INSERT targetlist.
342-
* We look first to see if the column has a default value expression.
343-
* If not, generate a constant of the default value for the attribute type,
344-
* or a NULL if the type has no default value either.
345-
*/
346-
static Node *
347-
build_column_default(Relation rel, int attrno)
348-
{
349-
TupleDesc rd_att = rel->rd_att;
350-
Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
351-
Oid atttype = att_tup->atttypid;
352-
int32 atttypmod = att_tup->atttypmod;
353-
int16 typlen = att_tup->attlen;
354-
bool typbyval = att_tup->attbyval;
355-
Node *expr = NULL;
356-
357-
/*
358-
* Scan to see if relation has a default for this column.
359-
*/
360-
if (rd_att->constr && rd_att->constr->num_defval > 0)
361-
{
362-
AttrDefault *defval = rd_att->constr->defval;
363-
int ndef = rd_att->constr->num_defval;
364-
365-
while (--ndef >= 0)
366-
{
367-
if (attrno == defval[ndef].adnum)
368-
{
369-
/*
370-
* Found it, convert string representation to node tree.
371-
*/
372-
expr = stringToNode(defval[ndef].adbin);
373-
break;
374-
}
375-
}
376-
}
377-
378-
if (expr == NULL)
379-
{
380-
/*
381-
* No per-column default, so look for a default for the type itself.
382-
*/
383-
if (att_tup->attisset)
384-
{
385-
/*
386-
* Set attributes are represented as OIDs no matter what the set
387-
* element type is, and the element type's default is irrelevant
388-
* too.
389-
*/
390-
typlen = sizeof(Oid);
391-
typbyval = true;
392-
}
393-
else
394-
{
395-
expr = get_typdefault(atttype);
396-
}
397-
}
398-
399-
if (expr == NULL)
400-
{
401-
/*
402-
* No default anywhere, so generate a NULL constant.
403-
*/
404-
expr = (Node *) makeConst(atttype,
405-
typlen,
406-
(Datum) 0,
407-
true, /* isnull */
408-
typbyval,
409-
false, /* not a set */
410-
false);
411-
}
412-
else
413-
{
414-
Oid exprtype;
415-
416-
/*
417-
* Make sure the value is coerced to the target column
418-
* type (might not be right type yet if it's not a
419-
* constant!) This should match the parser's processing of
420-
* non-defaulted expressions --- see
421-
* updateTargetListEntry().
422-
*/
423-
exprtype = exprType(expr);
424-
425-
if (exprtype != atttype)
426-
{
427-
expr = CoerceTargetExpr(NULL, expr, exprtype,
428-
atttype, atttypmod);
429-
430-
/*
431-
* This really shouldn't fail; should have checked the
432-
* default's type when it was created ...
433-
*/
434-
if (expr == NULL)
435-
elog(ERROR, "Column \"%s\" is of type %s"
436-
" but default expression is of type %s"
437-
"\n\tYou will need to rewrite or cast the expression",
438-
NameStr(att_tup->attname),
439-
format_type_be(atttype),
440-
format_type_be(exprtype));
441-
}
442-
443-
/*
444-
* If the column is a fixed-length type, it may need a
445-
* length coercion as well as a type coercion.
446-
*/
447-
expr = coerce_type_typmod(NULL, expr, atttype, atttypmod);
448-
}
449-
450-
return expr;
451-
}

0 commit comments

Comments
 (0)