home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / planner / plan / planmain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  19.7 KB  |  705 lines

  1.  
  2. /*     
  3.  *      FILE
  4.  *         planmain
  5.  *     
  6.  *      DESCRIPTION
  7.  *         Routines to plan a single query
  8.  *     
  9.  */
  10. /* RcsId ("$Header: /private/postgres/src/planner/plan/RCS/planmain.c,v 1.39 1992/07/23 15:37:36 joey Exp $"); */
  11.  
  12. /*     
  13.  *      EXPORTS
  14.  *             Plan query_planner();
  15.  */
  16.  
  17.  
  18. #include "nodes/pg_lisp.h"
  19. #include "nodes/plannodes.h"
  20. #include "nodes/plannodes.a.h"
  21. #include "nodes/relation.h"
  22. #include "nodes/relation.a.h"
  23.  
  24. #include "parser/parse.h"
  25.  
  26. #include "planner/internal.h"
  27. #include "planner/allpaths.h"
  28. #include "planner/clause.h"
  29. #include "planner/createplan.h"
  30. #include "planner/keys.h"
  31. #include "planner/planmain.h"
  32. #include "planner/setrefs.h"
  33. #include "planner/sortresult.h"
  34. #include "planner/sortresult.h"
  35. #include "planner/tlist.h"
  36. #include "tcop/dest.h"
  37. #include "utils/log.h"
  38. #include "nodes/mnodes.h"
  39. #include "utils/mcxt.h"
  40.  
  41. extern int testFlag;
  42.  
  43. /*    
  44.  *        query_planner
  45.  *    
  46.  *        Routine to create a query plan.  It does so by first creating a
  47.  *        subplan for the topmost level of attributes in the query.  Then,
  48.  *        it modifies all target list and qualifications to consider the next
  49.  *        level of nesting and creates a plan for this modified query by
  50.  *        recursively calling itself.  The two pieces are then merged together
  51.  *        by creating a result node that indicates which attributes should
  52.  *        be placed where and any relation level qualifications to be
  53.  *        satisfied.
  54.  *    
  55.  *        'command-type' is the query command, e.g., retrieve, delete, etc.
  56.  *        'tlist' is the target list of the query
  57.  *        'qual' is the qualification of the query
  58.  *        'currentlevel' is the current nesting level
  59.  *        'maxlevel' is the maximum nesting level in this query
  60.  *    
  61.  *        Returns a query plan.
  62.  *    
  63.  */
  64.  
  65. /*  .. init-query-planner, query_planner    */
  66.  
  67. Plan
  68. query_planner (command_type,tlist,qual,currentlevel,maxlevel)
  69.      int command_type;
  70.      List tlist,qual;
  71.      int currentlevel;
  72.      int maxlevel ;
  73. {
  74.      LispValue constant_qual = LispNil;
  75.      LispValue sortkeys = LispNil;
  76.      LispValue flattened_tlist = LispNil;
  77.      List     level_tlist = LispNil;
  78.      List    result_of_flatten_tlist = LispNil;
  79.      List    agg_tlist = LispNil;
  80.      List    all_but_agg_tlist = LispNil;
  81.      int    aggidnum = -17; /* okay, a hack on uniquness */
  82.      Plan    thePlan = (Plan)NULL;
  83.      Plan    subplan = (Plan)NULL;
  84.      List    subtlist = LispNil;
  85.      Plan     restplan = (Plan)NULL;
  86.      List    resttlist = LispNil;
  87.      List    relation_level_clauses = LispNil;
  88.      Plan     plan = (Plan)NULL;
  89.  
  90.      /*    For the topmost nesting level, */
  91.      /* 1. Pull out any non-variable qualifications so these can be put in */
  92.      /*    the topmost result node.  The opids for the remaining */
  93.      /*    qualifications will be changed to regprocs later. */
  94.      /*    ------
  95.       *    NOTE: we used to have one field called 'opno' in the Oper
  96.       *    nodes which used to be also be called 'opid' in some comments.
  97.       *    This field was either the pg_operator oid, or the
  98.       *    regproc oid. However now we have two separate
  99.       *    fields, the 'opno' (pg_operator oid) and the 'opid' (pg_proc
  100.       *    oid) so things are a little bit more clear now...   [sp.]
  101.       *    ------
  102.       */
  103.      /* 2. Determine the keys on which the result is to be sorted. */
  104.      /* 3. Create a target list that consists solely of (resdom var) target */
  105.      /*    list entries, i.e., contains no arbitrary expressions. */
  106.      
  107.      if ( currentlevel == 1) {
  108.      /* A command without a target list or qualification is an error, */
  109.      /* except for "delete foo". */
  110.      
  111.      if (null (tlist) && null (qual)) {
  112.          if ( command_type == DELETE ||
  113.          /* Total hack here. I don't know how to handle
  114.         statements like notify in action bodies.
  115.         Notify doesn't return anything but
  116.         scans a system table. */
  117.          command_type == NOTIFY) {
  118.          return ((Plan)make_seqscan(LispNil, LispNil,
  119.                        (Index)CInteger(_query_result_relation_),
  120.                        (Plan)NULL));
  121.          } else
  122.            return((Plan)NULL);
  123.      }
  124.      constant_qual = pull_constant_clauses (qual);
  125.      qual = nset_difference (qual,constant_qual);
  126.      fix_opids (constant_qual);
  127.      sortkeys = relation_sortkeys (tlist);
  128.      /* flatten_tlist will now return (var, aggs, rest) */
  129.  
  130.      result_of_flatten_tlist = flatten_tlist(tlist);
  131.  
  132.      flattened_tlist = CAR(result_of_flatten_tlist);
  133.      agg_tlist = CADR(result_of_flatten_tlist);
  134.  
  135.      result_of_flatten_tlist = CDR(result_of_flatten_tlist);
  136.      all_but_agg_tlist = CADR(result_of_flatten_tlist);
  137.      if (flattened_tlist)
  138.        level_tlist = flattened_tlist;
  139.      else if (tlist)
  140.        level_tlist = all_but_agg_tlist;
  141.        /* orig_tlist minus the aggregates */
  142.      else
  143.        level_tlist = (List)NULL;
  144.      }
  145.  
  146.   if(agg_tlist) {
  147.      if(all_but_agg_tlist) {
  148.     thePlan = query_planner(command_type,
  149.         all_but_agg_tlist, qual, currentlevel, maxlevel);
  150.      }
  151.      else {
  152.      /* all we have are aggregates */
  153.      thePlan = (Plan)make_agg(CAR(agg_tlist), --aggidnum);
  154.      /* also, there should never be a case by now where we neither 
  155.       * have aggs nor all_but_aggs
  156.       */
  157.       agg_tlist = CDR(agg_tlist);
  158.      }
  159.      if(agg_tlist != NULL) {  /* are there any aggs left */
  160.        thePlan = make_aggplan(thePlan, agg_tlist, aggidnum);
  161.       }
  162.       return(thePlan);
  163.    }
  164.  
  165.      /*    A query may have a non-variable target list and a non-variable */
  166.      /*    qualification only under certain conditions: */
  167.      /*    - the query creates all-new tuples, or */
  168.      /*   - the query is a replace (a scan must still be done in this case). */
  169.  
  170.      if ((0 == maxlevel || 
  171.       (1 == currentlevel && null (flattened_tlist))) &&
  172.      null (qual)) {
  173.       switch (command_type) {
  174.          case RETRIEVE : 
  175.          case APPEND :
  176.            return ((Plan)make_result (tlist,
  177.                     LispNil,
  178.                     constant_qual,
  179.                     (Plan)NULL,
  180.                     (Plan)NULL));
  181.            break;
  182.  
  183.          case DELETE :
  184.          case REPLACE : 
  185.            {
  186.             /* XXX - let form, maybe incorrect */
  187.            SeqScan scan = make_seqscan (tlist,
  188.                            LispNil,
  189.                            (Index)CInteger(
  190.                            _query_result_relation_),
  191.                            (Plan)NULL);
  192.            if ( consp (constant_qual) ) {
  193.                return ((Plan)make_result (tlist,
  194.                          LispNil,
  195.                          constant_qual,
  196.                          (Plan)scan,
  197.                          (Plan)NULL));
  198.            } 
  199.            else {
  200.                return ((Plan)scan);
  201.            } 
  202.            }
  203.            break;
  204.            
  205.          default: /* return nil */
  206.            return((Plan)NULL);
  207.       }
  208.      }
  209. /*    Find the subplan (access path) for attributes at this nesting level */
  210. /*    and destructively modify the target list of the newly created */
  211. /*    subplan to contain the appropriate join references. */
  212.       
  213.      subplan = subplanner (level_tlist,
  214.                 tlist,
  215.                 qual,
  216.                 currentlevel,
  217.                 sortkeys);
  218.      
  219.      subtlist = get_qptargetlist (subplan);
  220.      set_tlist_references (subplan);
  221.  
  222. /*    If there are further nesting levels, call the planner again to */
  223. /*    create subplans for lower levels of nesting.  Modify the target */
  224. /*    list and qualifications to reflect the new nesting level. */
  225.  
  226.        if (currentlevel != maxlevel) {
  227.         elog(WARN, "level mismatch -- should never happen with new nested dot parsing scheme!");
  228.        }
  229. /*    Build a result node linking the plan for deeper nesting levels and */
  230. /*    the subplan for this level.  Note that a plan that has no right */
  231. /*    subtree may still have relation level and constant quals, so we */
  232. /*    still have to build an appropriate result node. */
  233.  
  234.      relation_level_clauses = pull_relation_level_clauses (qual);
  235.      
  236.      if ( restplan ||
  237.      relation_level_clauses ||
  238.      constant_qual) {
  239.       List resttlist = LispNil;
  240.       List subtlist = LispNil;
  241.       Plan plan;
  242.       
  243.       if ( restplan ) 
  244.         resttlist = get_qptargetlist (restplan);
  245.       subtlist = get_qptargetlist (subplan);
  246.       
  247.       plan = (Plan)make_result (new_result_tlist (tlist,
  248.                               subtlist,
  249.                               resttlist,
  250.                               currentlevel,
  251.                               valid_sortkeys(sortkeys)),
  252.                    new_result_qual(relation_level_clauses,
  253.                            subtlist,
  254.                            resttlist,
  255.                            currentlevel),
  256.                     constant_qual,
  257.                    subplan,
  258.                    restplan);
  259.       /*
  260.        * Change all varno's of the Result's node target list.
  261.        */
  262.       set_result_tlist_references((Result)plan);
  263.  
  264.       if ( valid_numkeys (sortkeys) ) 
  265.         return (sort_level_result (plan, CInteger(sortkeys)));
  266.       else 
  267.         return (plan);
  268.      }
  269.  
  270.      /*
  271.       * fix up the flattened target list of the plan root node so that
  272.       * expressions are evaluated.  this forces expression evaluations
  273.       * that may involve expensive function calls to be delayed to
  274.       * the very last stage of query execution.  this could be bad.
  275.       * but it is joey's responsibility to optimally push these
  276.       * expressions down the plan tree.  -- Wei
  277.       */
  278.      set_qptargetlist (subplan, flatten_tlist_vars (tlist,
  279.                         get_qptargetlist (subplan)));
  280.      /*
  281.       * Destructively modify the query plan's targetlist to add fjoin
  282.       * lists to flatten functions that return sets of base types
  283.       */
  284.      set_qptargetlist(subplan, generate_fjoin(get_qptargetlist(subplan)));
  285.  
  286.     /*    If a sort is required, explicitly sort this subplan since: */
  287.     /*    there is only one level of attributes in this query, and */
  288.     /*the sort spans across expressions and/or multiple relations and so */
  289.     /*        indices were not considered in planning the sort. */
  290.  
  291.      if ( valid_numkeys (sortkeys) ) 
  292.        return (sort_level_result (subplan,CInteger(sortkeys)));
  293.      else 
  294.        return (subplan);
  295.  
  296. }  /* function end  */
  297.  
  298. /*    
  299.  *        subplanner
  300.  *    
  301.  *        Subplanner creates an entire plan consisting of joins and scans
  302.  *        for processing a single level of attributes.  Nested attributes are 
  303.  *        transparent (i.e., essentially ignored) from here on.
  304.  *    
  305.  *        'flat-tlist' is the flattened target list
  306.  *    --which now is of the form (vars, aggs)--, jc
  307.  *        'original-tlist' is the unflattened target list
  308.  *        'qual' is the qualification to be satisfied
  309.  *        'level' is the current nesting level
  310.  *    
  311.  *        Returns a subplan.
  312.  *    
  313.  */
  314.  
  315. /*  .. query_planner    */
  316.  
  317. Plan
  318. subplanner (flat_tlist,original_tlist,qual,level,sortkeys)
  319.      LispValue flat_tlist,original_tlist,qual,sortkeys ;
  320.      int level;
  321. {
  322.     Rel final_relation;
  323.     List final_relation_list;
  324.  
  325.      /* Initialize the targetlist and qualification, adding entries to */
  326.      /* *query-relation-list* as relation references are found (e.g., in the */
  327.      /*  qualification, the targetlist, etc.) */
  328.  
  329.     _base_relation_list_ = LispNil;
  330.     _join_relation_list_ = LispNil;
  331.     initialize_targetlist (flat_tlist);
  332.     initialize_qualification (qual);
  333.  
  334.     
  335. /* Find all possible scan and join paths. */
  336. /* Mark all the clauses and relations that can be processed using special */
  337. /* join methods, then do the exhaustive path search. */
  338.  
  339.     initialize_join_clause_info (_base_relation_list_);
  340.     final_relation_list = find_paths (_base_relation_list_,
  341.                     level,
  342.                     sortkeys);
  343.     if (final_relation_list)
  344.       final_relation = (Rel)CAR (final_relation_list);
  345.     else
  346.       final_relation = (Rel)LispNil;
  347.      
  348. /*    If we want a sorted result and indices could have been used to do so, */
  349. /*    then explicitly sort those paths that weren't sorted by indices so we */
  350. /*  can determine whether using the implicit sort (index) is better than an */
  351. /*  explicit one. */
  352.  
  353.      if ( valid_sortkeys (sortkeys)) {
  354.      sort_relation_paths (get_pathlist (final_relation),
  355.                    sortkeys,
  356.                   final_relation,
  357.                   compute_targetlist_width(original_tlist));
  358.      }
  359. /*    Determine the cheapest path and create a subplan corresponding to it. */
  360.     
  361.     if (final_relation) {
  362.       if (testFlag) {
  363.       List planlist = LispNil;
  364.       List pathlist = LispNil;
  365.       LispValue x;
  366.       Path path;
  367.       Plan plan;
  368.       Choose chooseplan;
  369.       pathlist = get_pathlist(final_relation);
  370.       foreach (x, pathlist) {
  371.           path = (Path)CAR(x);
  372.           plan = create_plan(path);
  373.           planlist = nappend1(planlist, (LispValue)plan);
  374.         }
  375.       chooseplan = RMakeChoose();
  376.       set_chooseplanlist(chooseplan, planlist);
  377.       set_qptargetlist((Plan)chooseplan, get_qptargetlist(plan));
  378.       return (Plan)chooseplan;
  379.     }
  380.       return (create_plan ((Path)get_cheapestpath (final_relation)));
  381.      }
  382.     else {
  383.     printf(" Warn: final relation is nil \n");
  384.     return(create_plan ((Path)NULL));
  385.     }
  386.     
  387. }  /* function end  */
  388.  
  389. Result
  390. make_result( tlist,resrellevelqual,resconstantqual,left,right)
  391.      List tlist;
  392.      List resrellevelqual,resconstantqual;
  393.      Plan left,right;
  394. {
  395.     Result node = RMakeResult();
  396.     
  397.     tlist = generate_fjoin(tlist);
  398.     set_cost((Plan) node, 0.0);
  399.     set_fragment((Plan) node, 0);
  400.     set_state((Plan) node, NULL);
  401.     set_qptargetlist((Plan)node, tlist);
  402.     set_qpqual((Plan) node, LispNil);
  403.     set_lefttree((Plan)node, (PlanPtr)left);
  404.     set_righttree((Plan)node, (PlanPtr)right);
  405.  
  406.     set_resrellevelqual(node, resrellevelqual); 
  407.     set_resconstantqual(node, resconstantqual); 
  408.     set_resstate(node, NULL);
  409.     
  410.     return(node);
  411.  
  412. /* for modifying in case of aggregates.
  413.  * we know that there are aggregates in the tlist*/
  414. Plan
  415. make_aggplan(subplan, agg_tlist, aggidnum)
  416.     Plan subplan;
  417.     List agg_tlist;
  418.     int aggidnum;
  419. {
  420.      List agg_tl_entry = LispNil;
  421.      List add_to_tl = LispNil;
  422.      NestLoop joinnode = (NestLoop)NULL;
  423.      LispValue entry = LispNil;
  424.      List level_tlist = LispNil;
  425.      Agg aggnode;
  426. /*    extern search_quals(); */
  427.  
  428.     /* we're assuming that subplan is not null from the caller*/
  429.     agg_tl_entry = CAR(agg_tlist);
  430.     aggnode = make_agg(agg_tl_entry, --aggidnum);
  431.  
  432.         set_qptargetlist(subplan, nconc(get_qptargetlist(subplan),
  433.                      get_qptargetlist((Plan)aggnode)));
  434.     level_tlist = get_qptargetlist(subplan);
  435.     joinnode = make_nestloop(level_tlist, LispNil, (Plan)aggnode,
  436.                               subplan);
  437.     /* inner tree is the aggregate, outer tree is the rest of
  438.      * the plan.  quals are nil here since we don't have aggregate
  439.      * functions yet.
  440.      */
  441.  
  442.     if(CDR(agg_tlist)) {  /* is this the last agg_tlist entry? */
  443.        /* if not */
  444.        return make_aggplan((Plan)joinnode, CDR(agg_tlist), aggidnum);
  445.             /* XXX jc.  type problem with joinnode and Plan subplan? */
  446.          }
  447.      else { /* if so */
  448.           return((Plan) joinnode);
  449.      }
  450. }
  451.  
  452.  
  453.     
  454.  
  455. bool
  456. plan_isomorphic(p1, p2)
  457. Plan p1, p2;
  458. {
  459.     if (p1 == NULL) return (p2 == NULL);
  460.     if (p2 == NULL) return (p1 == NULL);
  461.     if (IsA(p1,Join) && IsA(p2,Join)) {
  462.     return (plan_isomorphic(get_lefttree(p1), get_lefttree(p2)) &&
  463.             plan_isomorphic(get_righttree(p1), get_righttree(p2)));
  464.       }
  465.     if (IsA(p1,Scan) && IsA(p2,Scan)) {
  466.     if (get_scanrelid((Scan)p1) == get_scanrelid((Scan)p2))
  467.         if (get_scanrelid((Scan)p1) == _TEMP_RELATION_ID_)
  468.         return plan_isomorphic(get_lefttree(p1), get_lefttree(p2));
  469.         else
  470.         return true;
  471.     else if (get_scanrelid((Scan)p1) == _TEMP_RELATION_ID_)
  472.         return plan_isomorphic(get_lefttree(p1), p2);
  473.     else if (get_scanrelid((Scan)p2) == _TEMP_RELATION_ID_)
  474.         return plan_isomorphic(p1, get_lefttree(p2));
  475.     return false;
  476.       }
  477.     if (IsA(p1,Temp) || IsA(p1,Hash) ||
  478.     (IsA(p1,Scan) && get_scanrelid((Scan)p1) == _TEMP_RELATION_ID_))
  479.     return plan_isomorphic(get_lefttree(p1), p2);
  480.     if (IsA(p2,Temp) || IsA(p2,Hash) ||
  481.     (IsA(p2,Scan) && get_scanrelid((Scan)p2) == _TEMP_RELATION_ID_))
  482.     return plan_isomorphic(p1, get_lefttree(p2));
  483.     return false;
  484. }
  485.  
  486. List
  487. group_planlist(planlist)
  488. List    planlist;
  489. {
  490.     List plangroups = LispNil;
  491.     Plan p1, p2;
  492.     List plist;
  493.     List g, x;
  494.  
  495.     plist = planlist;
  496.     while (!lispNullp(plist)) {
  497.     p1 = (Plan)CAR(plist);
  498.     g = lispCons((LispValue)p1, LispNil);
  499.     foreach (x, CDR(plist)) {
  500.         p2 = (Plan)CAR(x);
  501.         if (plan_isomorphic(p1, p2)) {
  502.         g = nappend1(g, (LispValue)p2);
  503.           }
  504.       }
  505.     plist = nset_difference(plist, g);
  506.     plangroups = nappend1(plangroups, g);
  507.       }
  508.     return plangroups;
  509. }
  510.  
  511. bool
  512. allNestLoop(plan)
  513. Plan    plan;
  514. {
  515.     if (plan == NULL) return true;
  516.     if (IsA(plan,Temp) ||
  517.     (IsA(plan,Scan) && get_scanrelid((Scan)plan) == _TEMP_RELATION_ID_))
  518.     return allNestLoop(get_lefttree(plan));
  519.     if (IsA(plan,NestLoop)) {
  520.     return allNestLoop(get_lefttree(plan)) &&
  521.            allNestLoop(get_righttree(plan));
  522.       }
  523.     if (IsA(plan,Join))
  524.     return false;
  525.     return true;
  526. }
  527.  
  528. Plan
  529. pickNestLoopPlan(plangroup)
  530. List    plangroup;
  531. {
  532.     LispValue x;
  533.     Plan p;
  534.  
  535.     foreach (x, plangroup) {
  536.     p = (Plan)CAR(x);
  537.     if (allNestLoop(p))
  538.         return p;
  539.       }
  540.     return NULL;
  541. }
  542.  
  543. void
  544. setPlanStats(p1, p2)
  545. Plan p1, p2;
  546. {
  547.     if (p1 == NULL || p2 == NULL)
  548.     return;
  549.     if (IsA(p1,Join) && IsA(p2,Join)) {
  550.     set_plan_size(p2, get_plan_size(p1));
  551.     setPlanStats(get_lefttree(p1), get_lefttree(p2));
  552.     /*
  553.     setPlanStats(get_righttree(p1), get_righttree(p2));
  554.     */
  555.     return;
  556.       }
  557.     if (IsA(p1,Temp) || IsA(p1,Hash) ||
  558.     (IsA(p1,Scan) && get_scanrelid((Scan)p1) == _TEMP_RELATION_ID_)) {
  559.     setPlanStats(get_lefttree(p1), p2);
  560.     return;
  561.       }
  562.     if (IsA(p2,Temp) || IsA(p2,Hash) ||
  563.     (IsA(p2,Scan) && get_scanrelid((Scan)p2) == _TEMP_RELATION_ID_)) {
  564.     set_plan_size(p2, get_plan_size(p1));
  565.     setPlanStats(p1, get_lefttree(p2));
  566.     return;
  567.       }
  568.     if (IsA(p1,Scan) && IsA(p2,Scan)) {
  569.     set_plan_size(p2, get_plan_size(p1));
  570.     return;
  571.       }
  572.     return;
  573. }
  574.  
  575. void
  576. resetPlanStats(p)
  577. Plan p;
  578. {
  579.     if (p == NULL) return;
  580.     set_plan_size(p, 0);
  581.     if (IsA(p,Join)) {
  582.     resetPlanStats(get_lefttree(p));
  583.     resetPlanStats(get_righttree(p));
  584.     return;
  585.       }
  586.     if (IsA(p,Scan)) {
  587.     if (get_scanrelid((Scan)p) == _TEMP_RELATION_ID_)
  588.        resetPlanStats(get_lefttree(p));
  589.     return;
  590.       }
  591.     resetPlanStats(get_lefttree(p));
  592.     return;
  593. }
  594.  
  595. void
  596. setPlanGroupStats(plan, planlist)
  597. Plan    plan;
  598. List    planlist;
  599. {
  600.     List x;
  601.     Plan p;
  602.  
  603.     foreach (x, planlist) {
  604.     p = (Plan)CAR(x);
  605.     setPlanStats(plan, p);
  606.      }
  607. }
  608.  
  609. bool _exec_collect_stats_ = false;
  610.  
  611. /*
  612.  * XXX mao speaking:
  613.  *
  614.  *    i assume that this routine is used by some planner wizard to
  615.  *    collect actual plan execution costs, and that it's not run
  616.  *    during ordinary postgres processing.  the planner shouldn't
  617.  *    be executing queries directly, right?
  618.  */
  619.  
  620. List
  621. setRealPlanStats(parsetree, planlist)
  622. List    parsetree;
  623. List    planlist;
  624. {
  625.     List plangroups;
  626.     List plangroup;
  627.     LispValue x;
  628.     List ordered_planlist;
  629.     Plan nlplan;
  630.  
  631.     _exec_collect_stats_ = true;
  632.     plangroups = group_planlist(planlist);
  633.     ordered_planlist = LispNil;
  634.     foreach (x, plangroups) {
  635.     plangroup = CAR(x);
  636.     nlplan = pickNestLoopPlan(plangroup);
  637.     if (nlplan == NULL) {
  638.         elog(NOTICE, "no nestloop plan in plan group!");
  639.         break;
  640.       }
  641.     if (IsA(nlplan,Join)) {
  642.         resetPlanStats(nlplan);
  643.         p_plan(nlplan);
  644.         ResetUsage();
  645.         ProcessQuery(parsetree, nlplan, (char *) NULL, (ObjectId *) NULL,
  646.              0, None);
  647.         ShowUsage();
  648.         plangroup = nLispRemove(plangroup, (LispValue)nlplan);
  649.         setPlanGroupStats(nlplan, plangroup);
  650.       }
  651.     else {
  652.         ordered_planlist = plangroup;
  653.         break;
  654.       }
  655.     ordered_planlist = nconc(ordered_planlist, plangroup);
  656.       }
  657.     _exec_collect_stats_ = false;
  658.     return ordered_planlist;
  659. }
  660.  
  661. bool
  662. plan_contain_redundant_hashjoin(plan)
  663. Plan plan;
  664. {
  665.     Plan outerplan, innerplan;
  666.     int outerpages, innerpages;
  667.     if (plan == NULL)
  668.     return false;
  669.     if (IsA(plan,HashJoin)) {
  670.     outerplan = (Plan) get_lefttree(plan);
  671.     innerplan = (Plan) get_lefttree((Plan)get_righttree(plan));
  672.     outerpages = page_size(get_plan_size(outerplan), 
  673.                    get_plan_width(outerplan));
  674.     innerpages = page_size(get_plan_size(innerplan),
  675.                    get_plan_width(innerplan));
  676.     if (!IsA(outerplan,Join) && outerpages < innerpages)
  677.         return true;
  678.       }
  679.     if (IsA(plan,Join))
  680.     return plan_contain_redundant_hashjoin(get_lefttree(plan)) ||
  681.            plan_contain_redundant_hashjoin(get_righttree(plan));
  682.     if (IsA(plan,Temp) || IsA(plan,Hash) ||
  683.     (IsA(plan,Scan) && get_scanrelid((Scan)plan) == _TEMP_RELATION_ID_))
  684.     return plan_contain_redundant_hashjoin(get_lefttree(plan));
  685.     return false;
  686. }
  687.  
  688. List
  689. pruneHashJoinPlans(planlist)
  690. List planlist;
  691. {
  692.     LispValue x;
  693.     Plan plan;
  694.     List retlist;
  695.  
  696.     retlist = LispNil;
  697.     foreach (x, planlist) {
  698.     plan = (Plan)CAR(x);
  699.     if (!plan_contain_redundant_hashjoin(plan))
  700.         retlist = nappend1(retlist, (LispValue)plan);
  701.       }
  702.     return retlist;
  703. }
  704.