PostgreSQL查询语句分析

网友投稿 486 2024-01-03

PostgreSQL查询语句分析

这篇文章主要介绍“PostgreSQL查询语句分析”,在日常操作中,相信很多人在PostgreSQL查询语句分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL查询语句分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

PostgreSQL查询语句分析

子查询上拉在函数pull_up_subqueries中实现,该函数调用pull_up_subqueries_recurse函数递归实现子查询上拉.

pull_up_subqueries/*   * pull_up_subqueries   *      Look for subqueries in the rangetable that can be pulled up into   *      the parent query.  If the subquery has no special features like   *      grouping/aggregation then we can merge it into the parents jointree.   *      Also, subqueries that are simple UNION ALL structures can be   *      converted into "append relations".   */void  pull_up_subqueries(PlannerInfo *root)  {/* Top level of jointree must always be a FromExpr */      Assert(IsA(root->parse->jointree, FromExpr));      /* Reset flag saying we need a deletion cleanup pass */      root->hasDeletedRTEs = false;      /* Recursion starts with no containing join nor appendrel */root->parse->jointree = (FromExpr *)          pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,NULLNULLNULLfalse);      /* Apply cleanup phase if necessary */      if(root->hasDeletedRTEs)          root->parse->jointree = (FromExpr *)              pull_up_subqueries_cleanup((Node *) root->parse->jointree);      Assert(IsA(root->parse->jointree, FromExpr));  }

