subselect.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,470 行 · 第 1/3 页
C
1,470 行
/*------------------------------------------------------------------------- * * subselect.c * Planning routines for subselects and parameters. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.129.2.2 2008/07/10 01:17:36 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/subselect.h"#include "optimizer/var.h"#include "parser/parse_expr.h"#include "parser/parse_relation.h"#include "parser/parsetree.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"typedef struct convert_testexpr_context{ PlannerInfo *root; List *subst_nodes; /* Nodes to substitute for Params */} convert_testexpr_context;typedef struct process_sublinks_context{ PlannerInfo *root; bool isTopQual;} process_sublinks_context;typedef struct finalize_primnode_context{ PlannerInfo *root; Bitmapset *paramids; /* Non-local PARAM_EXEC paramids found */} finalize_primnode_context;static List *generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds);static List *generate_subquery_vars(PlannerInfo *root, List *tlist, Index varno);static Node *convert_testexpr(PlannerInfo *root, Node *testexpr, List *subst_nodes);static Node *convert_testexpr_mutator(Node *node, convert_testexpr_context *context);static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan);static bool hash_ok_operator(OpExpr *expr);static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);static Node *process_sublinks_mutator(Node *node, process_sublinks_context *context);static Bitmapset *finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params);static bool finalize_primnode(Node *node, finalize_primnode_context *context);/* * Generate a Param node to replace the given Var, * which is expected to have varlevelsup > 0 (ie, it is not local). */static Param *replace_outer_var(PlannerInfo *root, Var *var){ Param *retval; ListCell *ppl; PlannerParamItem *pitem; Index abslevel; int i; Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level); abslevel = root->query_level - var->varlevelsup; /* * If there's already a paramlist entry for this same Var, just use it. * NOTE: in sufficiently complex querytrees, it is possible for the same * varno/abslevel to refer to different RTEs in different parts of the * parsetree, so that different fields might end up sharing the same Param * number. As long as we check the vartype as well, I believe that this * sort of aliasing will cause no trouble. The correct field should get * stored into the Param slot at execution in each part of the tree. * * We also need to demand a match on vartypmod. This does not matter for * the Param itself, since those are not typmod-dependent, but it does * matter when make_subplan() instantiates a modified copy of the Var for * a subplan's args list. */ i = 0; foreach(ppl, root->glob->paramlist) { pitem = (PlannerParamItem *) lfirst(ppl); if (pitem->abslevel == abslevel && IsA(pitem->item, Var)) { Var *pvar = (Var *) pitem->item; if (pvar->varno == var->varno && pvar->varattno == var->varattno && pvar->vartype == var->vartype && pvar->vartypmod == var->vartypmod) break; } i++; } if (!ppl) { /* Nope, so make a new one */ var = (Var *) copyObject(var); var->varlevelsup = 0; pitem = makeNode(PlannerParamItem); pitem->item = (Node *) var; pitem->abslevel = abslevel; root->glob->paramlist = lappend(root->glob->paramlist, pitem); /* i is already the correct index for the new item */ } retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = i; retval->paramtype = var->vartype; retval->paramtypmod = var->vartypmod; return retval;}/* * Generate a Param node to replace the given Aggref * which is expected to have agglevelsup > 0 (ie, it is not local). */static Param *replace_outer_agg(PlannerInfo *root, Aggref *agg){ Param *retval; PlannerParamItem *pitem; Index abslevel; int i; Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level); abslevel = root->query_level - agg->agglevelsup; /* * It does not seem worthwhile to try to match duplicate outer aggs. Just * make a new slot every time. */ agg = (Aggref *) copyObject(agg); IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0); Assert(agg->agglevelsup == 0); pitem = makeNode(PlannerParamItem); pitem->item = (Node *) agg; pitem->abslevel = abslevel; root->glob->paramlist = lappend(root->glob->paramlist, pitem); i = list_length(root->glob->paramlist) - 1; retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = i; retval->paramtype = agg->aggtype; retval->paramtypmod = -1; return retval;}/* * Generate a new Param node that will not conflict with any other. * * This is used to allocate PARAM_EXEC slots for subplan outputs. */static Param *generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod){ Param *retval; PlannerParamItem *pitem; retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = list_length(root->glob->paramlist); retval->paramtype = paramtype; retval->paramtypmod = paramtypmod; pitem = makeNode(PlannerParamItem); pitem->item = (Node *) retval; pitem->abslevel = root->query_level; root->glob->paramlist = lappend(root->glob->paramlist, pitem); return retval;}/* * Get the datatype of the first column of the plan's output. * * This is stored for ARRAY_SUBLINK and for exprType(), which doesn't have any * way to get at the plan associated with a SubPlan node. We really only need * the value for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency * we set it always. */static Oidget_first_col_type(Plan *plan){ TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist); Assert(IsA(tent, TargetEntry)); Assert(!tent->resjunk); return exprType((Node *) tent->expr);}/* * Convert a SubLink (as created by the parser) into a SubPlan. * * We are given the original SubLink and the already-processed testexpr * (use this instead of the SubLink's own field). We are also told if * this expression appears at top level of a WHERE/HAVING qual. * * The result is whatever we need to substitute in place of the SubLink * node in the executable expression. This will be either the SubPlan * node (if we have to do the subplan as a subplan), or a Param node * representing the result of an InitPlan, or a row comparison expression * tree containing InitPlan Param nodes. */static Node *make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual){ Query *subquery = (Query *) (slink->subselect); double tuple_fraction; SubPlan *splan; Plan *plan; PlannerInfo *subroot; bool isInitPlan; Bitmapset *tmpset; int paramid; Node *result; /* * Copy the source Query node. This is a quick and dirty kluge to resolve * the fact that the parser can generate trees with multiple links to the * same sub-Query node, but the planner wants to scribble on the Query. * Try to clean this up when we do querytree redesign... */ subquery = (Query *) copyObject(subquery); /* * For an EXISTS subplan, tell lower-level planner to expect that only the * first tuple will be retrieved. For ALL and ANY subplans, we will be * able to stop evaluating if the test condition fails, so very often not * all the tuples will be retrieved; for lack of a better idea, specify * 50% retrieval. For EXPR and ROWCOMPARE subplans, use default behavior * (we're only expecting one row out, anyway). * * NOTE: if you change these numbers, also change cost_qual_eval_walker() * and get_initplan_cost() in path/costsize.c. * * XXX If an ALL/ANY subplan is uncorrelated, we may decide to hash or * materialize its result below. In that case it would've been better to * specify full retrieval. At present, however, we can only detect * correlation or lack of it after we've made the subplan :-(. Perhaps * detection of correlation should be done as a separate step. Meanwhile, * we don't want to be too optimistic about the percentage of tuples * retrieved, for fear of selecting a plan that's bad for the * materialization case. */ if (slink->subLinkType == EXISTS_SUBLINK) tuple_fraction = 1.0; /* just like a LIMIT 1 */ else if (slink->subLinkType == ALL_SUBLINK || slink->subLinkType == ANY_SUBLINK) tuple_fraction = 0.5; /* 50% */ else tuple_fraction = 0.0; /* default behavior */ /* * Generate the plan for the subquery. */ plan = subquery_planner(root->glob, subquery, root->query_level + 1, tuple_fraction, &subroot); /* * Initialize the SubPlan node. Note plan_id isn't set yet. */ splan = makeNode(SubPlan); splan->subLinkType = slink->subLinkType; splan->testexpr = NULL; splan->paramIds = NIL; splan->firstColType = get_first_col_type(plan); splan->useHashTable = false; /* At top level of a qual, can treat UNKNOWN the same as FALSE */ splan->unknownEqFalse = isTopQual; splan->setParam = NIL; splan->parParam = NIL; splan->args = NIL; /* * Make parParam list of params that current query level will pass to this * child plan. */ tmpset = bms_copy(plan->extParam); while ((paramid = bms_first_member(tmpset)) >= 0) { PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid); if (pitem->abslevel == root->query_level) splan->parParam = lappend_int(splan->parParam, paramid); } bms_free(tmpset); /* * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or * ROWCOMPARE types can be used as initPlans. For EXISTS, EXPR, or ARRAY, * we just produce a Param referring to the result of evaluating the * initPlan. For ROWCOMPARE, we must modify the testexpr tree to contain * PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the * parser. */ if (splan->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK) { Param *prm; prm = generate_new_param(root, BOOLOID, -1); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; } else if (splan->parParam == NIL && slink->subLinkType == EXPR_SUBLINK) { TargetEntry *te = linitial(plan->targetlist); Param *prm; Assert(!te->resjunk); prm = generate_new_param(root, exprType((Node *) te->expr), exprTypmod((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; } else if (splan->parParam == NIL && slink->subLinkType == ARRAY_SUBLINK) { TargetEntry *te = linitial(plan->targetlist); Oid arraytype; Param *prm; Assert(!te->resjunk); arraytype = get_array_type(exprType((Node *) te->expr)); if (!OidIsValid(arraytype)) elog(ERROR, "could not find array type for datatype %s", format_type_be(exprType((Node *) te->expr))); prm = generate_new_param(root, arraytype, exprTypmod((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; } else if (splan->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK) { /* Adjust the Params */ List *params; params = generate_subquery_params(root, plan->targetlist, &splan->paramIds); result = convert_testexpr(root, testexpr, params); splan->setParam = list_copy(splan->paramIds); isInitPlan = true; /* * The executable expression is returned to become part of the outer * plan's expression tree; it is not kept in the initplan node. */ } else { List *args; ListCell *l; if (testexpr) { List *params; /* Adjust the Params in the testexpr */ params = generate_subquery_params(root, plan->targetlist, &splan->paramIds); splan->testexpr = convert_testexpr(root, testexpr, params); } /* * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to * initPlans, even when they are uncorrelated or undirect correlated, * because we need to scan the output of the subplan for each outer * tuple. But if it's an IN (= ANY) test, we might be able to use a * hashtable to avoid comparing all the tuples. */ if (subplan_is_hashable(slink, splan, plan)) splan->useHashTable = true; /* * Otherwise, we have the option to tack a MATERIAL node onto the top * of the subplan, to reduce the cost of reading it repeatedly. This * is pointless for a direct-correlated subplan, since we'd have to * recompute its results each time anyway. For uncorrelated/undirect * correlated subplans, we add MATERIAL unless the subplan's top plan * node would materialize its output anyway. */ else if (splan->parParam == NIL) { bool use_material; switch (nodeTag(plan)) { case T_Material: case T_FunctionScan: case T_Sort: use_material = false; break; default: use_material = true; break; } if (use_material) plan = materialize_finished_plan(plan); } /* * Make splan->args from parParam. */ args = NIL; foreach(l, splan->parParam) { PlannerParamItem *pitem = list_nth(root->glob->paramlist, lfirst_int(l)); /* * The Var or Aggref has already been adjusted to have the correct * varlevelsup or agglevelsup. We probably don't even need to * copy it again, but be safe. */ args = lappend(args, copyObject(pitem->item)); } splan->args = args; result = (Node *) splan; isInitPlan = false; } /* * Add the subplan and its rtable to the global lists. */ root->glob->subplans = lappend(root->glob->subplans, plan); root->glob->subrtables = lappend(root->glob->subrtables, subroot->parse->rtable); splan->plan_id = list_length(root->glob->subplans); if (isInitPlan) root->init_plans = lappend(root->init_plans, splan); /* * A parameterless subplan (not initplan) should be prepared to handle * REWIND efficiently. If it has direct parameters then there's no point * since it'll be reset on each scan anyway; and if it's an initplan then * there's no point since it won't get re-run without parameter changes * anyway. The input of a hashed subplan doesn't need REWIND either. */ if (splan->parParam == NIL && !isInitPlan && !splan->useHashTable) root->glob->rewindPlanIDs = bms_add_member(root->glob->rewindPlanIDs,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?