prepunion.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 1,246 行 · 第 1/3 页
C
1,246 行
/*------------------------------------------------------------------------- * * prepunion.c * Routines to plan set-operation queries. The filename is a leftover * from a time when only UNIONs were implemented. * * There is also some code here to support planning of queries that use * inheritance (SELECT FROM foo*). This no longer has much connection * to the processing of UNION queries, but it's still here. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.127.2.1 2005/11/22 18:23:11 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/namespace.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/plancat.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/prep.h"#include "optimizer/tlist.h"#include "parser/parse_clause.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parsetree.h"#include "utils/lsyscache.h"typedef struct{ Index old_rt_index; Index new_rt_index; Oid old_rel_type; Oid new_rel_type; TupleDesc old_tupdesc; TupleDesc new_tupdesc; char *old_rel_name; char *new_rel_name;} adjust_inherited_attrs_context;static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root, double tuple_fraction, List *colTypes, bool junkOK, int flag, List *refnames_tlist, List **sortClauses);static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root, double tuple_fraction, List *refnames_tlist, List **sortClauses);static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **sortClauses);static List *recurse_union_children(Node *setOp, PlannerInfo *root, double tuple_fraction, SetOperationStmt *top_union, List *refnames_tlist);static List *generate_setop_tlist(List *colTypes, int flag, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist);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);static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);static List *adjust_inherited_tlist(List *tlist, adjust_inherited_attrs_context *context);/* * plan_set_operations * * Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT) * * This routine only deals with the setOperations tree of the given query. * Any top-level ORDER BY requested in root->parse->sortClause will be added * when we return to grouping_planner. * * tuple_fraction is the fraction of tuples we expect will be retrieved. * tuple_fraction is interpreted as for grouping_planner(); in particular, * zero means "all the tuples will be fetched". Any LIMIT present at the * top level has already been factored into tuple_fraction. * * *sortClauses is an output argument: it is set to a list of SortClauses * representing the result ordering of the topmost set operation. */Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction, List **sortClauses){ Query *parse = root->parse; SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations; Node *node; Query *leftmostQuery; Assert(topop && IsA(topop, SetOperationStmt)); /* check for unsupported stuff */ Assert(parse->utilityStmt == NULL); Assert(parse->jointree->fromlist == NIL); Assert(parse->jointree->quals == NULL); Assert(parse->groupClause == NIL); Assert(parse->havingQual == NULL); Assert(parse->distinctClause == NIL); /* * Find the leftmost component Query. We need to use its column names for * all generated tlists (else SELECT INTO won't work right). */ node = topop->larg; while (node && IsA(node, SetOperationStmt)) node = ((SetOperationStmt *) node)->larg; Assert(node && IsA(node, RangeTblRef)); leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex, parse->rtable)->subquery; Assert(leftmostQuery != NULL); /* * Recurse on setOperations tree to generate plans for set ops. The final * output plan should have just the column types shown as the output from * the top-level node, plus possibly resjunk working columns (we can rely * on upper-level nodes to deal with that). */ return recurse_set_operations((Node *) topop, root, tuple_fraction, topop->colTypes, true, -1, leftmostQuery->targetList, sortClauses);}/* * 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 */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 */ }}/* * Generate plan for a UNION or UNION ALL node */static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root, double tuple_fraction, List *refnames_tlist, List **sortClauses){ List *planlist; List *tlist; Plan *plan; /* * If plain UNION, tell children to fetch all tuples. * * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to * each arm of the UNION ALL. One could make a case for reducing the * tuple fraction for later arms (discounting by the expected size of the * earlier arms' results) but it seems not worth the trouble. The normal * case where tuple_fraction isn't already zero is a LIMIT at top level, * and passing it down as-is is usually enough to get the desired result * of preferring fast-start plans. */ if (!op->all) tuple_fraction = 0.0; /* * If any of my children are identical UNION nodes (same op, all-flag, and * colTypes) then they can be merged into this node so that we generate * only one Append and Sort for the lot. Recurse to find such nodes and * compute their children's plans. */ planlist = list_concat(recurse_union_children(op->larg, root, tuple_fraction, op, refnames_tlist), recurse_union_children(op->rarg, root, tuple_fraction, op, refnames_tlist)); /* * Generate tlist for Append plan node. * * The tlist for an Append plan isn't important as far as the Append is * concerned, but we must make it look real anyway for the benefit of the * next plan level up. */ tlist = generate_append_tlist(op->colTypes, false, planlist, refnames_tlist); /* * Append the child results together. */ plan = (Plan *) make_append(planlist, false, tlist); /* * For UNION ALL, we just need the Append plan. For UNION, need to add * Sort and Unique nodes to produce unique output. */ if (!op->all) { List *sortList; sortList = addAllTargetsToSortList(NULL, NIL, tlist, false); if (sortList) { plan = (Plan *) make_sort_from_sortclauses(root, sortList, plan); plan = (Plan *) make_unique(plan, sortList); } *sortClauses = sortList; } else *sortClauses = NIL; return plan;}/* * Generate plan for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node */static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root, List *refnames_tlist, List **sortClauses){ Plan *lplan, *rplan, *plan; List *tlist, *sortList, *planlist, *child_sortclauses; SetOpCmd cmd; /* Recurse on children, ensuring their outputs are marked */ lplan = recurse_set_operations(op->larg, root, 0.0 /* all tuples needed */ , op->colTypes, false, 0, refnames_tlist, &child_sortclauses); rplan = recurse_set_operations(op->rarg, root, 0.0 /* all tuples needed */ , op->colTypes, false, 1, refnames_tlist, &child_sortclauses); planlist = list_make2(lplan, rplan); /* * Generate tlist for Append plan node. * * The tlist for an Append plan isn't important as far as the Append is * concerned, but we must make it look real anyway for the benefit of the * next plan level up. In fact, it has to be real enough that the flag * column is shown as a variable not a constant, else setrefs.c will get * confused. */ tlist = generate_append_tlist(op->colTypes, true, planlist, refnames_tlist); /* * Append the child results together. */ plan = (Plan *) make_append(planlist, false, tlist); /* * Sort the child results, then add a SetOp plan node to generate the * correct output. */ sortList = addAllTargetsToSortList(NULL, NIL, tlist, false); if (sortList == NIL) /* nothing to sort on? */ { *sortClauses = NIL; return plan; } plan = (Plan *) make_sort_from_sortclauses(root, sortList, plan); switch (op->op) { case SETOP_INTERSECT: cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT; break; case SETOP_EXCEPT: cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT; break; default: elog(ERROR, "unrecognized set op: %d", (int) op->op); cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */ break; } plan = (Plan *) make_setop(cmd, plan, sortList, list_length(op->colTypes) + 1); *sortClauses = sortList; return plan;}/* * Pull up children of a UNION node that are identically-propertied UNIONs. * * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct * output rows will be lost anyway. */static List *recurse_union_children(Node *setOp, PlannerInfo *root, double tuple_fraction, SetOperationStmt *top_union, List *refnames_tlist){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?