Get rid of the rather fuzzily defined FlattenedSubLink node type in favor of
authorTom Lane
Wed, 25 Feb 2009 03:30:38 +0000 (03:30 +0000)
committerTom Lane
Wed, 25 Feb 2009 03:30:38 +0000 (03:30 +0000)
making pull_up_sublinks() construct a full-blown JoinExpr tree representation
of IN/EXISTS SubLinks that it is able to convert to semi or anti joins.
This makes pull_up_sublinks() a shade more complex, but the gain in semantic
clarity is worth it.  I still have more to do in this area to address the
previously-discussed problems, but this commit in itself fixes at least one
bug in HEAD, as shown by added regression test case.

18 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/var.c
src/backend/rewrite/rewriteManip.c
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/subselect.h
src/test/regress/expected/join.out
src/test/regress/expected/join_1.out
src/test/regress/sql/join.sql

index 2175e0c0fc056e6189a8283b2ffe162985fd0fa4..ef3d5fc3732016caa81b26ac19f5ef76109fa34f 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.424 2009/02/24 10:06:32 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.425 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1624,22 +1624,6 @@ _copyRestrictInfo(RestrictInfo *from)
    return newnode;
 }
 
-/*
- * _copyFlattenedSubLink
- */
-static FlattenedSubLink *
-_copyFlattenedSubLink(FlattenedSubLink *from)
-{
-   FlattenedSubLink *newnode = makeNode(FlattenedSubLink);
-
-   COPY_SCALAR_FIELD(jointype);
-   COPY_BITMAPSET_FIELD(lefthand);
-   COPY_BITMAPSET_FIELD(righthand);
-   COPY_NODE_FIELD(quals);
-
-   return newnode;
-}
-
 /*
  * _copyPlaceHolderVar
  */
@@ -3710,9 +3694,6 @@ copyObject(void *from)
        case T_RestrictInfo:
            retval = _copyRestrictInfo(from);
            break;
-       case T_FlattenedSubLink:
-           retval = _copyFlattenedSubLink(from);
-           break;
        case T_PlaceHolderVar:
            retval = _copyPlaceHolderVar(from);
            break;
index 4d905dc945ef34445e46e939dafb2880fac927e1..6b2d99b943a19c7508b95a56c15d2e6beb356d95 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.348 2009/02/24 10:06:32 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.349 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -766,17 +766,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
    return true;
 }
 
-static bool
-_equalFlattenedSubLink(FlattenedSubLink *a, FlattenedSubLink *b)
-{
-   COMPARE_SCALAR_FIELD(jointype);
-   COMPARE_BITMAPSET_FIELD(lefthand);
-   COMPARE_BITMAPSET_FIELD(righthand);
-   COMPARE_NODE_FIELD(quals);
-
-   return true;
-}
-
 static bool
 _equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b)
 {
@@ -2496,9 +2485,6 @@ equal(void *a, void *b)
        case T_RestrictInfo:
            retval = _equalRestrictInfo(a, b);
            break;
-       case T_FlattenedSubLink:
-           retval = _equalFlattenedSubLink(a, b);
-           break;
        case T_PlaceHolderVar:
            retval = _equalPlaceHolderVar(a, b);
            break;
index 683436f17ba53fdb633d3534c218e94fe96aae26..8a04d71eb82c951630b0f00e0b7288dc4607a362 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.37 2009/01/01 17:23:43 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.38 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1309,14 +1309,6 @@ expression_tree_walker(Node *node,
                /* groupClauses are deemed uninteresting */
            }
            break;
-       case T_FlattenedSubLink:
-           {
-               FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
-               if (walker(fslink->quals, context))
-                   return true;
-           }
-           break;
        case T_PlaceHolderVar:
            return walker(((PlaceHolderVar *) node)->phexpr, context);
        case T_AppendRelInfo:
@@ -1972,17 +1964,6 @@ expression_tree_mutator(Node *node,
                return (Node *) newnode;
            }
            break;
-       case T_FlattenedSubLink:
-           {
-               FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-               FlattenedSubLink *newnode;
-
-               FLATCOPY(newnode, fslink, FlattenedSubLink);
-               /* Assume we need not copy the relids bitmapsets */
-               MUTATE(newnode->quals, fslink->quals, Expr *);
-               return (Node *) newnode;
-           }
-           break;
        case T_PlaceHolderVar:
            {
                PlaceHolderVar *phv = (PlaceHolderVar *) node;
index 5dc9db98bf9f8ae62c0f56a1f36a970ea796c914..c68dea8a2ce491dbdc2f52bb7f93f7f317e50da9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.352 2009/02/06 23:43:23 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.353 2009/02/25 03:30:37 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -1630,17 +1630,6 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node)
    WRITE_NODE_FIELD(cheapest_total_innerpath);
 }
 
-static void
-_outFlattenedSubLink(StringInfo str, FlattenedSubLink *node)
-{
-   WRITE_NODE_TYPE("FLATTENEDSUBLINK");
-
-   WRITE_ENUM_FIELD(jointype, JoinType);
-   WRITE_BITMAPSET_FIELD(lefthand);
-   WRITE_BITMAPSET_FIELD(righthand);
-   WRITE_NODE_FIELD(quals);
-}
-
 static void
 _outPlaceHolderVar(StringInfo str, PlaceHolderVar *node)
 {
@@ -2660,9 +2649,6 @@ _outNode(StringInfo str, void *obj)
            case T_InnerIndexscanInfo:
                _outInnerIndexscanInfo(str, obj);
                break;
-           case T_FlattenedSubLink:
-               _outFlattenedSubLink(str, obj);
-               break;
            case T_PlaceHolderVar:
                _outPlaceHolderVar(str, obj);
                break;
index 1b3296f971aac49f4c0ef080208c819fcecec79b..25117716c349bd5ad595d399531bdea3e276f84b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.147 2009/02/20 00:01:03 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.148 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,9 +52,6 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                        Relids qualscope,
                        Relids ojscope,
                        Relids outerjoin_nonnullable);
-static void distribute_sublink_quals_to_rels(PlannerInfo *root,
-                                FlattenedSubLink *fslink,
-                                bool below_outer_join);
 static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
                      bool is_pushed_down);
 static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
