/* * recurse_set_operations * Recursively handle one step in a tree of set operations * * colTypes: list of type OIDs of expected output columns * junkOK: if true, child resjunk columns may be left in the result * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from */ static Plan * recurse_set_operations(Node *setOp, Query *parse, List *colTypes, bool junkOK, int flag, List *refnames_tlist) { if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, parse->rtable); Query *subquery = rte->subquery; Plan *subplan, *plan; Assert(subquery != NULL); /* * Generate plan for primitive subquery */ subplan = subquery_planner(subquery, 0.0 /* default case */ ); /* * Add a SubqueryScan with the caller-requested targetlist */ plan = (Plan *) make_subqueryscan(generate_setop_tlist(colTypes, flag, true, subplan->targetlist, refnames_tlist), NIL, rtr->rtindex, subplan); return plan; } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; Plan *plan; /* UNIONs are much different from INTERSECT/EXCEPT */ if (op->op == SETOP_UNION) plan = generate_union_plan(op, parse, refnames_tlist); else plan = generate_nonunion_plan(op, parse, refnames_tlist); /* * If necessary, add a Result node to project the caller-requested * output columns. * * 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 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)) { plan = (Plan *) make_result(generate_setop_tlist(colTypes, flag, false, plan->targetlist, refnames_tlist), NULL, plan); } return plan; } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); return NULL; /* keep compiler quiet */ } }
/* * recurse_set_operations * Recursively handle one step in a tree of set operations * * tuple_fraction: fraction of tuples we expect to retrieve from node * colTypes: list of type OIDs of expected output columns * junkOK: if true, child resjunk columns may be left in the result * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from * *sortClauses: receives list of SortClauses for result plan, if any * * We don't have to care about typmods here: the only allowed difference * between set-op input and output typmods is input is a specific typmod * and output is -1, and that does not require a coercion. */ static Plan * recurse_set_operations(Node *setOp, PlannerInfo *root, double tuple_fraction, List *colTypes, bool junkOK, int flag, List *refnames_tlist, List **sortClauses) { if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); Query *subquery = rte->subquery; Plan *subplan, *plan; Assert(subquery != NULL); /* * Generate plan for primitive subquery */ subplan = subquery_planner(subquery, tuple_fraction, NULL); /* * Add a SubqueryScan with the caller-requested targetlist */ plan = (Plan *) make_subqueryscan(generate_setop_tlist(colTypes, flag, rtr->rtindex, true, subplan->targetlist, refnames_tlist), NIL, rtr->rtindex, subplan); /* * We don't bother to determine the subquery's output ordering since * it won't be reflected in the set-op result anyhow. */ *sortClauses = NIL; return plan; } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; Plan *plan; /* UNIONs are much different from INTERSECT/EXCEPT */ if (op->op == SETOP_UNION) plan = generate_union_plan(op, root, tuple_fraction, refnames_tlist, sortClauses); else plan = generate_nonunion_plan(op, root, refnames_tlist, sortClauses); /* * If necessary, add a Result node to project the caller-requested * output columns. * * 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 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. We just tell * generate_setop_tlist() to use varno 0. */ if (flag >= 0 || !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) make_result(generate_setop_tlist(colTypes, flag, 0, false, plan->targetlist, refnames_tlist), NULL, plan); } return plan; } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); return NULL; /* keep compiler quiet */ } }