diff options
Diffstat (limited to 'src/backend/optimizer/prep/preptlist.c')
-rw-r--r-- | src/backend/optimizer/prep/preptlist.c | 312 |
1 files changed, 49 insertions, 263 deletions
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 324674a5834..2f8d0d593e7 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.51 2002/04/02 08:51:51 inoue Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.52 2002/04/05 05:47:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,19 +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" static List *expand_targetlist(List *tlist, int command_type, Index result_relation, List *range_table); -static TargetEntry *process_matched_tle(TargetEntry *src_tle, - TargetEntry *prior_tle, - int attrno); -static Node *build_column_default(Relation rel, int attrno); /* @@ -119,31 +110,25 @@ preprocess_targetlist(List *tlist, /* * expand_targetlist * Given a target list as generated by the parser and a result relation, - * add targetlist entries for any missing attributes, and order the - * non-junk attributes in proper field order. + * add targetlist entries for any missing attributes, and ensure the + * non-junk attributes appear in proper field order. + * + * NOTE: if you are tempted to put more processing here, consider whether + * it shouldn't go in the rewriter's rewriteTargetList() instead. */ static List * expand_targetlist(List *tlist, int command_type, Index result_relation, List *range_table) { - int old_tlist_len = length(tlist); List *new_tlist = NIL; - bool *tlistentry_used; Relation rel; int attrno, - numattrs, - old_tlist_index; - List *temp; + numattrs; /* - * Keep a map of which tlist items we have transferred to new list. + * The rewriter should have already ensured that the TLEs are in + * correct order; but we have to insert TLEs for any missing attributes. * - * +1 here just keeps palloc from complaining if old_tlist_len==0. - */ - tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool)); - memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool)); - - /* * Scan the tuple description in the relation's relcache entry to make * sure we have all the user attributes in the right order. */ @@ -154,28 +139,20 @@ expand_targetlist(List *tlist, int command_type, for (attrno = 1; attrno <= numattrs; attrno++) { Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1]; - char *attrname = NameStr(att_tup->attname); TargetEntry *new_tle = NULL; - /* - * We match targetlist entries to attributes using the resname. - * Junk attributes are not candidates to be matched. - */ - old_tlist_index = 0; - foreach(temp, tlist) + if (tlist != NIL) { - TargetEntry *old_tle = (TargetEntry *) lfirst(temp); + TargetEntry *old_tle = (TargetEntry *) lfirst(tlist); Resdom *resdom = old_tle->resdom; - if (!tlistentry_used[old_tlist_index] && - !resdom->resjunk && - strcmp(resdom->resname, attrname) == 0) + if (!resdom->resjunk && resdom->resno == attrno) { - new_tle = process_matched_tle(old_tle, new_tle, attrno); - tlistentry_used[old_tlist_index] = true; - /* keep scanning to detect multiple assignments to attr */ + Assert(strcmp(resdom->resname, + NameStr(att_tup->attname)) == 0); + new_tle = old_tle; + tlist = lnext(tlist); } - old_tlist_index++; } if (new_tle == NULL) @@ -183,7 +160,8 @@ expand_targetlist(List *tlist, int command_type, /* * Didn't find a matching tlist entry, so make one. * - * For INSERT, generate an appropriate default value. + * For INSERT, generate a NULL constant. (We assume the + * rewriter would have inserted any available default value.) * * For UPDATE, generate a Var reference to the existing value of * the attribute, so that it gets copied to the new tuple. @@ -195,14 +173,20 @@ expand_targetlist(List *tlist, int command_type, switch (command_type) { case CMD_INSERT: - new_expr = build_column_default(rel, attrno); + new_expr = (Node *) makeConst(atttype, + att_tup->attlen, + (Datum) 0, + true, /* isnull */ + att_tup->attbyval, + false, /* not a set */ + false); break; case CMD_UPDATE: - new_expr = (Node *) makeVar(result_relation, - attrno, - atttype, - atttypmod, - 0); + new_expr = (Node *) makeVar(result_relation, + attrno, + atttype, + atttypmod, + 0); break; default: elog(ERROR, "expand_targetlist: unexpected command_type"); @@ -213,7 +197,7 @@ expand_targetlist(List *tlist, int command_type, new_tle = makeTargetEntry(makeResdom(attrno, atttype, atttypmod, - pstrdup(attrname), + pstrdup(NameStr(att_tup->attname)), false), new_expr); } @@ -222,230 +206,32 @@ expand_targetlist(List *tlist, int command_type, } /* - * Copy all unprocessed tlist entries to the end of the new tlist, - * making sure they are marked resjunk = true. Typical junk entries - * include ORDER BY or GROUP BY expressions (are these actually - * possible in an INSERT or UPDATE?), system attribute references, - * etc. + * The remaining tlist entries should be resjunk; append them all to + * the end of the new tlist, making sure they have resnos higher than + * the last real attribute. (Note: although the rewriter already did + * such renumbering, we have to do it again here in case we are doing + * an UPDATE in an inheritance child table with more columns.) */ - old_tlist_index = 0; - foreach(temp, tlist) + while (tlist) { - TargetEntry *old_tle = (TargetEntry *) lfirst(temp); + TargetEntry *old_tle = (TargetEntry *) lfirst(tlist); + Resdom *resdom = old_tle->resdom; - if (!tlistentry_used[old_tlist_index]) + if (!resdom->resjunk) + elog(ERROR, "expand_targetlist: targetlist is not sorted correctly"); + /* Get the resno right, but don't copy unnecessarily */ + if (resdom->resno != attrno) { - Resdom *resdom = old_tle->resdom; - - if (!resdom->resjunk) - elog(ERROR, "Unexpected assignment to attribute \"%s\"", - resdom->resname); - /* Get the resno right, but don't copy unnecessarily */ - if (resdom->resno != attrno) - { - resdom = (Resdom *) copyObject((Node *) resdom); - resdom->resno = attrno; - old_tle = makeTargetEntry(resdom, old_tle->expr); - } - new_tlist = lappend(new_tlist, old_tle); - attrno++; + resdom = (Resdom *) copyObject((Node *) resdom); + resdom->resno = attrno; + old_tle = makeTargetEntry(resdom, old_tle->expr); } - old_tlist_index++; + new_tlist = lappend(new_tlist, old_tle); + attrno++; + tlist = lnext(tlist); } heap_close(rel, AccessShareLock); - pfree(tlistentry_used); - return new_tlist; } - - -/* - * Convert a matched TLE from the original tlist into a correct new TLE. - * - * This routine checks for multiple assignments to the same target attribute, - * such as "UPDATE table SET foo = 42, foo = 43". This is OK only if they - * are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43". - * If so, we need to merge the operations into a single assignment op. - * Essentially, the expression we want to produce in this case is like - * foo = array_set(array_set(foo, 2, 42), 4, 43) - */ -static TargetEntry * -process_matched_tle(TargetEntry *src_tle, - TargetEntry *prior_tle, - int attrno) -{ - Resdom *resdom = src_tle->resdom; - Node *priorbottom; - ArrayRef *newexpr; - - if (prior_tle == NULL) - { - /* - * Normal case where this is the first assignment to the - * attribute. - * - * We can recycle the old TLE+resdom if right resno; else make a new - * one to avoid modifying the old tlist structure. (Is preserving - * old tlist actually necessary? Not sure, be safe.) - */ - if (resdom->resno == attrno) - return src_tle; - resdom = (Resdom *) copyObject((Node *) resdom); - resdom->resno = attrno; - return makeTargetEntry(resdom, src_tle->expr); - } - - /* - * Multiple assignments to same attribute. Allow only if all are - * array-assign operators with same bottom array object. - */ - if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) || - ((ArrayRef *) src_tle->expr)->refassgnexpr == NULL || - prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) || - ((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL || - ((ArrayRef *) src_tle->expr)->refelemtype != - ((ArrayRef *) prior_tle->expr)->refelemtype) - elog(ERROR, "Multiple assignments to same attribute \"%s\"", - resdom->resname); - - /* - * Prior TLE could be a nest of ArrayRefs if we do this more than - * once. - */ - priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr; - while (priorbottom != NULL && IsA(priorbottom, ArrayRef) && - ((ArrayRef *) priorbottom)->refassgnexpr != NULL) - priorbottom = ((ArrayRef *) priorbottom)->refexpr; - if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr)) - elog(ERROR, "Multiple assignments to same attribute \"%s\"", - resdom->resname); - - /* - * Looks OK to nest 'em. - */ - newexpr = makeNode(ArrayRef); - memcpy(newexpr, src_tle->expr, sizeof(ArrayRef)); - newexpr->refexpr = prior_tle->expr; - - resdom = (Resdom *) copyObject((Node *) resdom); - 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; - int16 typlen = att_tup->attlen; - bool typbyval = att_tup->attbyval; - Node *expr = NULL; - - /* - * 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) - { - /* - * Found it, convert string representation to node tree. - */ - expr = stringToNode(defval[ndef].adbin); - break; - } - } - } - - if (expr == NULL) - { - /* - * No per-column default, so look for a default for the type itself. - */ - 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. - */ - typlen = sizeof(Oid); - typbyval = true; - } - else - { - expr = get_typdefault(atttype); - } - } - - if (expr == NULL) - { - /* - * No default anywhere, so generate a NULL constant. - */ - expr = (Node *) makeConst(atttype, - typlen, - (Datum) 0, - true, /* isnull */ - typbyval, - false, /* not a set */ - false); - } - else - { - Oid exprtype; - - /* - * 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(). - */ - exprtype = exprType(expr); - - if (exprtype != atttype) - { - expr = CoerceTargetExpr(NULL, expr, exprtype, - 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(exprtype)); - } - - /* - * 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; -} |