@@ -336,15 +333,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
        {
            Node   *qual = (Node *) lfirst(l);
 
-           /* FlattenedSubLink wrappers need special processing */
-           if (qual && IsA(qual, FlattenedSubLink))
-               distribute_sublink_quals_to_rels(root,
-                                                (FlattenedSubLink *) qual,
-                                                below_outer_join);
-           else
-               distribute_qual_to_rels(root, qual,
-                                       false, below_outer_join, JOIN_INNER,
-                                       *qualscope, NULL, NULL);
+           distribute_qual_to_rels(root, qual,
+                                   false, below_outer_join, JOIN_INNER,
+                                   *qualscope, NULL, NULL);
        }
    }
    else if (IsA(jtnode, JoinExpr))
@@ -399,6 +390,18 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
                *inner_join_rels = bms_union(left_inners, right_inners);
                nonnullable_rels = leftids;
                break;
+           case JOIN_SEMI:
+               leftjoinlist = deconstruct_recurse(root, j->larg,
+                                                  below_outer_join,
+                                                  &leftids, &left_inners);
+               rightjoinlist = deconstruct_recurse(root, j->rarg,
+                                                   below_outer_join,
+                                                   &rightids, &right_inners);
+               *qualscope = bms_union(leftids, rightids);
+               *inner_join_rels = bms_union(left_inners, right_inners);
+               /* Semi join adds no restrictions for quals */
+               nonnullable_rels = NULL;
+               break;
            case JOIN_FULL:
                leftjoinlist = deconstruct_recurse(root, j->larg,
                                                   true,
@@ -425,6 +428,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
         * semantic scope (ojscope) to pass to distribute_qual_to_rels.  But
         * we mustn't add it to join_info_list just yet, because we don't want
         * distribute_qual_to_rels to think it is an outer join below us.
+        *
+        * Semijoins are a bit of a hybrid: we build a SpecialJoinInfo,
+        * but we want ojscope = NULL for distribute_qual_to_rels.
         */
        if (j->jointype != JOIN_INNER)
        {
@@ -433,7 +439,11 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
                                        *inner_join_rels,
                                        j->jointype,
                                        (List *) j->quals);
-           ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
+           if (j->jointype == JOIN_SEMI)
+               ojscope = NULL;
+           else
+               ojscope = bms_union(sjinfo->min_lefthand,
+                                   sjinfo->min_righthand);
        }
        else
        {
@@ -446,16 +456,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
        {
            Node   *qual = (Node *) lfirst(l);
 
-           /* FlattenedSubLink wrappers need special processing */
-           if (qual && IsA(qual, FlattenedSubLink))
-               distribute_sublink_quals_to_rels(root,
-                                                (FlattenedSubLink *) qual,
-                                                below_outer_join);
-           else
-               distribute_qual_to_rels(root, qual,
-                                       false, below_outer_join, j->jointype,
-                                       *qualscope,
-                                       ojscope, nonnullable_rels);
+           distribute_qual_to_rels(root, qual,
+                                   false, below_outer_join, j->jointype,
+                                   *qualscope,
+                                   ojscope, nonnullable_rels);
        }
 
        /* Now we can add the SpecialJoinInfo to join_info_list */
@@ -1044,64 +1048,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
    distribute_restrictinfo_to_rels(root, restrictinfo);
 }
 
-/*
- * distribute_sublink_quals_to_rels
- *   Pull sublink quals out of a FlattenedSubLink node and distribute
- *   them appropriately; then add a SpecialJoinInfo node to the query's
- *   join_info_list.  The FlattenedSubLink node itself is no longer
- *   needed and does not propagate into further processing.
- */
-static void
-distribute_sublink_quals_to_rels(PlannerInfo *root,
-                                FlattenedSubLink *fslink,
-                                bool below_outer_join)
-{
-   List       *quals = make_ands_implicit(fslink->quals);
-   SpecialJoinInfo *sjinfo;
-   Relids      qualscope;
-   Relids      ojscope;
-   Relids      outerjoin_nonnullable;
-   ListCell   *l;
-
-   /*
-    * Build a suitable SpecialJoinInfo for the sublink.  Note: using
-    * righthand as inner_join_rels is the conservative worst case;
-    * it might be possible to use a smaller set and thereby allow
-    * the sublink join to commute with others inside its RHS.
-    */
-   sjinfo = make_outerjoininfo(root,
-                               fslink->lefthand, fslink->righthand,
-                               fslink->righthand,
-                               fslink->jointype,
-                               quals);
-
-   /* Treat as inner join if SEMI, outer join if ANTI */
-   qualscope = bms_union(sjinfo->syn_lefthand, sjinfo->syn_righthand);
-   if (fslink->jointype == JOIN_SEMI)
-   {
-       ojscope = outerjoin_nonnullable = NULL;
-   }
-   else
-   {
-       Assert(fslink->jointype == JOIN_ANTI);
-       ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
-       outerjoin_nonnullable = fslink->lefthand;
-   }
-
-   /* Distribute the join quals much as for a regular JOIN node */
-   foreach(l, quals)
-   {
-       Node   *qual = (Node *) lfirst(l);
-
-       distribute_qual_to_rels(root, qual,
-                               false, below_outer_join, fslink->jointype,
-                               qualscope, ojscope, outerjoin_nonnullable);
-   }
-
-   /* Now we can add the SpecialJoinInfo to join_info_list */
-   root->join_info_list = lappend(root->join_info_list, sjinfo);
-}
-
 /*
  * check_outerjoin_delay
  *     Detect whether a qual referencing the given relids must be delayed
index e8484437fd179edfb27c04f83a04af6e3aacc3d6..88dcbc23049120c0f4df01b3bb380357e49cdc84 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.145 2009/01/01 17:23:44 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.146 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -932,12 +932,13 @@ SS_process_ctes(PlannerInfo *root)
 }
 
 /*
- * convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
+ * convert_ANY_sublink_to_join: try to convert an ANY SubLink to a join
  *
  * The caller has found an ANY SubLink at the top level of one of the query's
  * qual clauses, but has not checked the properties of the SubLink further.
  * Decide whether it is appropriate to process this SubLink in join style.
- * Return TRUE if so, FALSE if the SubLink cannot be converted.
+ * If so, form a JoinExpr and return it.  Return NULL if the SubLink cannot
+ * be converted to a join.
  *
  * The only non-obvious input parameter is available_rels: this is the set
  * of query rels that can safely be referenced in the sublink expression.
@@ -945,30 +946,33 @@ SS_process_ctes(PlannerInfo *root)
  * is present in an outer join's ON qual.)  The conversion must fail if
  * the converted qual would reference any but these parent-query relids.
  *
- * On success, two output parameters are returned:
- * *new_qual is set to the qual tree that should replace the SubLink in
- *     the parent query's qual tree.  The qual clauses are wrapped in a
- *     FlattenedSubLink node to help later processing place them properly.
- * *fromlist is set to a list of pulled-up jointree item(s) that must be
- *     added at the proper spot in the parent query's jointree.
+ * On success, the returned JoinExpr has larg = NULL and rarg = the jointree
+ * item representing the pulled-up subquery.  The caller must set larg to
+ * represent the relation(s) on the lefthand side of the new join, and insert
+ * the JoinExpr into the upper query's jointree at an appropriate place
+ * (typically, where the lefthand relation(s) had been).  Note that the
+ * passed-in SubLink must also be removed from its original position in the
+ * query quals, since the quals of the returned JoinExpr replace it.
+ * (Notionally, we replace the SubLink with a constant TRUE, then elide the
+ * redundant constant from the qual.)
  *
  * Side effects of a successful conversion include adding the SubLink's
- * subselect to the query's rangetable.
+ * subselect to the query's rangetable, so that it can be referenced in
+ * the JoinExpr's rarg.
  */
