From: Tom Lane Date: Tue, 5 Mar 2002 05:10:24 +0000 (+0000) Subject: Previous patch to mark UNION outputs with common typmod (if any) breaks X-Git-Tag: REL7_3~1973 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=944671820fbc9e97be7bfdb05589f69428536632;p=postgresql Previous patch to mark UNION outputs with common typmod (if any) breaks three-or-more-way UNIONs, as per example from Josh Berkus. Cause is a fragile assumption that one tlist's entries will exactly match another. Restructure code to make that assumption a little less fragile. --- diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 9b7ea633b6..64d8b78f06 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.70 2002/03/01 06:01:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -63,7 +63,9 @@ static List *generate_setop_tlist(List *colTypes, int flag, bool hack_constants, List *input_tlist, List *refnames_tlist); -static void merge_tlist_typmods(List *tlist, List *planlist); +static List *generate_append_tlist(List *colTypes, bool flag, + List *input_plans, + List *refnames_tlist); static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK); static Node *adjust_inherited_attrs_mutator(Node *node, adjust_inherited_attrs_context *context); @@ -169,13 +171,11 @@ recurse_set_operations(Node *setOp, Query *parse, * * XXX you don't really want to know about this: setrefs.c will apply * replace_vars_with_subplan_refs() to the Result node's tlist. - * This would fail if the input plan's non-resjunk tlist entries - * were not all simple Vars equal() to the referencing Vars - * generated by generate_setop_tlist(). However, since the input - * plan was generated by generate_union_plan() or - * generate_nonunion_plan(), the referencing Vars will equal the - * tlist entries they reference. Ugly but I don't feel like making - * that code more general right now. + * This would fail if the Vars generated by generate_setop_tlist() + * were not exactly equal() to the corresponding tlist entries of + * the subplan. However, since the subplan was generated by + * generate_union_plan() or generate_nonunion_plan(), and hence its + * tlist was generated by generate_append_tlist(), this will work. */ if (flag >= 0 || !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) @@ -226,10 +226,8 @@ generate_union_plan(SetOperationStmt *op, Query *parse, * concerned, but we must make it look real anyway for the benefit of * the next plan level up. */ - tlist = generate_setop_tlist(op->colTypes, -1, false, - ((Plan *) lfirst(planlist))->targetlist, - refnames_tlist); - merge_tlist_typmods(tlist, planlist); + tlist = generate_append_tlist(op->colTypes, false, + planlist, refnames_tlist); /* * Append the child results together. @@ -285,10 +283,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, * flag column is shown as a variable not a constant, else setrefs.c * will get confused. */ - tlist = generate_setop_tlist(op->colTypes, 2, false, - lplan->targetlist, - refnames_tlist); - merge_tlist_typmods(tlist, planlist); + tlist = generate_append_tlist(op->colTypes, true, + planlist, refnames_tlist); /* * Append the child results together. @@ -368,8 +364,7 @@ recurse_union_children(Node *setOp, Query *parse, * Generate targetlist for a set-operation plan node * * colTypes: column datatypes for non-junk columns - * flag: -1 if no flag column needed, 0 or 1 to create a const flag column, - * 2 to create a variable flag column + * flag: -1 if no flag column needed, 0 or 1 to create a const flag column * hack_constants: true to copy up constants (see comments in code) * input_tlist: targetlist of this node's input node * refnames_tlist: targetlist to take column names from @@ -450,26 +445,14 @@ generate_setop_tlist(List *colTypes, int flag, -1, pstrdup("flag"), true); - if (flag <= 1) - { - /* flag value is the given constant */ - expr = (Node *) makeConst(INT4OID, - sizeof(int4), - Int32GetDatum(flag), - false, - true, - false, - false); - } - else - { - /* flag value is being copied up from subplan */ - expr = (Node *) makeVar(0, - resdom->resno, - INT4OID, - -1, - 0); - } + /* flag value is the given constant */ + expr = (Node *) makeConst(INT4OID, + sizeof(int4), + Int32GetDatum(flag), + false, + true, + false, + false); tlist = lappend(tlist, makeTargetEntry(resdom, expr)); } @@ -477,44 +460,117 @@ generate_setop_tlist(List *colTypes, int flag, } /* - * Merge typmods of a list of set-operation subplans. + * Generate targetlist for a set-operation Append node + * + * colTypes: column datatypes for non-junk columns + * flag: true to create a flag column copied up from subplans + * input_plans: list of sub-plans of the Append + * refnames_tlist: targetlist to take column names from * - * If the inputs all agree on type and typmod of a particular column, - * use that typmod; else use -1. We assume the result tlist has been - * initialized with the types and typmods of the first input subplan. + * The entries in the Append's targetlist should always be simple Vars; + * we just have to make sure they have the right datatypes and typmods. */ -static void -merge_tlist_typmods(List *tlist, List *planlist) +static List * +generate_append_tlist(List *colTypes, bool flag, + List *input_plans, + List *refnames_tlist) { + List *tlist = NIL; + int resno = 1; + List *curColType; + int colindex; + Resdom *resdom; + Node *expr; List *planl; + int32 *colTypmods; + + /* + * First extract typmods to use. + * + * If the inputs all agree on type and typmod of a particular column, + * use that typmod; else use -1. + */ + colTypmods = (int32 *) palloc(length(colTypes) * sizeof(int32)); - foreach(planl, planlist) + foreach(planl, input_plans) { Plan *subplan = (Plan *) lfirst(planl); - List *subtlist = subplan->targetlist; - List *restlist; + List *subtlist; - foreach(restlist, tlist) + curColType = colTypes; + colindex = 0; + foreach(subtlist, subplan->targetlist) { - TargetEntry *restle = (TargetEntry *) lfirst(restlist); - TargetEntry *subtle; + TargetEntry *subtle = (TargetEntry *) lfirst(subtlist); - if (restle->resdom->resjunk) + if (subtle->resdom->resjunk) continue; - Assert(subtlist != NIL); - subtle = (TargetEntry *) lfirst(subtlist); - while (subtle->resdom->resjunk) + Assert(curColType != NIL); + if (subtle->resdom->restype == (Oid) lfirsti(curColType)) { - subtlist = lnext(subtlist); - Assert(subtlist != NIL); - subtle = (TargetEntry *) lfirst(subtlist); + /* If first subplan, copy the typmod; else compare */ + if (planl == input_plans) + colTypmods[colindex] = subtle->resdom->restypmod; + else if (subtle->resdom->restypmod != colTypmods[colindex]) + colTypmods[colindex] = -1; } - if (restle->resdom->restype != subtle->resdom->restype || - restle->resdom->restypmod != subtle->resdom->restypmod) - restle->resdom->restypmod = -1; - subtlist = lnext(subtlist); + else + { + /* types disagree, so force typmod to -1 */ + colTypmods[colindex] = -1; + } + curColType = lnext(curColType); + colindex++; } + Assert(curColType == NIL); } + + /* + * Now we can build the tlist for the Append. + */ + colindex = 0; + foreach(curColType, colTypes) + { + Oid colType = (Oid) lfirsti(curColType); + int32 colTypmod = colTypmods[colindex++]; + TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist); + + Assert(reftle->resdom->resno == resno); + Assert(!reftle->resdom->resjunk); + expr = (Node *) makeVar(0, + resno, + colType, + colTypmod, + 0); + resdom = makeResdom((AttrNumber) resno++, + colType, + colTypmod, + pstrdup(reftle->resdom->resname), + false); + tlist = lappend(tlist, makeTargetEntry(resdom, expr)); + refnames_tlist = lnext(refnames_tlist); + } + + if (flag) + { + /* Add a resjunk flag column */ + resdom = makeResdom((AttrNumber) resno++, + INT4OID, + -1, + pstrdup("flag"), + true); + /* flag value is shown as copied up from subplan */ + expr = (Node *) makeVar(0, + resdom->resno, + INT4OID, + -1, + 0); + tlist = lappend(tlist, makeTargetEntry(resdom, expr)); + } + + pfree(colTypmods); + + return tlist; } /*