pull_up_subqueries_recurse

   /*   * pull_up_subqueries_recurse   *      Recursive guts of pull_up_subqueries.   *   * This recursively processes the jointree and returns a modified jointree.   * Or, if its valid to drop the current node from the jointree completely,   * it returns NULL.   *   * If this jointree node is within either side of an outer join, then   * lowest_outer_join references the lowest such JoinExpr node; otherwise   * it is NULL.  We use this to constrain the effects of LATERAL subqueries.   *   * If this jointree node is within the nullable side of an outer join, then   * lowest_nulling_outer_join references the lowest such JoinExpr node;   * otherwise it is NULL.  This forces use of the PlaceHolderVar mechanism for   * references to non-nullable targetlist items, but only for references above   * that join.   *   * If we are looking at a member subquery of an append relation,   * containing_appendrel describes that relation; else it is NULL.   * This forces use of the PlaceHolderVar mechanism for all non-Var targetlist   * items, and puts some additional restrictions on what can be pulled up.   *   * deletion_ok is true if the caller can cope with us returning NULL for a   * deletable leaf node (for example, a VALUES RTE that could be pulled up).   * If its false, well avoid pullup in such cases.   *   * A tricky aspect of this code is that if we pull up a subquery we have   * to replace Vars that reference the subquerys outputs throughout the   * parent query, including quals attached to jointree nodes above the one   * we are currently processing!  We handle this by being careful not to   * change the jointree structure while recursing: no nodes other than leaf   * RangeTblRef entries and entirely-empty FromExprs will be replaced or   * deleted.  Also, we cant turn pullup_replace_vars loose on the whole   * jointree, because itll return a mutated copy of the tree; we have to   * invoke it just on the quals, instead.  This behavior is what makes it   * reasonable to pass lowest_outer_join and lowest_nulling_outer_join as   * pointers rather than some more-indirect way of identifying the lowest   * OJs.  Likewise, we dont replace append_rel_list members but only their   * substructure, so the containing_appendrel reference is safe to use.   *   * Because of the rule that no jointree nodes with substructure can be   * replaced, we cannot fully handle the case of deleting nodes from the tree:   * when we delete one child of a JoinExpr, we need to replace the JoinExpr   * with a FromExpr, and that cant happen here.  Instead, we set the   * root->hasDeletedRTEs flag, which tells pull_up_subqueries() that an   * additional pass over the tree is needed to clean up.   */  /*  输入参数:     root-计划器相关信息     jtnode-需要处理的Node(jointree)     lowest_outer_join-如该节点位于外连接的任意一侧,则该指针指向此节点     lowest_nulling_outer_join-如该节点位于外连接的可空一侧,,则该指针指向此节点     containing_appendrel-Append操作中的Relation     deletion_ok-调用方可处理在可删除的叶子节点的情况下返回NULL,此值为true  输出参数:  */  staticNode *  pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode,                             JoinExpr *lowest_outer_join,                             JoinExpr *lowest_nulling_outer_join,                             AppendRelInfo *containing_appendrel,                             bool deletion_ok)  {      Assert(jtnode !=NULL);      if (IsA(jtnode, RangeTblRef))//如为RTR      {          //获取该RTR相应的RTEint         varno = ((RangeTblRef *) jtnode)->rtindex;          RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);/*           * Is this a subquery RTE, and if so, is the subquery simple enough to           * pull up?           *           * If we are looking at an append-relation member, we cant pull it up           * unless is_safe_append_member says so.           */          if(rte->rtekind == RTE_SUBQUERY &&              is_simple_subquery(rte->subquery, rte,                                 lowest_outer_join, deletion_ok) &&              (containing_appendrel ==NULL||               is_safe_append_member(rte->subquery)))//简单子查询              returnpull_up_simple_subquery(root, jtnode, rte,                                             lowest_outer_join,                                             lowest_nulling_outer_join,                                             containing_appendrel,                                             deletion_ok);/*           * Alternatively, is it a simple UNION ALL subquery?  If so, flatten           * into an "append relation".           *           * Its safe to do this regardless of whether this query is itself an           * appendrel member.  (If youre thinking we should try to flatten the           * two levels of appendrel together, youre right; but we handle that           * in set_append_rel_pathlist, not here.)           */          if(rte->rtekind == RTE_SUBQUERY &&              is_simple_union_all(rte->subquery))//UNION ALL子查询              return pull_up_simple_union_all(root, jtnode, rte);            /*           * Or perhaps its a simple VALUES RTE?           *           * We dont allow VALUES pullup below an outer join nor into an           * appendrel (such cases are impossible anyway at the moment).           */          if(rte->rtekind == RTE_VALUES &&              lowest_outer_join ==NULL &&              containing_appendrel == NULL&&              is_simple_values(root, rte, deletion_ok))//VALUES子查询              returnpull_up_simple_values(root, jtnode, rte);/* Otherwise, do nothing at this node. */      }      else if (IsA(jtnode, FromExpr))//如为FromExpr{          FromExpr   *f = (FromExpr *) jtnode;          bool        have_undeleted_child =false;          ListCell   *l;            Assert(containing_appendrel ==NULL);            /*           * If the FromExpr has quals, its not deletable even if its parent           * would allow deletion.           */          if(f->quals)              deletion_ok =false;            foreach(l, f->fromlist)          {              /*               * In a non-deletable FromExpr, we can allow deletion of child               * nodes so long as at least one child remains; so its okay               * either if any previous child survives, or if theres more to               * come.  If all children are deletable in themselves, well force               * the last one to remain unflattened.               *               * As a separate matter, we can allow deletion of all children of               * the top-level FromExpr in a query, since thats a special case               * anyway.               */bool        sub_deletion_ok = (deletion_ok ||                                             have_undeleted_child ||                                             lnext(l) !=NULL||                                             f == root->parse->jointree);                lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l),                                                     lowest_outer_join,                                                     lowest_nulling_outer_join,NULL,                                                     sub_deletion_ok);//递归调用              if (lfirst(l) != NULL)                  have_undeleted_child =true;          }            if(deletion_ok && !have_undeleted_child)          {/* OK to delete this FromExpr entirely */              root->hasDeletedRTEs = true;    /* probably is set already */              return NULL;          }      }      else if (IsA(jtnode, JoinExpr))//如为JoinExpr{          JoinExpr   *j = (JoinExpr *) jtnode;            Assert(containing_appendrel ==NULL);          /* Recurse, being careful to tell myself when inside outer join */          switch(j->jointype)          {case JOIN_INNER:                    /*                   * INNER JOIN can allow deletion of either child node, but not                   * both.  So right child gets permission to delete only if                   * left child didnt get removed.                   */j->larg = pull_up_subqueries_recurse(root, j->larg,                                                       lowest_outer_join,                                                       lowest_nulling_outer_join,NULL,                                                       true);                  j->rarg = pull_up_subqueries_recurse(root, j->rarg,                                                       lowest_outer_join,                                                       lowest_nulling_outer_join,NULL,                                                       j->larg != NULL);                  break;              caseJOIN_LEFT:case JOIN_SEMI:              caseJOIN_ANTI:                  j->larg = pull_up_subqueries_recurse(root, j->larg,                                                       j,                                                       lowest_nulling_outer_join,NULL,                                                       false);                  j->rarg = pull_up_subqueries_recurse(root, j->rarg,                                                       j,                                                       j,NULL,                                                       false);                  break;              caseJOIN_FULL:                  j->larg = pull_up_subqueries_recurse(root, j->larg,                                                       j,                                                       j,NULL,                                                       false);                  j->rarg = pull_up_subqueries_recurse(root, j->rarg,                                                       j,                                                       j,NULL,                                                       false);                  break;              caseJOIN_RIGHT:                  j->larg = pull_up_subqueries_recurse(root, j->larg,                                                       j,                                                       j,NULL,                                                       false);                  j->rarg = pull_up_subqueries_recurse(root, j->rarg,                                                       j,                                                       lowest_nulling_outer_join,NULL,                                                       false);                  break;              default:                  elog(ERROR, "unrecognized join type: %d",                       (int) j->jointype);                  break;          }      }else          elog(ERROR, "unrecognized node type: %d",               (int) nodeTag(jtnode));      returnjtnode;  }/*   * pull_up_simple_subquery   *      Attempt to pull up a single simple subquery.   *   * jtnode is a RangeTblRef that has been tentatively identified as a simple   * subquery by pull_up_subqueries.  We return the replacement jointree node,   * or NULL if the subquery can be deleted entirely, or jtnode itself if we   * determine that the subquery cant be pulled up after all.   *   * rte is the RangeTblEntry referenced by jtnode.  Remaining parameters are   * as for pull_up_subqueries_recurse.   */  staticNode *  pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,                          JoinExpr *lowest_outer_join,                          JoinExpr *lowest_nulling_outer_join,                          AppendRelInfo *containing_appendrel,                          bool deletion_ok)  {      Query      *parse = root->parse;//查询树int         varno = ((RangeTblRef *) jtnode)->rtindex;//RTR中的index,指向rtable中的位置      Query      *subquery;//子查询      PlannerInfo *subroot;//子rootint         rtoffset;//rtable中的偏移      pullup_replace_vars_context rvcontext;//上下文      ListCell   *lc;//临时变量        /*       * Need a modifiable copy of the subquery to hack on.  Even if we didnt       * sometimes choose not to pull up below, we must do this to avoid       * problems if the same subquery is referenced from multiple jointree       * items (which cant happen normally, but might after rule rewriting).       */subquery = copyObject(rte->subquery);//子查询        /*       * Create a PlannerInfo data structure for this subquery.       *       *NOTE:the next few steps should match the first processing in       * subquery_planner().  Can we refactor to avoid code duplication, or       * would that just make things uglier?       */      //为子查询构建PlannerInfo,尝试对此子查询进行上拉subroot = makeNode(PlannerInfo);      subroot->parse = subquery;      subroot->glob = root->glob;      subroot->query_level = root->query_level;      subroot->parent_root = root->parent_root;      subroot->plan_params = NIL;      subroot->outer_params =NULL;      subroot->planner_cxt = CurrentMemoryContext;      subroot->init_plans = NIL;      subroot->cte_plan_ids = NIL;      subroot->multiexpr_params = NIL;      subroot->eq_classes = NIL;      subroot->append_rel_list = NIL;      subroot->rowMarks = NIL;      memset(subroot->upper_rels,0, sizeof(subroot->upper_rels));      memset(subroot->upper_targets,0, sizeof(subroot->upper_targets));      subroot->processed_tlist = NIL;      subroot->grouping_map =NULL;      subroot->minmax_aggs = NIL;      subroot->qual_security_level =0;      subroot->inhTargetKind = INHKIND_NONE;      subroot->hasRecursion =false;      subroot->wt_param_id = -1;      subroot->non_recursive_path = NULL;        /* No CTEs to worry about */      Assert(subquery->cteList == NIL);        /*       * Pull up any SubLinks within the subquerys quals, so that we dont       * leave unoptimized SubLinks behind.       */      if (subquery->hasSubLinks)//子链接?上拉子链接          pull_up_sublinks(subroot);        /*       * Similarly, inline any set-returning functions in its rangetable.       */inline_set_returning_functions(subroot);/*       * Recursively pull up the subquerys subqueries, so that       * pull_up_subqueries processing is complete for its jointree and       * rangetable.       *       *Note:its okay that the subquerys recursion starts with NULL for       * containing-join info, even if we are within an outer join in the upper       * query; the lower query starts with a clean slate for outer-join       * semantics.  Likewise, we neednt pass down appendrel state.       */      pull_up_subqueries(subroot);//递归上拉子查询中的子查询        /*       * Now we must recheck whether the subquery is still simple enough to pull       * up.  If not, abandon processing it.       *       * We dont really need to recheck all the conditions involved, but its       * easier just to keep this "if" looking the same as the one in       * pull_up_subqueries_recurse.       */      //子查询中子链接&子查询上拉后,再次检查,确保本次上拉没有问题      if(is_simple_subquery(subquery, rte,                             lowest_outer_join, deletion_ok) &&          (containing_appendrel ==NULL || is_safe_append_member(subquery)))      {          /* good to go */      }      else      {          /*           * Give up, return unmodified RangeTblRef.           *           *Note:The work we just did will be redone when the subquery gets           * planned on its own.  Perhaps we could avoid that by storing the           * modified subquery back into the rangetable, but Im not gonna risk           * it now.           */          return jtnode;      }        /*       * We must flatten any join alias Vars in the subquerys targetlist,       * because pulling up the subquerys subqueries might have changed their       * expansions into arbitrary expressions, which could affect       * pullup_replace_vars decisions about whether PlaceHolderVar wrappers       * are needed for tlist entries.  (Likely itd be better to do       * flatten_join_alias_vars on the whole query tree at some earlier stage,       * maybe even in the rewriter; but for now lets just fix this case here.)       */      //子查询中的targetList扁平化处理      subquery->targetList = (List*)          flatten_join_alias_vars(subroot, (Node *) subquery->targetList);/*       * Adjust level-0 varnos in subquery so that we can append its rangetable       * to upper querys.  We have to fix the subquerys append_rel_list as       * well.       */      //调整Var.varnortoffset = list_length(parse->rtable);      OffsetVarNodes((Node *) subquery, rtoffset,0);      OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset,0);        /*       * Upper-level vars in subquery are now one level closer to their parent       * than before.       */      //调整Var.varlevelsupIncrementVarSublevelsUp((Node *) subquery,-11);      IncrementVarSublevelsUp((Node *) subroot->append_rel_list,-11);        /*       * The subquerys targetlist items are now in the appropriate form to       * insert into the top query, except that we may need to wrap them in       * PlaceHolderVars.  Set up required context data for pullup_replace_vars.       */rvcontext.root = root;      rvcontext.targetlist = subquery->targetList;      rvcontext.target_rte = rte;if(rte->lateral)          rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree,true);      else                        /* wont need relids */          rvcontext.relids = NULL;      rvcontext.outer_hasSubLinks = &parse->hasSubLinks;      rvcontext.varno = varno;/* these flags will be set below, if needed */      rvcontext.need_phvs = false;      rvcontext.wrap_non_vars = false;      /* initialize cache array with indexes 0 .. length(tlist) */rvcontext.rv_cache = palloc0((list_length(subquery->targetList) +1) *                                   sizeof(Node *));        /*       * If we are under an outer join then non-nullable items and lateral       * references may have to be turned into PlaceHolderVars.       */      if(lowest_nulling_outer_join !=NULL)          rvcontext.need_phvs = true;        /*       * If we are dealing with an appendrel member then anything thats not a       * simple Var has to be turned into a PlaceHolderVar.  We force this to       * ensure that what we pull up doesnt get merged into a surrounding       * expression during later processing and then fail to match the       * expression actually available from the appendrel.       */      if(containing_appendrel !=NULL)      {          rvcontext.need_phvs = true;          rvcontext.wrap_non_vars =true;      }        /*       * If the parent query uses grouping sets, we need a PlaceHolderVar for       * anything thats not a simple Var.  Again, this ensures that expressions       * retain their separate identity so that they will match grouping set       * columns when appropriate.  (Itd be sufficient to wrap values used in       * grouping set columns, and do so only in non-aggregated portions of the       * tlist and havingQual, but that would require a lot of infrastructure       * that pullup_replace_vars hasnt currently got.)       */      if(parse->groupingSets)      {          rvcontext.need_phvs =true;          rvcontext.wrap_non_vars = true;      }        /*       * Replace all of the top querys references to the subquerys outputs       * with copies of the adjusted subtlist items, being careful not to       * replace any of the jointree structure. (Thisd be a lot cleaner if we       * could use query_tree_mutator.)  We have to use PHVs in the targetList,       * returningList, and havingQual, since those are certainly above any       * outer join.  replace_vars_in_jointree tracks its location in the       * jointree and uses PHVs or not appropriately.       */      //处理投影      parse->targetList = (List*)          pullup_replace_vars((Node *) parse->targetList, &rvcontext);      parse->returningList = (List*)          pullup_replace_vars((Node *) parse->returningList, &rvcontext);if(parse->onConflict)      {          parse->onConflict->onConflictSet = (List*)              pullup_replace_vars((Node *) parse->onConflict->onConflictSet,                                  &rvcontext);          parse->onConflict->onConflictWhere =              pullup_replace_vars(parse->onConflict->onConflictWhere,                                  &rvcontext);/*           * We assume ON CONFLICTs arbiterElems, arbiterWhere, exclRelTlist           * cant contain any references to a subquery           */}      replace_vars_in_jointree((Node *) parse->jointree, &rvcontext,                               lowest_nulling_outer_join);      Assert(parse->setOperations ==NULL);      parse->havingQual = pullup_replace_vars(parse->havingQual, &rvcontext);/*       * Replace references in the translated_vars lists of appendrels. When       * pulling up an appendrel member, we do not need PHVs in the list of the       * parent appendrel --- there isnt any outer join between. Elsewhere, use       * PHVs for safety.  (This analysis could be made tighter but it seems       * unlikely to be worth much trouble.)       */      //处理appendrels中的信息      foreach(lc, root->append_rel_list)      {          AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);          bool        save_need_phvs = rvcontext.need_phvs;if(appinfo == containing_appendrel)              rvcontext.need_phvs =false;          appinfo->translated_vars = (List*)              pullup_replace_vars((Node *) appinfo->translated_vars, &rvcontext);          rvcontext.need_phvs = save_need_phvs;      }/*       * Replace references in the joinaliasvars lists of join RTEs.       *       * You might think that we could avoid using PHVs for alias vars of joins       * below lowest_nulling_outer_join, but that doesnt work because the       * alias vars could be referenced above that join; we need the PHVs to be       * present in such references after the alias vars get flattened.  (It       * might be worth trying to be smarter here, someday.)       */      //处理RTE中类型为RTE_JOIN的节点      foreach(lc, parse->rtable)      {          RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(lc);if(otherrte->rtekind == RTE_JOIN)              otherrte->joinaliasvars = (List*)                  pullup_replace_vars((Node *) otherrte->joinaliasvars,                                      &rvcontext);      }/*       * If the subquery had a LATERAL marker, propagate that to any of its       * child RTEs that could possibly now contain lateral cross-references.       * The children might or might not contain any actual lateral       * cross-references, but we have to mark the pulled-up child RTEs so that       * later planner stages will check for such.       */      //LATERAL支持      if (rte->lateral)      {          foreach(lc, subquery->rtable)          {              RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(lc);switch (child_rte->rtekind)              {                  case RTE_RELATION:                      if(child_rte->tablesample)                          child_rte->lateral =true;                      break;                  case RTE_SUBQUERY:                  case RTE_FUNCTION:                  case RTE_VALUES:                  case RTE_TABLEFUNC:                      child_rte->lateral = true;                      break;                  caseRTE_JOIN:case RTE_CTE:                  case RTE_NAMEDTUPLESTORE:                      /* these cant contain any lateral references */                      break;              }          }      }/*       * Now append the adjusted rtable entries to upper query. (We hold off       * until after fixing the upper rtable entries; no point in running that       * code on the subquery ones too.)       */      //子查询中的RTE填充至父查询中      parse->rtable = list_concat(parse->rtable, subquery->rtable);        /*       * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes already       * adjusted the marker rtindexes, so just concat the lists.)       */parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);/*       * We also have to fix the relid sets of any PlaceHolderVar nodes in the       * parent query.  (This could perhaps be done by pullup_replace_vars(),       * but it seems cleaner to use two passes.)  Note in particular that any       * PlaceHolderVar nodes just created by pullup_replace_vars() will be       * adjusted, so having created them with the subquerys varno is correct.       *       * Likewise, relids appearing in AppendRelInfo nodes have to be fixed. We       * already checked that this wont require introducing multiple subrelids       * into the single-slot AppendRelInfo structs.       */      if(parse->hasSubLinks || root->glob->lastPHId !=0||          root->append_rel_list)      {          Relids      subrelids;            subrelids = get_relids_in_jointree((Node *) subquery->jointree,false);          substitute_multiple_relids((Node *) parse, varno, subrelids);          fix_append_rel_relids(root->append_rel_list, varno, subrelids);      }/*       * And now add subquerys AppendRelInfos to our list.       */root->append_rel_list = list_concat(root->append_rel_list,                                          subroot->append_rel_list);/*       * We dont have to do the equivalent bookkeeping for outer-join info,       * because that hasnt been set up yet.  placeholder_list likewise.       */Assert(root->join_info_list == NIL);      Assert(subroot->join_info_list == NIL);      Assert(root->placeholder_list == NIL);      Assert(subroot->placeholder_list == NIL);/*       * Miscellaneous housekeeping.       *       * Although replace_rte_variables() faithfully updated parse->hasSubLinks       * if it copied any SubLinks out of the subquerys targetlist, we still       * could have SubLinks added to the query in the expressions of FUNCTION       * and VALUES RTEs copied up from the subquery.  So its necessary to copy       * subquery->hasSubLinks anyway.  Perhaps this can be improved someday.       */      parse->hasSubLinks |= subquery->hasSubLinks;        /* If subquery had any RLS conditions, now main query does too */parse->hasRowSecurity |= subquery->hasRowSecurity;/*       * subquery wont be pulled up if it hasAggs, hasWindowFuncs, or       * hasTargetSRFs, so no work needed on those flags       */        /*       * Return the adjusted subquery jointree to replace the RangeTblRef entry       * in parents jointree; or, if were flattening a subquery with empty       * FROM list, return NULL to signal deletion of the subquery from the       * parent jointree (and set hasDeletedRTEs to ensure cleanup later).       */      if(subquery->jointree->fromlist == NIL)      {          Assert(deletion_ok);          Assert(subquery->jointree->quals ==NULL);          root->hasDeletedRTEs = true;          return NULL;      }        return(Node *) subquery->jointree;  }

is_simple_subquery

 /*   * is_simple_subquery   *    Check a subquery in the range table to see if its simple enough   *    to pull up into the parent query.   *   * rte is the RTE_SUBQUERY RangeTblEntry that contained the subquery.   * (Note subquery is not necessarily equal to rte->subquery; it could be a   * processed copy of that.)   * lowest_outer_join is the lowest outer join above the subquery, or NULL.   * deletion_ok is true if itd be okay to delete the subquery entirely.   */  staticbool  is_simple_subquery(Query *subquery, RangeTblEntry *rte,                     JoinExpr *lowest_outer_join,                     bool deletion_ok)  {/*       * Lets just make sure its a valid subselect ...       */      if(!IsA(subquery, Query) ||          subquery->commandType != CMD_SELECT)          elog(ERROR,"subquery is bogus");        /*       * Cant currently pull up a query with setops (unless its simple UNION       * ALL, which is handled by a different code path). Maybe after querytree       * redesign...       */      if (subquery->setOperations)          return false;//存在集合操作        /*       * Cant pull up a subquery involving grouping, aggregation, SRFs,       * sorting, limiting, or WITH.  (XXX WITH could possibly be allowed later)       *       * We also dont pull up a subquery that has explicit FOR UPDATE/SHARE       * clauses, because pullup would cause the locking to occur semantically       * higher than it should.  Implicit FOR UPDATE/SHARE is okay because in       * that case the locking was originally declared in the upper query       * anyway.       */      if(subquery->hasAggs ||          subquery->hasWindowFuncs ||          subquery->hasTargetSRFs ||          subquery->groupClause ||          subquery->groupingSets ||          subquery->havingQual ||          subquery->sortClause ||          subquery->distinctClause ||          subquery->limitOffset ||          subquery->limitCount ||          subquery->hasForUpdate ||          subquery->cteList)return false;//存在聚合函数/窗口函数...        /*       * Dont pull up if the RTE represents a security-barrier view; we       * couldnt prevent information leakage once the RTEs Vars are scattered       * about in the upper query.       */      if (rte->security_barrier)          return false;//        /*       * Dont pull up a subquery with an empty jointree, unless it has no quals       * and deletion_ok is true and were not underneath an outer join.       *       * query_planner() will correctly generate a Result plan for a jointree       * thats totally empty, but we cant cope with an empty FromExpr       * appearing lower down in a jointree: we identify join rels via baserelid       * sets, so we couldnt distinguish a join containing such a FromExpr from       * one without it.  We can only handle such cases if the place where the       * subquery is linked is a FromExpr or inner JOIN that would still be       * nonempty after removal of the subquery, so that its still identifiable       * via its contained baserelids.  Safe contexts are signaled by       * deletion_ok.       *       * But even in a safe context, we must keep the subquery if it has any       * quals, because its unclear where to put them in the upper query.       *       * Also, we must forbid pullup if such a subquery is underneath an outer       * join, because then we might need to wrap its output columns with       * PlaceHolderVars, and the PHVs would then have empty relid sets meaning       * we couldnt tell where to evaluate them.  (This test is separate from       * the deletion_ok flag for possible future expansion: deletion_ok tells       * whether the immediate parent site in the jointree could cope, not       * whether wed have PHV issues.  Its possible this restriction could be       * fixed by letting the PHVs use the relids of the parent jointree item,       * but that complication is for another day.)       *       * Note that deletion of a subquery is also dependent on the check below       * that its targetlist contains no set-returning functions.  Deletion from       * a FROM list or inner JOIN is okay only if the subquery must return       * exactly one row.       */      if(subquery->jointree->fromlist == NIL &&          (subquery->jointree->quals !=NULL||           !deletion_ok ||           lowest_outer_join !=NULL))          return false;        /*       * If the subquery is LATERAL, check for pullup restrictions from that.       */      if(rte->lateral)      {          bool        restricted;          Relids      safe_upper_varnos;/*           * The subquerys WHERE and JOIN/ON quals mustnt contain any lateral           * references to rels outside a higher outer join (including the case           * where the outer join is within the subquery itself).  In such a           * case, pulling up would result in a situation where we need to           * postpone quals from below an outer join to above it, which is           * probably completely wrong and in any case is a complication that           * doesnt seem worth addressing at the moment.           */          if (lowest_outer_join != NULL)          {              restricted = true;              safe_upper_varnos = get_relids_in_jointree((Node *) lowest_outer_join,true);          }else          {              restricted = false;              safe_upper_varnos =NULL;   /* doesnt matter */          }            if(jointree_contains_lateral_outer_refs((Node *) subquery->jointree,                                                   restricted, safe_upper_varnos))return false;            /*           * If theres an outer join above the LATERAL subquery, also disallow           * pullup if the subquerys targetlist has any references to rels           * outside the outer join, since these might get pulled into quals           * above the subquery (but in or below the outer join) and then lead           * to qual-postponement issues similar to the case checked for above.           * (We wouldnt need to prevent pullup if no such references appear in           * outer-query quals, but we dont have enough info here to check           * that.  Also, maybe this restriction could be removed if we forced           * such refs to be wrapped in PlaceHolderVars, even when theyre below           * the nearest outer join?  But its a pretty hokey usage, so not           * clear this is worth sweating over.)           */          if (lowest_outer_join != NULL)          {              Relids      lvarnos = pull_varnos_of_level((Node *) subquery->targetList,1);                if (!bms_is_subset(lvarnos, safe_upper_varnos))                  return false;          }      }/*       * Dont pull up a subquery that has any volatile functions in its       * targetlist.  Otherwise we might introduce multiple evaluations of these       * functions, if they get copied to multiple places in the upper query,       * leading to surprising results.  (Note:the PlaceHolderVar mechanism       * doesnt quite guarantee single evaluation; else we could pull up anyway       * and just wrap such items in PlaceHolderVars ...)       */      if(contain_volatile_functions((Node *) subquery->targetList))return false;//存在易变函数        return true;  }

pull_up_subqueries_cleanup

   /*   * pull_up_subqueries_cleanup   *      Recursively fix up jointree after deletion of some subqueries.   *   * The jointree now contains some NULL subtrees, which we need to get rid of.   * In a FromExpr, just rebuild the child-node list with null entries deleted.   * In an inner JOIN, replace the JoinExpr node with a one-child FromExpr.   */  staticNode *  pull_up_subqueries_cleanup(Node *jtnode)  {      Assert(jtnode !=NULL);      if (IsA(jtnode, RangeTblRef))      {          /* Nothing to do at leaf nodes. */      }      else if(IsA(jtnode, FromExpr))      {          FromExpr   *f = (FromExpr *) jtnode;List*newfrom = NIL;          ListCell   *l;foreach(l, f->fromlist)          {              Node       *child = (Node *) lfirst(l);if (child == NULL)                  continue;              child = pull_up_subqueries_cleanup(child);              newfrom = lappend(newfrom, child);          }          f->fromlist = newfrom;      }else if(IsA(jtnode, JoinExpr))      {          JoinExpr   *j = (JoinExpr *) jtnode;if(j->larg)              j->larg = pull_up_subqueries_cleanup(j->larg);if(j->rarg)              j->rarg = pull_up_subqueries_cleanup(j->rarg);if (j->larg == NULL)          {              Assert(j->jointype == JOIN_INNER);              Assert(j->rarg !=NULL);              return(Node *) makeFromExpr(list_make1(j->rarg), j->quals);          }else if (j->rarg == NULL)          {              Assert(j->jointype == JOIN_INNER);return(Node *) makeFromExpr(list_make1(j->larg), j->quals);          }      }else          elog(ERROR, "unrecognized node type: %d",               (int) nodeTag(jtnode));return jtnode;  }三、跟踪分析

gdb跟踪分析:

(gdb) b pull_up_subqueries Breakpoint 1 at 0x77d63b: file prepjointree.c, line 612. (gdb) c Continuing. Breakpoint 1, pull_up_subqueries (root=0x1d092d0) at prepjointree.c:612 612     root->hasDeletedRTEs = false; (gdb)  #输入参数,root参见上拉子链接中的说明 #进入pull_up_subqueries_recurse (gdb) step 615         pull_up_subqueries_recurse(root, (Node *) root->parse->jointree, (gdb) step pull_up_subqueries_recurse (root=0x1d092d0, jtnode=0x1d092a0, lowest_outer_join=0x0, lowest_nulling_outer_join=0x0,      containing_appendrel=0x0, deletion_ok=false) at prepjointree.c:680 680     if (IsA(jtnode, RangeTblRef)) (gdb)  #输入参数: #1.root,同pull_up_subqueries #2.jtnode,Query查询树 #3/4/5.lowest_outer_join/lowest_nulling_outer_join/containing_appendrel均为NULL #6.deletion_ok,false ... (gdb) p *jtnode $2 = {type = T_FromExpr} #FromExpr,进入相应的分支 ... #递归调用pull_up_subqueries_recurse (gdb)  763             lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l), (gdb) step pull_up_subqueries_recurse (root=0x1d092d0, jtnode=0x1c73078, lowest_outer_join=0x0, lowest_nulling_outer_join=0x0,      containing_appendrel=0x0, deletion_ok=true) at prepjointree.c:680 680     if (IsA(jtnode, RangeTblRef)) #注意:这时候的jtnode类型为RangeTblRef (gdb) n 682         int         varno = ((RangeTblRef *) jtnode)->rtindex; (gdb)  683         RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable); (gdb)  692         if (rte->rtekind == RTE_SUBQUERY && (gdb) p varno $4 = 1 #rtable中第1个RTE是父查询的Relation(即t_dwxx),不是子查询 (gdb) p *rte $5 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16394, relkind = 114 r, tablesample = 0x0, subquery = 0x0,    security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, funcordinality = false,    tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, self_reference = false, coltypes = 0x0,    coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, enrtuples = 0, alias = 0x1c4fd58, eref = 0x1c72c98,    lateral = false, inh = true, inFromCl = true, requiredPerms = 2, checkAsUser = 0, selectedCols = 0x1d07698,    insertedCols = 0x0, updatedCols = 0x0, securityQuals = 0x0} (gdb) n 712         if (rte->rtekind == RTE_SUBQUERY && (gdb)  722         if (rte->rtekind == RTE_VALUES && (gdb)  852     return jtnode; (gdb)  ... #rtable中的第2个元素,类型为RTE_SUBQUERY (gdb) step pull_up_subqueries_recurse (root=0x1d092d0, jtnode=0x1d07358, lowest_outer_join=0x0, lowest_nulling_outer_join=0x0,      containing_appendrel=0x0, deletion_ok=true) at prepjointree.c:680 680     if (IsA(jtnode, RangeTblRef)) (gdb) n 682         int         varno = ((RangeTblRef *) jtnode)->rtindex; (gdb)  (gdb) p *rte $7 = {type = T_RangeTblEntry, rtekind = RTE_SUBQUERY, relid = 0, relkind = 0 \000, tablesample = 0x0,    subquery = 0x1c72968, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,    funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, self_reference = false,    coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, enrtuples = 0, alias = 0x1c50548, eref = 0x1d071a0,    lateral = false, inh = false, inFromCl = true, requiredPerms = 0, checkAsUser = 0, selectedCols = 0x0,    insertedCols = 0x0, updatedCols = 0x0, securityQuals = 0x0} ... #进入pull_up_simple_subquery 697             return pull_up_simple_subquery(root, jtnode, rte, (gdb) step pull_up_simple_subquery (root=0x1d092d0, jtnode=0x1d07358, rte=0x1c72a78, lowest_outer_join=0x0,      lowest_nulling_outer_join=0x0, containing_appendrel=0x0, deletion_ok=true) at prepjointree.c:874 874     Query      *parse = root->parse; ... 1247        return (Node *) subquery->jointree; (gdb)  1248    } (gdb)  pull_up_subqueries_recurse (root=0x1d09838, jtnode=0x1c736e0, lowest_outer_join=0x0, lowest_nulling_outer_join=0x0,      containing_appendrel=0x0, deletion_ok=true) at prepjointree.c:853 853 } (gdb)

到此,关于“PostgreSQL查询语句分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Oracle如何创建普通用户并赋予权限
下一篇:Oracle set unused怎么用
相关文章

 发表评论

暂时没有评论,来抢沙发吧~