-bool
+JoinExpr *
 convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
-                           Relids available_rels,
-                           Node **new_qual, List **fromlist)
+                           Relids available_rels)
 {
+   JoinExpr   *result;
    Query      *parse = root->parse;
    Query      *subselect = (Query *) sublink->subselect;
-   Relids      left_varnos;
+   Relids      upper_varnos;
    int         rtindex;
    RangeTblEntry *rte;
    RangeTblRef *rtr;
    List       *subquery_vars;
-   Expr       *quals;
-   FlattenedSubLink *fslink;
+   Node       *quals;
 
    Assert(sublink->subLinkType == ANY_SUBLINK);
 
@@ -977,28 +981,28 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
     * higher levels should be okay, though.)
     */
    if (contain_vars_of_level((Node *) subselect, 1))
-       return false;
+       return NULL;
 
    /*
-    * The test expression must contain some Vars of the current query,
+    * The test expression must contain some Vars of the parent query,
     * else it's not gonna be a join.  (Note that it won't have Vars
     * referring to the subquery, rather Params.)
     */
-   left_varnos = pull_varnos(sublink->testexpr);
-   if (bms_is_empty(left_varnos))
-       return false;
+   upper_varnos = pull_varnos(sublink->testexpr);
+   if (bms_is_empty(upper_varnos))
+       return NULL;
 
    /*
     * However, it can't refer to anything outside available_rels.
     */
-   if (!bms_is_subset(left_varnos, available_rels))
-       return false;
+   if (!bms_is_subset(upper_varnos, available_rels))
+       return NULL;
 
    /*
     * The combining operators and left-hand expressions mustn't be volatile.
     */
    if (contain_volatile_functions(sublink->testexpr))
-       return false;
+       return NULL;
 
    /*
     * Okay, pull up the sub-select into upper range table.
@@ -1016,13 +1020,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
    rtindex = list_length(parse->rtable);
 
    /*
-    * Form a RangeTblRef for the pulled-up sub-select.  This must be added
-    * to the upper jointree, but it is caller's responsibility to figure
-    * out where.
+    * Form a RangeTblRef for the pulled-up sub-select.
     */
    rtr = makeNode(RangeTblRef);
    rtr->rtindex = rtindex;
-   *fromlist = list_make1(rtr);
 
    /*
     * Build a list of Vars representing the subselect outputs.
@@ -1032,53 +1033,45 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
                                           rtindex);
 
    /*
-    * Build the replacement qual expression, replacing Params with these Vars.
+    * Build the new join's qual expression, replacing Params with these Vars.
     */
-   quals = (Expr *) convert_testexpr(root,
-                                     sublink->testexpr,
-                                     subquery_vars);
+   quals = convert_testexpr(root, sublink->testexpr, subquery_vars);
 
    /*
-    * And finally, build the FlattenedSubLink node.
-    *
-    * Note: at this point left_varnos may well contain join relids, since
-    * the testexpr hasn't been run through flatten_join_alias_vars.  This
-    * will get fixed when flatten_join_alias_vars is run.
+    * And finally, build the JoinExpr node.
     */
-   fslink = makeNode(FlattenedSubLink);
-   fslink->jointype = JOIN_SEMI;
-   fslink->lefthand = left_varnos;
-   fslink->righthand = bms_make_singleton(rtindex);
-   fslink->quals = quals;
+   result = makeNode(JoinExpr);
+   result->jointype = JOIN_SEMI;
+   result->isNatural = false;
+   result->larg = NULL;        /* caller must fill this in */
+   result->rarg = (Node *) rtr;
+   result->using = NIL;
+   result->quals = quals;
+   result->alias = NULL;
+   result->rtindex = 0;        /* we don't need an RTE for it */
 
-   *new_qual = (Node *) fslink;
-
-   return true;
+   return result;
 }
 
 /*
- * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
+ * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join
  *
  * The API of this function is identical to convert_ANY_sublink_to_join's,
  * except that we also support the case where the caller has found NOT EXISTS,
  * so we need an additional input parameter "under_not".
  */
-bool
+JoinExpr *
 convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
-                              bool under_not,
-                              Relids available_rels,
-                              Node **new_qual, List **fromlist)
+                              bool under_not, Relids available_rels)
 {
+   JoinExpr   *result;
    Query      *parse = root->parse;
    Query      *subselect = (Query *) sublink->subselect;
    Node       *whereClause;
    int         rtoffset;
    int         varno;
    Relids      clause_varnos;
-   Relids      left_varnos;
-   Relids      right_varnos;
-   Relids      subselect_varnos;
-   FlattenedSubLink *fslink;
+   Relids      upper_varnos;
 
    Assert(sublink->subLinkType == EXISTS_SUBLINK);
 
@@ -1095,13 +1088,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
     * us with noplace to evaluate the targetlist.
     */
    if (!simplify_EXISTS_query(subselect))
-       return false;
+       return NULL;
 
    /*
     * The subquery must have a nonempty jointree, else we won't have a join.
     */
    if (subselect->jointree->fromlist == NIL)
-       return false;
+       return NULL;
 
    /*
     * Separate out the WHERE clause.  (We could theoretically also remove
@@ -1116,20 +1109,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
     * query.  (Vars of higher levels should be okay, though.)
     */
    if (contain_vars_of_level((Node *) subselect, 1))
-       return false;
+       return NULL;
 
    /*
     * On the other hand, the WHERE clause must contain some Vars of the
     * parent query, else it's not gonna be a join.
     */
    if (!contain_vars_of_level(whereClause, 1))
-       return false;
+       return NULL;
 
    /*
     * We don't risk optimizing if the WHERE clause is volatile, either.
     */
    if (contain_volatile_functions(whereClause))
-       return false;
+       return NULL;
 
    /*
     * Prepare to pull up the sub-select into top range table.
@@ -1142,7 +1135,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
     * to do.  The machinations of simplify_EXISTS_query ensured that there
     * is nothing interesting in the subquery except an rtable and jointree,
     * and even the jointree FromExpr no longer has quals.  So we can just
-    * append the rtable to our own and attach the fromlist to our own.
+    * append the rtable to our own and use the FromExpr in our jointree.
     * But first, adjust all level-zero varnos in the subquery to account
     * for the rtable merger.
     */
@@ -1161,58 +1154,47 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
    /*
     * Now that the WHERE clause is adjusted to match the parent query
     * environment, we can easily identify all the level-zero rels it uses.
-    * The ones <= rtoffset are "left rels" of the join we're forming,
-    * and the ones > rtoffset are "right rels".
+    * The ones <= rtoffset belong to the upper query; the ones > rtoffset
+    * do not.
     */
    clause_varnos = pull_varnos(whereClause);
-   left_varnos = right_varnos = NULL;
+   upper_varnos = NULL;
    while ((varno = bms_first_member(clause_varnos)) >= 0)
    {
        if (varno <= rtoffset)
-           left_varnos = bms_add_member(left_varnos, varno);
-       else
-           right_varnos = bms_add_member(right_varnos, varno);
+           upper_varnos = bms_add_member(upper_varnos, varno);
    }
    bms_free(clause_varnos);
-   Assert(!bms_is_empty(left_varnos));
+   Assert(!bms_is_empty(upper_varnos));
 
    /*
     * Now that we've got the set of upper-level varnos, we can make the
     * last check: only available_rels can be referenced.
     */
-   if (!bms_is_subset(left_varnos, available_rels))
-       return false;
-
-   /* Identify all the rels syntactically within the subselect */
-   subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree,
-                                             true);
-   Assert(!bms_is_empty(subselect_varnos));
-   Assert(bms_is_subset(right_varnos, subselect_varnos));
+   if (!bms_is_subset(upper_varnos, available_rels))
+       return NULL;
 
    /* Now we can attach the modified subquery rtable to the parent */
    parse->rtable = list_concat(parse->rtable, subselect->rtable);
 
    /*
-    * Pass back the subquery fromlist to be attached to upper jointree
-    * in a suitable place.
+    * And finally, build the JoinExpr node.
     */
-   *fromlist = subselect->jointree->fromlist;
-
-   /*
-    * And finally, build the FlattenedSubLink node.
-    *
-    * Note: at this point left_varnos and subselect_varnos may well contain
-    * join relids.  This will get fixed when flatten_join_alias_vars is run.
-    */
-   fslink = makeNode(FlattenedSubLink);
-   fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
-   fslink->lefthand = left_varnos;
-   fslink->righthand = subselect_varnos;
-   fslink->quals = (Expr *) whereClause;
-
-   *new_qual = (Node *) fslink;
+   result = makeNode(JoinExpr);
+   result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
+   result->isNatural = false;
+   result->larg = NULL;        /* caller must fill this in */
+   /* flatten out the FromExpr node if it's useless */
+   if (list_length(subselect->jointree->fromlist) == 1)
+       result->rarg = (Node *) linitial(subselect->jointree->fromlist);
+   else
+       result->rarg = (Node *) subselect->jointree;
+   result->using = NIL;
+   result->quals = whereClause;
+   result->alias = NULL;
+   result->rtindex = 0;        /* we don't need an RTE for it */
 
-   return true;
+   return result;
 }
 
 /*
index 9896b102806236cbd72dd30c01d8a98580cb0762..600facb578d00e56aaa9f9b4f91f764ce7fd8b34 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.62 2009/01/01 17:23:44 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.63 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,7 @@ typedef struct reduce_outer_joins_state
 static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
                                  Relids *relids);
 static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
-                             Relids available_rels, List **fromlist);
+                             Relids available_rels, Node **jtlink);
 static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
                        RangeTblEntry *rte,
                        bool below_outer_join,
@@ -91,7 +91,7 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
  * distinguish whether the ANY ought to return FALSE or NULL in cases
  * involving NULL inputs.  Also, in an outer join's ON clause we can only
  * do this if the sublink is degenerate (ie, references only the nullable
- * side of the join).  In that case we can effectively push the semijoin
+ * side of the join).  In that case it is legal to push the semijoin
  * down into the nullable side of the join.  If the sublink references any
  * nonnullable-side variables then it would have to be evaluated as part
  * of the outer join, which makes things way too complicated.
@@ -110,13 +110,22 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
 void
 pull_up_sublinks(PlannerInfo *root)
 {
+   Node       *jtnode;
    Relids      relids;
 
    /* Begin recursion through the jointree */
-   root->parse->jointree = (FromExpr *)
-       pull_up_sublinks_jointree_recurse(root,
-                                         (Node *) root->parse->jointree,
-                                         &relids);
+   jtnode = pull_up_sublinks_jointree_recurse(root,
+                                              (Node *) root->parse->jointree,
+                                              &relids);
+
+   /*
+    * root->parse->jointree must always be a FromExpr, so insert a dummy one
+    * if we got a bare RangeTblRef or JoinExpr out of the recursion.
+    */
+   if (IsA(jtnode, FromExpr))
+       root->parse->jointree = (FromExpr *) jtnode;
+   else
+       root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
 }
 
 /*
@@ -144,9 +153,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
    {
        FromExpr   *f = (FromExpr *) jtnode;
        List       *newfromlist = NIL;
-       Node       *newquals;
-       List       *subfromlist = NIL;
        Relids      frelids = NULL;
+       FromExpr   *newf;
+       Node       *jtlink;
        ListCell   *l;
 
        /* First, recurse to process children and collect their relids */
@@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
            newfromlist = lappend(newfromlist, newchild);
            frelids = bms_join(frelids, childrelids);
        }
+       /* Build the replacement FromExpr; no quals yet */
+       newf = makeFromExpr(newfromlist, NULL);
+       /* Set up a link representing the rebuilt jointree */
+       jtlink = (Node *) newf;
        /* Now process qual --- all children are available for use */
-       newquals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
-                                                &subfromlist);
-       /* Any pulled-up subqueries can just be attached to the fromlist */
-       newfromlist = list_concat(newfromlist, subfromlist);
+       newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
+                                                   &jtlink);
 
        /*
+        * Note that the result will be either newf, or a stack of JoinExprs
+        * with newf at the base.  We rely on subsequent optimization steps
+        * to flatten this and rearrange the joins as needed.
+        *
         * Although we could include the pulled-up subqueries in the returned
         * relids, there's no need since upper quals couldn't refer to their
         * outputs anyway.
         */
        *relids = frelids;
-       jtnode = (Node *) makeFromExpr(newfromlist, newquals);
+       jtnode = jtlink;
    }
    else if (IsA(jtnode, JoinExpr))
    {
        JoinExpr   *j;
        Relids      leftrelids;
        Relids      rightrelids;
-       List       *subfromlist = NIL;
+       Node       *jtlink;
 
        /*
         * Make a modifiable copy of join node, but don't bother copying
@@ -188,6 +203,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
         */
        j = (JoinExpr *) palloc(sizeof(JoinExpr));
        memcpy(j, jtnode, sizeof(JoinExpr));
+       jtlink = (Node *) j;
 
        /* Recurse to process children and collect their relids */
        j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
@@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
 
        /*
         * Now process qual, showing appropriate child relids as available,
-        * and then attach any pulled-up jointree items at the right place.
-        * The pulled-up items must go below where the quals that refer to
-        * them will be placed.  Since the JoinExpr itself can only handle
-        * two child nodes, we hack up a valid jointree by inserting dummy
-        * FromExprs that have no quals.  These should get flattened out
-        * during deconstruct_recurse(), so they won't impose any extra
-        * overhead.
+        * and attach any pulled-up jointree items at the right place.
+        * In the inner-join case we put new JoinExprs above the existing one
+        * (much as for a FromExpr-style join).  In outer-join cases the
+        * new JoinExprs must go into the nullable side of the outer join.
+        * The point of the available_rels machinations is to ensure that we
+        * only pull up quals for which that's okay.
+        *
+        * We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI
+        * nodes here.
         */
        switch (j->jointype)
        {
@@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
                j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
                                                         bms_union(leftrelids,
                                                                  rightrelids),
-                                                        &subfromlist);
-               /* We arbitrarily put pulled-up subqueries into right child */
-               if (subfromlist)
-                   j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
-                                                         subfromlist),
-                                                   NULL);
+                                                        &jtlink);
                break;
            case JOIN_LEFT:
                j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
                                                         rightrelids,
-                                                        &subfromlist);
-               /* Any pulled-up subqueries must go into right child */
-               if (subfromlist)
-                   j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
-                                                         subfromlist),
-                                                   NULL);
+                                                        &j->rarg);
                break;
            case JOIN_FULL:
                /* can't do anything with full-join quals */
@@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
            case JOIN_RIGHT:
                j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
                                                         leftrelids,
-                                                        &subfromlist);
-               /* Any pulled-up subqueries must go into left child */
-               if (subfromlist)
-                   j->larg = (Node *) makeFromExpr(lcons(j->larg,
-                                                         subfromlist),
-                                                   NULL);
+                                                        &j->larg);
                break;
            default:
                elog(ERROR, "unrecognized join type: %d",
@@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
         * levels would mistakenly think they couldn't use references to this
         * join.
         */
-       *relids = bms_add_member(bms_join(leftrelids, rightrelids),
-                                j->rtindex);
-       jtnode = (Node *) j;
+       *relids = bms_join(leftrelids, rightrelids);
+       if (j->rtindex)
+           *relids = bms_add_member(*relids, j->rtindex);
+       jtnode = jtlink;
    }
    else
        elog(ERROR, "unrecognized node type: %d",
@@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
 /*
  * Recurse through top-level qual nodes for pull_up_sublinks()
  *
- * Caller must have initialized *fromlist to NIL.  We append any new
- * jointree items to that list.
+ * jtlink points to the link in the jointree where any new JoinExprs should be
+ * inserted.  If we find multiple pull-up-able SubLinks, they'll get stacked
+ * there in the order we encounter them.  We rely on subsequent optimization
+ * to rearrange the stack if appropriate.
  */
 static Node *
 pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
-                             Relids available_rels, List **fromlist)
+                             Relids available_rels, Node **jtlink)
 {
    if (node == NULL)
        return NULL;
    if (IsA(node, SubLink))
    {
        SubLink    *sublink = (SubLink *) node;
-       Node       *new_qual;
-       List       *new_fromlist;
+       JoinExpr   *j;
 
        /* Is it a convertible ANY or EXISTS clause? */
        if (sublink->subLinkType == ANY_SUBLINK)
        {
-           if (convert_ANY_sublink_to_join(root, sublink,
-                                           available_rels,
-                                           &new_qual, &new_fromlist))
+           j = convert_ANY_sublink_to_join(root, sublink,
+                                           available_rels);
+           if (j)
            {
-               *fromlist = list_concat(*fromlist, new_fromlist);
-               return new_qual;
+               /* Yes, insert the new join node into the join tree */
+               j->larg = *jtlink;
+               *jtlink = (Node *) j;
+               /* and return NULL representing constant TRUE */
+               return NULL;
            }
        }
        else if (sublink->subLinkType == EXISTS_SUBLINK)
        {
-           if (convert_EXISTS_sublink_to_join(root, sublink, false,
-                                              available_rels,
-                                              &new_qual, &new_fromlist))
+           j = convert_EXISTS_sublink_to_join(root, sublink, false,
+                                              available_rels);
+           if (j)
            {
-               *fromlist = list_concat(*fromlist, new_fromlist);
-               return new_qual;
+               /* Yes, insert the new join node into the join tree */
+               j->larg = *jtlink;
+               *jtlink = (Node *) j;
+               /* and return NULL representing constant TRUE */
+               return NULL;
            }
        }
        /* Else return it unmodified */
@@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
    {
        /* If the immediate argument of NOT is EXISTS, try to convert */
        SubLink    *sublink = (SubLink *) get_notclausearg((Expr *) node);
-       Node       *new_qual;
-       List       *new_fromlist;
+       JoinExpr   *j;
 
        if (sublink && IsA(sublink, SubLink))
        {
            if (sublink->subLinkType == EXISTS_SUBLINK)
            {
-               if (convert_EXISTS_sublink_to_join(root, sublink, true,
-                                                  available_rels,
-                                                  &new_qual, &new_fromlist))
+               j = convert_EXISTS_sublink_to_join(root, sublink, true,
+                                                  available_rels);
+               if (j)
                {
-                   *fromlist = list_concat(*fromlist, new_fromlist);
-                   return new_qual;
+                   /* Yes, insert the new join node into the join tree */
+                   j->larg = *jtlink;
+                   *jtlink = (Node *) j;
+                   /* and return NULL representing constant TRUE */
+                   return NULL;
                }
            }
        }
@@ -339,14 +352,22 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
        foreach(l, ((BoolExpr *) node)->args)
        {
            Node       *oldclause = (Node *) lfirst(l);
-
-           newclauses = lappend(newclauses,
-                                pull_up_sublinks_qual_recurse(root,
-                                                              oldclause,
-                                                              available_rels,
-                                                              fromlist));
+           Node       *newclause;
+
+           newclause = pull_up_sublinks_qual_recurse(root,
+                                                     oldclause,
+                                                     available_rels,
+                                                     jtlink);
+           if (newclause)
+               newclauses = lappend(newclauses, newclause);
        }
-       return (Node *) make_andclause(newclauses);
+       /* We might have got back fewer clauses than we started with */
+       if (newclauses == NIL)
+           return NULL;
+       else if (list_length(newclauses) == 1)
+           return (Node *) linitial(newclauses);
+       else
+           return (Node *) make_andclause(newclauses);
    }
    /* Stop if not an AND */
    return node;
@@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
                                             below_outer_join, false);
                break;
            case JOIN_LEFT:
+           case JOIN_SEMI:
+           case JOIN_ANTI:
                j->larg = pull_up_subqueries(root, j->larg,
                                             below_outer_join, false);
                j->rarg = pull_up_subqueries(root, j->rarg,
@@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
    parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
 
    /*
-    * We also have to fix the relid sets of any FlattenedSubLink and
-    * PlaceHolderVar nodes in the parent query.  (This could perhaps be done
-    * by ResolveNew, but it would clutter that routine's API unreasonably.)
-    * Note in particular that any PlaceHolderVar nodes just created by
-    * insert_targetlist_placeholders() will be adjusted, so having created
-    * them with the subquery's varno is correct.
+    * We also have to fix the relid sets of any PlaceHolderVar nodes in the
+    * parent query.  (This could perhaps be done by ResolveNew, but it would
+    * clutter that routine's API unreasonably.)  Note in particular that any
+    * PlaceHolderVar nodes just created by insert_targetlist_placeholders()
+    * will be adjusted, so having created them with the subquery's varno is
+    * correct.
     *
     * Likewise, relids appearing in AppendRelInfo nodes have to be fixed.
     * We already checked that this won't require introducing multiple
@@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode,
                        jointype = JOIN_RIGHT;
                }
                break;
+           case JOIN_SEMI:
+           case JOIN_ANTI:
+               /*
+                * These could only have been introduced by pull_up_sublinks,
+                * so there's no way that upper quals could refer to their
+                * righthand sides, and no point in checking.
+                */
+               break;
            default:
                elog(ERROR, "unrecognized join type: %d",
                     (int) jointype);
@@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode,
        }
 
        /* Apply the jointype change, if any, to both jointree node and RTE */
-       if (jointype != j->jointype)
+       if (rtindex && jointype != j->jointype)
        {
            RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
 
            Assert(rte->rtekind == RTE_JOIN);
            Assert(rte->jointype == j->jointype);
-           rte->jointype = j->jointype = jointype;
+           rte->jointype = jointype;
        }
+       j->jointype = jointype;
 
        /* Only recurse if there's more to do below here */
        if (left_state->contains_outer || right_state->contains_outer)
@@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode,
                    pass_nonnullable_vars = local_nonnullable_vars;
                    pass_forced_null_vars = local_forced_null_vars;
                }
-               else if (jointype != JOIN_FULL)     /* ie, LEFT or ANTI */
+               else if (jointype != JOIN_FULL)     /* ie, LEFT/SEMI/ANTI */
                {
                    /* can't pass local constraints to non-nullable side */
                    pass_nonnullable_rels = nonnullable_rels;
@@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode,
 
            if (right_state->contains_outer)
            {
-               if (jointype != JOIN_FULL)      /* ie, INNER, LEFT or ANTI */
+               if (jointype != JOIN_FULL)      /* ie, INNER/LEFT/SEMI/ANTI */
                {
                    /* pass appropriate constraints, per comment above */
                    pass_nonnullable_rels = local_nonnullable_rels;
@@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode,
  * substitute_multiple_relids - adjust node relid sets after pulling up
  * a subquery
  *
- * Find any FlattenedSubLink or PlaceHolderVar nodes in the given tree that
- * reference the pulled-up relid, and change them to reference the replacement
- * relid(s).  We do not need to recurse into subqueries, since no subquery of
- * the current top query could (yet) contain such a reference.
+ * Find any PlaceHolderVar nodes in the given tree that reference the
+ * pulled-up relid, and change them to reference the replacement relid(s).
+ * We do not need to recurse into subqueries, since no subquery of the current
+ * top query could (yet) contain such a reference.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
  * nodes in-place.  This should be OK since the tree was copied by ResolveNew
@@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node,
 {
    if (node == NULL)
        return false;
-   if (IsA(node, FlattenedSubLink))
-   {
-       FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
-       if (bms_is_member(context->varno, fslink->lefthand))
-       {
-           fslink->lefthand = bms_union(fslink->lefthand,
-                                        context->subrelids);
-           fslink->lefthand = bms_del_member(fslink->lefthand,
-                                             context->varno);
-       }
-       if (bms_is_member(context->varno, fslink->righthand))
-       {
-           fslink->righthand = bms_union(fslink->righthand,
-                                         context->subrelids);
-           fslink->righthand = bms_del_member(fslink->righthand,
-                                              context->varno);
-       }
-       /* fall through to examine children */
-   }
    if (IsA(node, PlaceHolderVar))
    {
        PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins)
        result = get_relids_in_jointree(j->larg, include_joins);
        result = bms_join(result,
                          get_relids_in_jointree(j->rarg, include_joins));
-       if (include_joins)
+       if (include_joins && j->rtindex)
            result = bms_add_member(result, j->rtindex);
    }
    else
index b9ce6d2ed0cb8bca1c3a1be55ecd41399cf60a54..13d08eef3eec666f984f6bc310d49bb24bcfb59e 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.165 2009/02/06 23:43:23 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.166 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1587,23 +1587,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
            j->rtindex = context->child_relid;
        return (Node *) j;
    }
-   if (IsA(node, FlattenedSubLink))
-   {
-       /* Copy the FlattenedSubLink node with correct mutation of subnodes */
-       FlattenedSubLink *fslink;
-
-       fslink = (FlattenedSubLink *) expression_tree_mutator(node,
-                                             adjust_appendrel_attrs_mutator,
-                                                            (void *) context);
-       /* now fix FlattenedSubLink's relid sets */
-       fslink->lefthand = adjust_relid_set(fslink->lefthand,
-                                           context->parent_relid,
-                                           context->child_relid);
-       fslink->righthand = adjust_relid_set(fslink->righthand,
-                                            context->parent_relid,
-                                            context->child_relid);
-       return (Node *) fslink;
-   }
    if (IsA(node, PlaceHolderVar))
    {
        /* Copy the PlaceHolderVar node with correct mutation of subnodes */
index 65c9b61458460954b0b6a0bfbc68349fa7a5e0cb..c9c7270d2b86d9886f5739f5e6e95a5548f41df3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.275 2009/01/09 15:46:10 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.276 2009/02/25 03:30:37 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1300,15 +1300,6 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
             expr->booltesttype == IS_NOT_UNKNOWN))
            result = find_nonnullable_rels_walker((Node *) expr->arg, false);
    }
-   else if (IsA(node, FlattenedSubLink))
-   {
-       /* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */
-       FlattenedSubLink *expr = (FlattenedSubLink *) node;
-
-       if (expr->jointype == JOIN_SEMI)
-           result = find_nonnullable_rels_walker((Node *) expr->quals,
-                                                 top_level);
-   }
    else if (IsA(node, PlaceHolderVar))
    {
        PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -1511,15 +1502,6 @@ find_nonnullable_vars_walker(Node *node, bool top_level)
             expr->booltesttype == IS_NOT_UNKNOWN))
            result = find_nonnullable_vars_walker((Node *) expr->arg, false);
    }
-   else if (IsA(node, FlattenedSubLink))
-   {
-       /* JOIN_SEMI sublinks preserve strictness, but JOIN_ANTI ones don't */
-       FlattenedSubLink *expr = (FlattenedSubLink *) node;
-
-       if (expr->jointype == JOIN_SEMI)
-           result = find_nonnullable_vars_walker((Node *) expr->quals,
-                                                 top_level);
-   }
    else if (IsA(node, PlaceHolderVar))
    {
        PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -2943,24 +2925,6 @@ eval_const_expressions_mutator(Node *node,
        newbtest->booltesttype = btest->booltesttype;
        return (Node *) newbtest;
    }
-   if (IsA(node, FlattenedSubLink))
-   {
-       FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-       FlattenedSubLink *newfslink;
-       Expr       *quals;
-
-       /* Simplify and also canonicalize the arguments */
-       quals = (Expr *) eval_const_expressions_mutator((Node *) fslink->quals,
-                                                       context);
-       quals = canonicalize_qual(quals);
-
-       newfslink = makeNode(FlattenedSubLink);
-       newfslink->jointype = fslink->jointype;
-       newfslink->lefthand = fslink->lefthand;
-       newfslink->righthand = fslink->righthand;
-       newfslink->quals = quals;
-       return (Node *) newfslink;
-   }
    if (IsA(node, PlaceHolderVar) && context->estimate)
    {
        /*
index 64d6bba14808b55364ca058657580fcfc830780d..7768712b569012db9c0163cc9dcfcef6ec2a9d14 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.83 2009/01/01 17:23:45 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.84 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -766,24 +766,6 @@ flatten_join_alias_vars_mutator(Node *node,
        /* Recurse in case join input is itself a join */
        return flatten_join_alias_vars_mutator(newvar, context);
    }
-   if (IsA(node, FlattenedSubLink))
-   {
-       /* Copy the FlattenedSubLink node with correct mutation of subnodes */
-       FlattenedSubLink *fslink;
-
-       fslink = (FlattenedSubLink *) expression_tree_mutator(node,
-                                            flatten_join_alias_vars_mutator,
-                                                            (void *) context);
-       /* now fix FlattenedSubLink's relid sets */
-       if (context->sublevels_up == 0)
-       {
-           fslink->lefthand = alias_relid_set(context->root,
-                                              fslink->lefthand);
-           fslink->righthand = alias_relid_set(context->root,
-                                               fslink->righthand);
-       }
-       return (Node *) fslink;
-   }
    if (IsA(node, PlaceHolderVar))
    {
        /* Copy the PlaceHolderVar node with correct mutation of subnodes */
index e0403927f32368a7de466c566457b794da3539d8..72cddaaa34cc78ab70044258668526ae7f38d325 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.120 2009/01/01 17:23:47 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.121 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -348,23 +348,10 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
    {
        JoinExpr   *j = (JoinExpr *) node;
 
-       if (context->sublevels_up == 0)
+       if (j->rtindex && context->sublevels_up == 0)
            j->rtindex += context->offset;
        /* fall through to examine children */
    }
-   if (IsA(node, FlattenedSubLink))
-   {
-       FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
-       if (context->sublevels_up == 0)
-       {
-           fslink->lefthand = offset_relid_set(fslink->lefthand,
-                                               context->offset);
-           fslink->righthand = offset_relid_set(fslink->righthand,
-                                                context->offset);
-       }
-       /* fall through to examine children */
-   }
    if (IsA(node, PlaceHolderVar))
    {
        PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -530,21 +517,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
            j->rtindex = context->new_index;
        /* fall through to examine children */
    }
-   if (IsA(node, FlattenedSubLink))
-   {
-       FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
-       if (context->sublevels_up == 0)
-       {
-           fslink->lefthand = adjust_relid_set(fslink->lefthand,
-                                               context->rt_index,
-                                               context->new_index);
-           fslink->righthand = adjust_relid_set(fslink->righthand,
-                                                context->rt_index,
-                                                context->new_index);
-       }
-       /* fall through to examine children */
-   }
    if (IsA(node, PlaceHolderVar))
    {
        PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -838,7 +810,6 @@ rangeTableEntry_used_walker(Node *node,
        /* fall through to examine children */
    }
    /* Shouldn't need to handle planner auxiliary nodes here */
-   Assert(!IsA(node, FlattenedSubLink));
    Assert(!IsA(node, PlaceHolderVar));
    Assert(!IsA(node, SpecialJoinInfo));
    Assert(!IsA(node, AppendRelInfo));
index 31881976bd8e510be5e0cc9bffc13ae0a38f0ad3..11e31e0a65ad51b777b7fa61a25ec2e56ebf84a7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.220 2009/02/02 19:31:40 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.221 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -216,7 +216,6 @@ typedef enum NodeTag
    T_PathKey,
    T_RestrictInfo,
    T_InnerIndexscanInfo,
-   T_FlattenedSubLink,
    T_PlaceHolderVar,
    T_SpecialJoinInfo,
    T_AppendRelInfo,
index 0d8efbe98802fbd9b9528bff8077b17d7909e4bf..3315329923e50027ba9b007db09f277fac928c1a 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.145 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.146 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1137,7 +1137,9 @@ typedef struct RangeTblRef
  *
  * During parse analysis, an RTE is created for the Join, and its index
  * is filled into rtindex. This RTE is present mainly so that Vars can
- * be created that refer to the outputs of the join.
+ * be created that refer to the outputs of the join.  The planner sometimes
+ * generates JoinExprs internally; these can have rtindex = 0 if there are
+ * no join alias variables referencing such joins.
  *----------
  */
 typedef struct JoinExpr
@@ -1150,7 +1152,7 @@ typedef struct JoinExpr
    List       *using;          /* USING clause, if any (list of String) */
    Node       *quals;          /* qualifiers on join, if any */
    Alias      *alias;          /* user-written alias clause, if any */
-   int         rtindex;        /* RT index assigned for join */
+   int         rtindex;        /* RT index assigned for join, or 0 */
 } JoinExpr;
 
 /*----------
index f00d1becc70c62c0f1f59f4028a26c2b04f59935..e8e20d202b15506e408b0ad9e036ce1b19e952c5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.168 2009/02/06 23:43:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.169 2009/02/25 03:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1075,30 +1075,6 @@ typedef struct InnerIndexscanInfo
    Path       *cheapest_total_innerpath;       /* cheapest total cost */
 } InnerIndexscanInfo;
 
-/*
- * "Flattened SubLinks"
- *
- * When we pull an IN or EXISTS SubLink up into the parent query, the
- * join conditions extracted from the IN/EXISTS clause need to be specially
- * treated in distribute_qual_to_rels processing.  We handle this by
- * wrapping such expressions in a FlattenedSubLink node that identifies
- * the join they come from.  The FlattenedSubLink node is discarded after
- * distribute_qual_to_rels, having served its purpose.
- *
- * Although the planner treats this as an expression node type, it is not
- * recognized by the parser or executor, so we declare it here rather than
- * in primnodes.h.
- */
-
-typedef struct FlattenedSubLink
-{
-   Expr        xpr;
-   JoinType    jointype;       /* must be JOIN_SEMI or JOIN_ANTI */
-   Relids      lefthand;       /* base relids treated as syntactic LHS */
-   Relids      righthand;      /* base relids syntactically within RHS */
-   Expr       *quals;          /* join quals (in explicit-AND format) */
-} FlattenedSubLink;
-
 /*
  * Placeholder node for an expression to be evaluated below the top level
  * of a plan tree.  This is used during planning to represent the contained
@@ -1171,8 +1147,11 @@ typedef struct PlaceHolderVar
  * For purposes of join selectivity estimation, we create transient
  * SpecialJoinInfo structures for regular inner joins; so it is possible
  * to have jointype == JOIN_INNER in such a structure, even though this is
- * not allowed within join_info_list.  Note that lhs_strict, delay_upper_joins,
- * and join_quals are not set meaningfully for such structs.
+ * not allowed within join_info_list.  We also create transient
+ * SpecialJoinInfos with jointype == JOIN_INNER for outer joins, since for
+ * cost estimation purposes it is sometimes useful to know the join size under
+ * plain innerjoin semantics.  Note that lhs_strict, delay_upper_joins, and
+ * join_quals are not set meaningfully within such structs.
  */
 
 typedef struct SpecialJoinInfo
index 54965a5fe713ab1e8364abf6dcddd6caea038197..a3188535965bceef9e48163742e1fd0ed1ae1355 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.35 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.36 2009/02/25 03:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "nodes/relation.h"
 
 extern void SS_process_ctes(PlannerInfo *root);
-extern bool convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
-                                       Relids available_rels,
-                                       Node **new_qual, List **fromlist);
-extern bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
-                                          bool under_not,
-                                          Relids available_rels,
-                                          Node **new_qual, List **fromlist);
+extern JoinExpr *convert_ANY_sublink_to_join(PlannerInfo *root,
+                                            SubLink *sublink,
+                                            Relids available_rels);
+extern JoinExpr *convert_EXISTS_sublink_to_join(PlannerInfo *root,
+                                               SubLink *sublink,
+                                               bool under_not,
+                                               Relids available_rels);
 extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
 extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
 extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
index 12b54aad172fda320a1df6df25cdecc3a5ca5062..8e6fc13bb38fd8e9fb8ef4e766c6737f6d0205e3 100644 (file)
@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
 ---------+-----+----------+---------+---------
 (0 rows)
 
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+  select count(*) from tenk1 a left join tenk1 b
+    on (a.unique2 = b.unique1 and exists
+        (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+ count 
+-------
+ 10000
+(1 row)
+
+execute foo(false);
+ count 
+-------
+ 10000
+(1 row)
+
index 2721c3f1cf0a6b5a638e696b3fc08684140f7461..3032beacd421fdaaf04a3b583d2fcca8b7c998ae 100644 (file)
@@ -2333,3 +2333,22 @@ where a.unique1 = 42 and
 ---------+-----+----------+---------+---------
 (0 rows)
 
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+  select count(*) from tenk1 a left join tenk1 b
+    on (a.unique2 = b.unique1 and exists
+        (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+ count 
+-------
+ 10000
+(1 row)
+
+execute foo(false);
+ count 
+-------
+ 10000
+(1 row)
+
index 245fde58ae43c6898c01a2b708a1dc1d9b9e394b..149530e14db25d50e38d3d69f01c15c4043fee79 100644 (file)
@@ -495,3 +495,13 @@ select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred
 from tenk1 a left join tenk1 b on a.unique2 = b.tenthous
 where a.unique1 = 42 and
       ((b.unique2 is null and a.ten = 2) or b.hundred = 3);
+
+--
+-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
+--
+prepare foo(bool) as
+  select count(*) from tenk1 a left join tenk1 b
+    on (a.unique2 = b.unique1 and exists
+        (select 1 from tenk1 c where c.thousand = b.unique2 and $1));
+execute foo(true);
+execute foo(false);