Allow merge and hash joins to occur on arbitrary expressions (anything not
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 15 Jan 2003 19:35:48 +0000 (19:35 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 15 Jan 2003 19:35:48 +0000 (19:35 +0000)
containing a volatile function), rather than only on 'Var = Var' clauses
as before.  This makes it practical to do flatten_join_alias_vars at the
start of planning, which in turn eliminates a bunch of klugery inside the
planner to deal with alias vars.  As a free side effect, we now detect
implied equality of non-Var expressions; for example in
SELECT ... WHERE a.x = b.y and b.y = 42
we will deduce a.x = 42 and use that as a restriction qual on a.  Also,
we can remove the restriction introduced 12/5/02 to prevent pullup of
subqueries whose targetlists contain sublinks.
Still TODO: make statistical estimation routines in selfuncs.c and costsize.c
smarter about expressions that are more complex than plain Vars.  The need
for this is considerably greater now that we have to be able to estimate
the suitability of merge and hash join techniques on such expressions.

32 files changed:
doc/src/sgml/xoper.sgml
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/optimizer/README
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/var.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/cache/lsyscache.c
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/optimizer/pathnode.h
src/include/optimizer/planmain.h
src/include/optimizer/var.h
src/include/utils/lsyscache.h
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/type_sanity.out
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/type_sanity.sql

index 395306bbd60e3234d1a257e97775f1466fac9352..24c74cd8b60c96e179c5f2cfb3bcee0f9a6e0f96 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.21 2003/01/06 01:20:40 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.22 2003/01/15 19:35:35 tgl Exp $
 -->
 
  <Chapter Id="xoper">
@@ -375,6 +375,27 @@ table1.column1 OP table2.column2
      equality operators that are (or could be) implemented by <function>memcmp()</function>.
     </para>
 
+    <note>
+    <para>
+     The function underlying a hashjoinable operator must be marked
+     immutable or stable.  If it is volatile, the system will never
+     attempt to use the operator for a hash join.
+    </para>
+    </note>
+
+    <note>
+    <para>
+     If a hashjoinable operator has an underlying function that is marked
+     strict, the
+     function must also be complete: that is, it should return TRUE or
+     FALSE, never NULL, for any two non-NULL inputs.  If this rule is
+     not followed, hash-optimization of <literal>IN</> operations may
+     generate wrong results.  (Specifically, <literal>IN</> might return
+     FALSE where the correct answer per spec would be NULL; or it might
+     yield an error complaining that it wasn't prepared for a NULL result.)
+    </para>
+    </note>
+
    </sect2>
 
    <sect2>
@@ -472,6 +493,14 @@ table1.column1 OP table2.column2
      </itemizedlist>
     </para>
 
+    <note>
+    <para>
+     The function underlying a mergejoinable operator must be marked
+     immutable or stable.  If it is volatile, the system will never
+     attempt to use the operator for a merge join.
+    </para>
+    </note>
+
     <note>
     <para>
      <literal>GROUP BY</> and <literal>DISTINCT</> operations require each
index e260bc6595077d21a6e21841aeae90412ec94a5a..8663c6c4a147faa47d1ad653b65d6bd09c537a12 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.235 2003/01/10 21:08:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.236 2003/01/15 19:35:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1059,6 +1059,8 @@ _copyRestrictInfo(RestrictInfo *from)
        COPY_NODE_FIELD(subclauseindices); /* XXX probably bad */
        COPY_SCALAR_FIELD(eval_cost);
        COPY_SCALAR_FIELD(this_selec);
+       COPY_INTLIST_FIELD(left_relids);
+       COPY_INTLIST_FIELD(right_relids);
        COPY_SCALAR_FIELD(mergejoinoperator);
        COPY_SCALAR_FIELD(left_sortop);
        COPY_SCALAR_FIELD(right_sortop);
index 0ef9b3fa2209a20c8d1afae7770c8208ec098a30..a4e9e1092d804781dab5daa978f31d37eeb9c7a3 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.179 2003/01/10 21:08:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.180 2003/01/15 19:35:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -464,10 +464,10 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
        COMPARE_NODE_FIELD(clause);
        COMPARE_SCALAR_FIELD(ispusheddown);
        /*
-        * We ignore subclauseindices, eval_cost, this_selec, left/right_pathkey,
-        * and left/right_bucketsize, since they may not be set yet, and should be
-        * derivable from the clause anyway.  Probably it's not really necessary
-        * to compare any of these remaining fields ...
+        * We ignore subclauseindices, eval_cost, this_selec, left/right_relids,
+        * left/right_pathkey, and left/right_bucketsize, since they may not be
+        * set yet, and should be derivable from the clause anyway.  Probably it's
+        * not really necessary to compare any of these remaining fields ...
         */
        COMPARE_SCALAR_FIELD(mergejoinoperator);
        COMPARE_SCALAR_FIELD(left_sortop);
index e7d8fa71ed78ea0b41c30affb6dce7ee9d13ca14..e72b52570e56b22fa1ec343a5b1b294b02036f05 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.192 2003/01/10 21:08:11 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.193 2003/01/15 19:35:39 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -952,6 +952,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
        WRITE_NODE_FIELD(clause);
        WRITE_BOOL_FIELD(ispusheddown);
        WRITE_NODE_FIELD(subclauseindices);
+       WRITE_INTLIST_FIELD(left_relids);
+       WRITE_INTLIST_FIELD(right_relids);
        WRITE_OID_FIELD(mergejoinoperator);
        WRITE_OID_FIELD(left_sortop);
        WRITE_OID_FIELD(right_sortop);
index dd5186860bfe33fb37ed3427e60904ef24ec3177..43b8e99893cef3db4a24d28e5807ffc00e29025e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.58 2002/12/12 15:49:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.59 2003/01/15 19:35:39 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -370,10 +370,10 @@ print_expr(Node *expr, List *rtable)
                {
                        char       *opname;
 
-                       print_expr((Node *) get_leftop(e), rtable);
+                       print_expr(get_leftop(e), rtable);
                        opname = get_opname(((OpExpr *) e)->opno);
                        printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
-                       print_expr((Node *) get_rightop(e), rtable);
+                       print_expr(get_rightop(e), rtable);
                }
                else
                        printf("an expr");
index f4d64ebbcadfc5fddcf8cc0b6df359e4faba5b78..955e022d8f6d5bd0b34c838c2b1825c9f9098af1 100644 (file)
@@ -251,8 +251,10 @@ Optimizer Data Structures
 
 RelOptInfo      - a relation or joined relations
 
- RestrictInfo   - restriction clauses, like "x = 3"
- JoinInfo       - join clauses, including the relids needed for the join
+ RestrictInfo   - WHERE clauses, like "x = 3" or "y = z"
+                  (note the same structure is used for restriction and
+                   join clauses)
+ JoinInfo       - join clauses associated with a particular pair of relations
 
  Path           - every way to generate a RelOptInfo(sequential,index,joins)
   SeqScan       - a plain Path node with pathtype = T_SeqScan
index 0294c828124e4ea9ee07a1337264e3105c8450bc..84041a566d18b3fc7fed3e7d8985143e87475842 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.54 2002/12/12 15:49:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.55 2003/01/15 19:35:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -266,12 +266,12 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause,
 
        if (varonleft)
        {
-               var = (Node *) get_leftop((Expr *) clause);
+               var = get_leftop((Expr *) clause);
                is_lobound = !isLTsel;  /* x < something is high bound */
        }
        else
        {
-               var = (Node *) get_rightop((Expr *) clause);
+               var = get_rightop((Expr *) clause);
                is_lobound = isLTsel;   /* something < x is low bound */
        }
 
index 1d736b34b91102c615c3924a58e3b13148e732d7..efd80dff1ed7a6ffc46b59dee1164ce3198a1ede 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.99 2003/01/12 22:35:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.100 2003/01/15 19:35:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -752,7 +752,6 @@ cost_mergejoin(Path *path, Query *root,
        Cost            cpu_per_tuple;
        QualCost        restrict_qual_cost;
        RestrictInfo *firstclause;
-       Var                *leftvar;
        double          outer_rows,
                                inner_rows;
        double          ntuples;
@@ -779,9 +778,7 @@ cost_mergejoin(Path *path, Query *root,
                                                 &firstclause->left_mergescansel,
                                                 &firstclause->right_mergescansel);
 
-       leftvar = get_leftop(firstclause->clause);
-       Assert(IsA(leftvar, Var));
-       if (VARISRELMEMBER(leftvar->varno, outer_path->parent))
+       if (is_subseti(firstclause->left_relids, outer_path->parent->relids))
        {
                /* left side of clause is outer */
                outerscansel = firstclause->left_mergescansel;
@@ -935,14 +932,9 @@ cost_hashjoin(Path *path, Query *root,
        foreach(hcl, hashclauses)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl);
-               Var                *left,
-                                  *right;
                Selectivity thisbucketsize;
 
                Assert(IsA(restrictinfo, RestrictInfo));
-               /* these must be OK, since check_hashjoinable accepted the clause */
-               left = get_leftop(restrictinfo->clause);
-               right = get_rightop(restrictinfo->clause);
 
                /*
                 * First we have to figure out which side of the hashjoin clause is the
@@ -952,27 +944,30 @@ cost_hashjoin(Path *path, Query *root,
                 * a large query, we cache the bucketsize estimate in the RestrictInfo
                 * node to avoid repeated lookups of statistics.
                 */
-               if (VARISRELMEMBER(right->varno, inner_path->parent))
+               if (is_subseti(restrictinfo->right_relids, inner_path->parent->relids))
                {
                        /* righthand side is inner */
                        thisbucketsize = restrictinfo->right_bucketsize;
                        if (thisbucketsize < 0)
                        {
                                /* not cached yet */
-                               thisbucketsize = estimate_hash_bucketsize(root, right,
+                               thisbucketsize = estimate_hash_bucketsize(root,
+                                                                       (Var *) get_rightop(restrictinfo->clause),
                                                                                                                  virtualbuckets);
                                restrictinfo->right_bucketsize = thisbucketsize;
                        }
                }
                else
                {
-                       Assert(VARISRELMEMBER(left->varno, inner_path->parent));
+                       Assert(is_subseti(restrictinfo->left_relids,
+                                                         inner_path->parent->relids));
                        /* lefthand side is inner */
                        thisbucketsize = restrictinfo->left_bucketsize;
                        if (thisbucketsize < 0)
                        {
                                /* not cached yet */
-                               thisbucketsize = estimate_hash_bucketsize(root, left,
+                               thisbucketsize = estimate_hash_bucketsize(root,
+                                                                       (Var *) get_leftop(restrictinfo->clause),
                                                                                                                  virtualbuckets);
                                restrictinfo->left_bucketsize = thisbucketsize;
                        }
@@ -1088,7 +1083,7 @@ estimate_hash_bucketsize(Query *root, Var *var, int nbuckets)
         * Lookup info about var's relation and attribute; if none available,
         * return default estimate.
         */
-       if (!IsA(var, Var))
+       if (var == NULL || !IsA(var, Var))
                return 0.1;
 
        relid = getrelid(var->varno, root->rtable);
index 228a876f71adebd3a88582a0a07c062abab185ec..7e68c41ef37ae5f345461f1e4676457940e0d856 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.130 2002/12/16 21:30:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.131 2003/01/15 19:35:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -85,15 +85,15 @@ static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
 static Path *make_innerjoin_index_path(Query *root,
                                                                           RelOptInfo *rel, IndexOptInfo *index,
                                                                           List *clausegroup);
-static bool match_index_to_operand(int indexkey, Var *operand,
+static bool match_index_to_operand(int indexkey, Node *operand,
                                           RelOptInfo *rel, IndexOptInfo *index);
 static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
                                           IndexOptInfo *index);
 static bool match_special_index_operator(Expr *clause, Oid opclass,
                                                         bool indexkey_on_left);
-static List *prefix_quals(Var *leftop, Oid expr_op,
+static List *prefix_quals(Node *leftop, Oid expr_op,
                         Const *prefix, Pattern_Prefix_Status pstatus);
-static List *network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop);
+static List *network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop);
 static Oid     find_operator(const char *opname, Oid datatype);
 static Datum string_to_datum(const char *str, Oid datatype);
 static Const *string_to_const(const char *str, Oid datatype);
@@ -713,7 +713,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
                                                 Oid opclass,
                                                 Expr *clause)
 {
-       Var                *leftop,
+       Node       *leftop,
                           *rightop;
 
        /* Clause must be a binary opclause. */
@@ -730,7 +730,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
         * Anything that is a "pseudo constant" expression will do.
         */
        if (match_index_to_operand(indexkey, leftop, rel, index) &&
-               is_pseudo_constant_clause((Node *) rightop))
+               is_pseudo_constant_clause(rightop))
        {
                if (is_indexable_operator(clause, opclass, true))
                        return true;
@@ -745,7 +745,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
        }
 
        if (match_index_to_operand(indexkey, rightop, rel, index) &&
-               is_pseudo_constant_clause((Node *) leftop))
+               is_pseudo_constant_clause(leftop))
        {
                if (is_indexable_operator(clause, opclass, false))
                        return true;
@@ -801,7 +801,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel,
                                                          Oid opclass,
                                                          Expr *clause)
 {
-       Var                *leftop,
+       Node       *leftop,
                           *rightop;
 
        /* Clause must be a binary opclause. */
@@ -820,12 +820,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel,
         */
        if (match_index_to_operand(indexkey, leftop, rel, index))
        {
-               List       *othervarnos = pull_varnos((Node *) rightop);
+               List       *othervarnos = pull_varnos(rightop);
                bool            isIndexable;
 
                isIndexable =
                        !intMember(lfirsti(rel->relids), othervarnos) &&
-                       !contain_volatile_functions((Node *) rightop) &&
+                       !contain_volatile_functions(rightop) &&
                        is_indexable_operator(clause, opclass, true);
                freeList(othervarnos);
                return isIndexable;
@@ -833,12 +833,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel,
 
        if (match_index_to_operand(indexkey, rightop, rel, index))
        {
-               List       *othervarnos = pull_varnos((Node *) leftop);
+               List       *othervarnos = pull_varnos(leftop);
                bool            isIndexable;
 
                isIndexable =
                        !intMember(lfirsti(rel->relids), othervarnos) &&
-                       !contain_volatile_functions((Node *) leftop) &&
+                       !contain_volatile_functions(leftop) &&
                        is_indexable_operator(clause, opclass, false);
                freeList(othervarnos);
                return isIndexable;
@@ -1622,7 +1622,7 @@ make_innerjoin_index_path(Query *root,
  */
 static bool
 match_index_to_operand(int indexkey,
-                                          Var *operand,
+                                          Node *operand,
                                           RelOptInfo *rel,
                                           IndexOptInfo *index)
 {
@@ -1633,7 +1633,7 @@ match_index_to_operand(int indexkey,
         * eval_const_expressions() will have simplified if more than one.
         */
        if (operand && IsA(operand, RelabelType))
-               operand = (Var *) ((RelabelType *) operand)->arg;
+               operand = (Node *) ((RelabelType *) operand)->arg;
 
        if (index->indproc == InvalidOid)
        {
@@ -1641,8 +1641,8 @@ match_index_to_operand(int indexkey,
                 * Simple index.
                 */
                if (operand && IsA(operand, Var) &&
-                       lfirsti(rel->relids) == operand->varno &&
-                       indexkey == operand->varattno)
+                       lfirsti(rel->relids) == ((Var *) operand)->varno &&
+                       indexkey == ((Var *) operand)->varattno)
                        return true;
                else
                        return false;
@@ -1764,7 +1764,7 @@ match_special_index_operator(Expr *clause, Oid opclass,
                                                         bool indexkey_on_left)
 {
        bool            isIndexable = false;
-       Var                *leftop,
+       Node       *leftop,
                           *rightop;
        Oid                     expr_op;
        Const      *patt = NULL;
@@ -1944,8 +1944,8 @@ expand_indexqual_conditions(List *indexquals)
                Expr       *clause = (Expr *) lfirst(q);
 
                /* we know these will succeed */
-               Var                *leftop = get_leftop(clause);
-               Var                *rightop = get_rightop(clause);
+               Node       *leftop = get_leftop(clause);
+               Node       *rightop = get_rightop(clause);
                Oid                     expr_op = ((OpExpr *) clause)->opno;
                Const      *patt = (Const *) rightop;
                Const      *prefix = NULL;
@@ -2033,7 +2033,7 @@ expand_indexqual_conditions(List *indexquals)
  * operators.
  */
 static List *
-prefix_quals(Var *leftop, Oid expr_op,
+prefix_quals(Node *leftop, Oid expr_op,
                         Const *prefix_const, Pattern_Prefix_Status pstatus)
 {
        List       *result;
@@ -2143,7 +2143,7 @@ prefix_quals(Var *leftop, Oid expr_op,
  * operator.
  */
 static List *
-network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop)
+network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop)
 {
        bool            is_eq;
        char       *opr1name;
index 65d0d8fa3581216a3a8fe4124fab6183348c5318..8a6fcd3f060215a6a4394e6dbe1447f8122faef6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.74 2002/11/30 05:21:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.75 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -774,10 +774,9 @@ hash_inner_and_outer(Query *root,
        foreach(i, restrictlist)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
-               Var                *left,
-                                  *right;
 
-               if (restrictinfo->hashjoinoperator == InvalidOid)
+               if (restrictinfo->left_relids == NIL ||
+                       restrictinfo->hashjoinoperator == InvalidOid)
                        continue;                       /* not hashjoinable */
 
                /*
@@ -787,26 +786,16 @@ hash_inner_and_outer(Query *root,
                if (isouterjoin && restrictinfo->ispusheddown)
                        continue;
 
-               /* these must be OK, since check_hashjoinable accepted the clause */
-               left = get_leftop(restrictinfo->clause);
-               right = get_rightop(restrictinfo->clause);
-
                /*
                 * Check if clause is usable with these input rels.
-                *
-                * Since we currently accept only var-op-var clauses as hashjoinable,
-                * we need only check the membership of the vars to determine whether
-                * a particular clause can be used with this pair of sub-relations.
-                * This code would need to be upgraded if we wanted to allow
-                * more-complex expressions in hash joins.
                 */
-               if (VARISRELMEMBER(left->varno, outerrel) &&
-                       VARISRELMEMBER(right->varno, innerrel))
+               if (is_subseti(restrictinfo->left_relids, outerrel->relids) &&
+                       is_subseti(restrictinfo->right_relids, innerrel->relids))
                {
                        /* righthand side is inner */
                }
-               else if (VARISRELMEMBER(left->varno, innerrel) &&
-                                VARISRELMEMBER(right->varno, outerrel))
+               else if (is_subseti(restrictinfo->left_relids, innerrel->relids) &&
+                                is_subseti(restrictinfo->right_relids, outerrel->relids))
                {
                        /* lefthand side is inner */
                }
@@ -874,9 +863,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
        foreach(i, restrictlist)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
-               Expr       *clause;
-               Var                *left,
-                                  *right;
 
                /*
                 * If processing an outer join, only use its own join clauses in
@@ -896,11 +882,13 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                        switch (jointype)
                        {
                                case JOIN_RIGHT:
-                                       if (restrictinfo->mergejoinoperator == InvalidOid)
+                                       if (restrictinfo->left_relids == NIL ||
+                                               restrictinfo->mergejoinoperator == InvalidOid)
                                                return NIL;             /* not mergejoinable */
                                        break;
                                case JOIN_FULL:
-                                       if (restrictinfo->mergejoinoperator == InvalidOid)
+                                       if (restrictinfo->left_relids == NIL ||
+                                               restrictinfo->mergejoinoperator == InvalidOid)
                                                elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
                                        break;
                                default:
@@ -909,19 +897,27 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                        }
                }
 
-               if (restrictinfo->mergejoinoperator == InvalidOid)
+               if (restrictinfo->left_relids == NIL ||
+                       restrictinfo->mergejoinoperator == InvalidOid)
                        continue;                       /* not mergejoinable */
 
-               clause = restrictinfo->clause;
-               /* these must be OK, since check_mergejoinable accepted the clause */
-               left = get_leftop(clause);
-               right = get_rightop(clause);
+               /*
+                * Check if clause is usable with these input rels.
+                */
+               if (is_subseti(restrictinfo->left_relids, outerrel->relids) &&
+                       is_subseti(restrictinfo->right_relids, innerrel->relids))
+               {
+                       /* righthand side is inner */
+               }
+               else if (is_subseti(restrictinfo->left_relids, innerrel->relids) &&
+                                is_subseti(restrictinfo->right_relids, outerrel->relids))
+               {
+                       /* lefthand side is inner */
+               }
+               else
+                       continue;                       /* no good for these input relations */
 
-               if ((VARISRELMEMBER(left->varno, outerrel) &&
-                        VARISRELMEMBER(right->varno, innerrel)) ||
-                       (VARISRELMEMBER(left->varno, innerrel) &&
-                        VARISRELMEMBER(right->varno, outerrel)))
-                       result_list = lcons(restrictinfo, result_list);
+               result_list = lcons(restrictinfo, result_list);
        }
 
        return result_list;
index 9c6ed4d1bd35422c2ffb69ca16f9a46f03e41eae..194bdddc2f09c1b151b3f64053331e9c3b0e7157 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.43 2002/12/17 01:18:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,27 +53,23 @@ makePathKeyItem(Node *key, Oid sortop)
  *       The given clause has a mergejoinable operator, so its two sides
  *       can be considered equal after restriction clause application; in
  *       particular, any pathkey mentioning one side (with the correct sortop)
- *       can be expanded to include the other as well.  Record the vars and
+ *       can be expanded to include the other as well.  Record the exprs and
  *       associated sortops in the query's equi_key_list for future use.
  *
  * The query's equi_key_list field points to a list of sublists of PathKeyItem
- * nodes, where each sublist is a set of two or more vars+sortops that have
+ * nodes, where each sublist is a set of two or more exprs+sortops that have
  * been identified as logically equivalent (and, therefore, we may consider
  * any two in a set to be equal).  As described above, we will subsequently
  * use direct pointers to one of these sublists to represent any pathkey
  * that involves an equijoined variable.
- *
- * This code would actually work fine with expressions more complex than
- * a single Var, but currently it won't see any because check_mergejoinable
- * won't accept such clauses as mergejoinable.
  */
 void
 add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
 {
        Expr       *clause = restrictinfo->clause;
-       PathKeyItem *item1 = makePathKeyItem((Node *) get_leftop(clause),
+       PathKeyItem *item1 = makePathKeyItem(get_leftop(clause),
                                                                                 restrictinfo->left_sortop);
-       PathKeyItem *item2 = makePathKeyItem((Node *) get_rightop(clause),
+       PathKeyItem *item2 = makePathKeyItem(get_rightop(clause),
                                                                                 restrictinfo->right_sortop);
        List       *newset,
                           *cursetlink;
@@ -717,13 +713,13 @@ cache_mergeclause_pathkeys(Query *root, RestrictInfo *restrictinfo)
 
        if (restrictinfo->left_pathkey == NIL)
        {
-               key = (Node *) get_leftop(restrictinfo->clause);
+               key = get_leftop(restrictinfo->clause);
                item = makePathKeyItem(key, restrictinfo->left_sortop);
                restrictinfo->left_pathkey = make_canonical_pathkey(root, item);
        }
        if (restrictinfo->right_pathkey == NIL)
        {
-               key = (Node *) get_rightop(restrictinfo->clause);
+               key = get_rightop(restrictinfo->clause);
                item = makePathKeyItem(key, restrictinfo->right_sortop);
                restrictinfo->right_pathkey = make_canonical_pathkey(root, item);
        }
@@ -852,32 +848,24 @@ make_pathkeys_for_mergeclauses(Query *root,
        foreach(i, mergeclauses)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
-               Node       *key;
                List       *pathkey;
 
                cache_mergeclause_pathkeys(root, restrictinfo);
 
-               key = (Node *) get_leftop(restrictinfo->clause);
-               if (IsA(key, Var) &&
-                       VARISRELMEMBER(((Var *) key)->varno, rel))
+               if (is_subseti(restrictinfo->left_relids, rel->relids))
                {
                        /* Rel is left side of mergeclause */
                        pathkey = restrictinfo->left_pathkey;
                }
+               else if (is_subseti(restrictinfo->right_relids, rel->relids))
+               {
+                       /* Rel is right side of mergeclause */
+                       pathkey = restrictinfo->right_pathkey;
+               }
                else
                {
-                       key = (Node *) get_rightop(restrictinfo->clause);
-                       if (IsA(key, Var) &&
-                               VARISRELMEMBER(((Var *) key)->varno, rel))
-                       {
-                               /* Rel is right side of mergeclause */
-                               pathkey = restrictinfo->right_pathkey;
-                       }
-                       else
-                       {
-                               elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use");
-                               pathkey = NIL;  /* keep compiler quiet */
-                       }
+                       elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use");
+                       pathkey = NIL;  /* keep compiler quiet */
                }
 
                /*
index 3d9ee6e4f4f6313c9166c36dcf59880f2acd4c2f..03ec35384797f41dfb0a5618f3dbfaf146b6ee93 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.129 2003/01/13 00:29:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.130 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,18 +49,15 @@ static FunctionScan *create_functionscan_plan(Path *best_path,
 static NestLoop *create_nestloop_plan(Query *root,
                                         NestPath *best_path, List *tlist,
                                         List *joinclauses, List *otherclauses,
-                                        Plan *outer_plan, List *outer_tlist,
-                                        Plan *inner_plan, List *inner_tlist);
+                                        Plan *outer_plan, Plan *inner_plan);
 static MergeJoin *create_mergejoin_plan(Query *root,
                                          MergePath *best_path, List *tlist,
                                          List *joinclauses, List *otherclauses,
-                                         Plan *outer_plan, List *outer_tlist,
-                                         Plan *inner_plan, List *inner_tlist);
+                                         Plan *outer_plan, Plan *inner_plan);
 static HashJoin *create_hashjoin_plan(Query *root,
                                         HashPath *best_path, List *tlist,
                                         List *joinclauses, List *otherclauses,
-                                        Plan *outer_plan, List *outer_tlist,
-                                        Plan *inner_plan, List *inner_tlist);
+                                        Plan *outer_plan, Plan *inner_plan);
 static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
                                                List **fixed_indexquals,
                                                List **recheck_indexquals);
@@ -70,7 +67,7 @@ static void fix_indxqual_sublist(List *indexqual, int baserelid,
 static Node *fix_indxqual_operand(Node *node, int baserelid,
                                         IndexOptInfo *index,
                                         Oid *opclass);
-static List *switch_outer(List *clauses);
+static List *get_switched_clauses(List *clauses, List *outerrelids);
 static List *order_qual_clauses(Query *root, List *clauses);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
@@ -98,6 +95,9 @@ static MergeJoin *make_mergejoin(List *tlist,
                           List *mergeclauses,
                           Plan *lefttree, Plan *righttree,
                           JoinType jointype);
+static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree,
+                                                                        List *relids, List *pathkeys);
+
 
 /*
  * create_plan
@@ -246,18 +246,13 @@ create_join_plan(Query *root, JoinPath *best_path)
 {
        List       *join_tlist = best_path->path.parent->targetlist;
        Plan       *outer_plan;
-       List       *outer_tlist;
        Plan       *inner_plan;
-       List       *inner_tlist;
        List       *joinclauses;
        List       *otherclauses;
        Join       *plan;
 
        outer_plan = create_plan(root, best_path->outerjoinpath);
-       outer_tlist = outer_plan->targetlist;
-
        inner_plan = create_plan(root, best_path->innerjoinpath);
-       inner_tlist = inner_plan->targetlist;
 
        if (IS_OUTER_JOIN(best_path->jointype))
        {
@@ -280,9 +275,7 @@ create_join_plan(Query *root, JoinPath *best_path)
                                                                                                  joinclauses,
                                                                                                  otherclauses,
                                                                                                  outer_plan,
-                                                                                                 outer_tlist,
-                                                                                                 inner_plan,
-                                                                                                 inner_tlist);
+                                                                                                 inner_plan);
                        break;
                case T_HashJoin:
                        plan = (Join *) create_hashjoin_plan(root,
@@ -291,9 +284,7 @@ create_join_plan(Query *root, JoinPath *best_path)
                                                                                                 joinclauses,
                                                                                                 otherclauses,
                                                                                                 outer_plan,
-                                                                                                outer_tlist,
-                                                                                                inner_plan,
-                                                                                                inner_tlist);
+                                                                                                inner_plan);
                        break;
                case T_NestLoop:
                        plan = (Join *) create_nestloop_plan(root,
@@ -302,9 +293,7 @@ create_join_plan(Query *root, JoinPath *best_path)
                                                                                                 joinclauses,
                                                                                                 otherclauses,
                                                                                                 outer_plan,
-                                                                                                outer_tlist,
-                                                                                                inner_plan,
-                                                                                                inner_tlist);
+                                                                                                inner_plan);
                        break;
                default:
                        elog(ERROR, "create_join_plan: unknown node type: %d",
@@ -672,10 +661,9 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
  *
  * A cleaner solution would be to not call join_references() here at all,
  * but leave it for setrefs.c to do at the end of plan tree construction.
- * But that would make switch_outer() much more complicated, and some care
- * would be needed to get setrefs.c to do the right thing with nestloop
- * inner indexscan quals.  So, we do subplan reference adjustment here for
- * quals of join nodes (and *only* for quals of join nodes).
+ * But some care would be needed to get setrefs.c to do the right thing with
+ * nestloop inner indexscan quals.  So, we do subplan reference adjustment
+ * here for quals of join nodes (and *only* for quals of join nodes).
  *
  *****************************************************************************/
 
@@ -686,10 +674,10 @@ create_nestloop_plan(Query *root,
                                         List *joinclauses,
                                         List *otherclauses,
                                         Plan *outer_plan,
-                                        List *outer_tlist,
-                                        Plan *inner_plan,
-                                        List *inner_tlist)
+                                        Plan *inner_plan)
 {
+       List       *outer_tlist = outer_plan->targetlist;
+       List       *inner_tlist = inner_plan->targetlist;
        NestLoop   *join_plan;
 
        if (IsA(inner_plan, IndexScan))
@@ -797,44 +785,45 @@ create_mergejoin_plan(Query *root,
                                          List *joinclauses,
                                          List *otherclauses,
                                          Plan *outer_plan,
-                                         List *outer_tlist,
-                                         Plan *inner_plan,
-                                         List *inner_tlist)
+                                         Plan *inner_plan)
 {
+       List       *outer_tlist = outer_plan->targetlist;
+       List       *inner_tlist = inner_plan->targetlist;
        List       *mergeclauses;
        MergeJoin  *join_plan;
 
+       /*
+        * Remove the mergeclauses from the list of join qual clauses, leaving
+        * the list of quals that must be checked as qpquals.
+        */
        mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
+       joinclauses = set_difference(joinclauses, mergeclauses);
 
        /*
-        * Remove the mergeclauses from the list of join qual clauses, leaving
-        * the list of quals that must be checked as qpquals. Set those
-        * clauses to contain INNER/OUTER var references.
+        * Rearrange mergeclauses, if needed, so that the outer variable
+        * is always on the left.
         */
-       joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+       mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
+                                                                               best_path->jpath.outerjoinpath->parent->relids);
+
+       /*
+        * Fix all the join clauses to contain INNER/OUTER var references.
+        */
+       joinclauses = join_references(joinclauses,
                                                                  root->rtable,
                                                                  outer_tlist,
                                                                  inner_tlist,
                                                                  (Index) 0);
-
-       /*
-        * Fix the additional qpquals too.
-        */
        otherclauses = join_references(otherclauses,
                                                                   root->rtable,
                                                                   outer_tlist,
                                                                   inner_tlist,
                                                                   (Index) 0);
-
-       /*
-        * Now set the references in the mergeclauses and rearrange them so
-        * that the outer variable is always on the left.
-        */
-       mergeclauses = switch_outer(join_references(mergeclauses,
-                                                                                               root->rtable,
-                                                                                               outer_tlist,
-                                                                                               inner_tlist,
-                                                                                               (Index) 0));
+       mergeclauses = join_references(mergeclauses,
+                                                                  root->rtable,
+                                                                  outer_tlist,
+                                                                  inner_tlist,
+                                                                  (Index) 0);
 
        /*
         * Create explicit sort nodes for the outer and inner join paths if
@@ -843,15 +832,15 @@ create_mergejoin_plan(Query *root,
        if (best_path->outersortkeys)
                outer_plan = (Plan *)
                        make_sort_from_pathkeys(root,
-                                                                       outer_tlist,
                                                                        outer_plan,
+                                                                       best_path->jpath.outerjoinpath->parent->relids,
                                                                        best_path->outersortkeys);
 
        if (best_path->innersortkeys)
                inner_plan = (Plan *)
                        make_sort_from_pathkeys(root,
-                                                                       inner_tlist,
                                                                        inner_plan,
+                                                                       best_path->jpath.innerjoinpath->parent->relids,
                                                                        best_path->innersortkeys);
 
        /*
@@ -877,47 +866,48 @@ create_hashjoin_plan(Query *root,
                                         List *joinclauses,
                                         List *otherclauses,
                                         Plan *outer_plan,
-                                        List *outer_tlist,
-                                        Plan *inner_plan,
-                                        List *inner_tlist)
+                                        Plan *inner_plan)
 {
+       List       *outer_tlist = outer_plan->targetlist;
+       List       *inner_tlist = inner_plan->targetlist;
        List       *hashclauses;
        HashJoin   *join_plan;
        Hash       *hash_plan;
        List       *innerhashkeys;
        List       *hcl;
 
+       /*
+        * Remove the hashclauses from the list of join qual clauses, leaving
+        * the list of quals that must be checked as qpquals.
+        */
        hashclauses = get_actual_clauses(best_path->path_hashclauses);
+       joinclauses = set_difference(joinclauses, hashclauses);
 
        /*
-        * Remove the hashclauses from the list of join qual clauses, leaving
-        * the list of quals that must be checked as qpquals. Set those
-        * clauses to contain INNER/OUTER var references.
+        * Rearrange hashclauses, if needed, so that the outer variable
+        * is always on the left.
+        */
+       hashclauses = get_switched_clauses(best_path->path_hashclauses,
+                                                                          best_path->jpath.outerjoinpath->parent->relids);
+
+       /*
+        * Fix all the join clauses to contain INNER/OUTER var references.
         */
-       joinclauses = join_references(set_difference(joinclauses, hashclauses),
+       joinclauses = join_references(joinclauses,
                                                                  root->rtable,
                                                                  outer_tlist,
                                                                  inner_tlist,
                                                                  (Index) 0);
-
-       /*
-        * Fix the additional qpquals too.
-        */
        otherclauses = join_references(otherclauses,
                                                                   root->rtable,
                                                                   outer_tlist,
                                                                   inner_tlist,
                                                                   (Index) 0);
-
-       /*
-        * Now set the references in the hashclauses and rearrange them so
-        * that the outer variable is always on the left.
-        */
-       hashclauses = switch_outer(join_references(hashclauses,
-                                                                                          root->rtable,
-                                                                                          outer_tlist,
-                                                                                          inner_tlist,
-                                                                                          (Index) 0));
+       hashclauses = join_references(hashclauses,
+                                                                 root->rtable,
+                                                                 outer_tlist,
+                                                                 inner_tlist,
+                                                                 (Index) 0);
 
        /*
         * Extract the inner hash keys (right-hand operands of the hashclauses)
@@ -1154,27 +1144,26 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
 }
 
 /*
- * switch_outer
- *       Given a list of merge or hash joinclauses, rearrange the elements within
- *       the clauses so the outer join variable is on the left and the inner is
- *       on the right.  The original list is not touched; a modified list
- *       is returned.
+ * get_switched_clauses
+ *       Given a list of merge or hash joinclauses (as RestrictInfo nodes),
+ *       extract the bare clauses, and rearrange the elements within the
+ *       clauses, if needed, so the outer join variable is on the left and
+ *       the inner is on the right.  The original data structure is not touched;
+ *       a modified list is returned.
  */
 static List *
-switch_outer(List *clauses)
+get_switched_clauses(List *clauses, List *outerrelids)
 {
        List       *t_list = NIL;
        List       *i;
 
        foreach(i, clauses)
        {
-               OpExpr     *clause = (OpExpr *) lfirst(i);
-               Var                *op;
+               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
+               OpExpr     *clause = (OpExpr *) restrictinfo->clause;
 
                Assert(is_opclause(clause));
-               op = get_rightop((Expr *) clause);
-               Assert(op && IsA(op, Var));
-               if (var_is_outer(op))
+               if (is_subseti(restrictinfo->right_relids, outerrelids))
                {
                        /*
                         * Duplicate just enough of the structure to allow commuting
@@ -1554,17 +1543,24 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
  * make_sort_from_pathkeys
  *       Create sort plan to sort according to given pathkeys
  *
- *       'tlist' is the target list of the input plan
  *       'lefttree' is the node which yields input tuples
+ *       'relids' is the set of relids represented by the input node
  *       'pathkeys' is the list of pathkeys by which the result is to be sorted
  *
  * We must convert the pathkey information into reskey and reskeyop fields
  * of resdom nodes in the sort plan's target list.
+ *
+ * If the pathkeys include expressions that aren't simple Vars, we will
+ * usually need to add resjunk items to the input plan's targetlist to
+ * compute these expressions (since the Sort node itself won't do it).
+ * If the input plan type isn't one that can do projections, this means
+ * adding a Result node just to do the projection.
  */
-Sort *
-make_sort_from_pathkeys(Query *root, List *tlist,
-                                               Plan *lefttree, List *pathkeys)
+static Sort *
+make_sort_from_pathkeys(Query *root, Plan *lefttree,
+                                               List *relids, List *pathkeys)
 {
+       List       *tlist = lefttree->targetlist;
        List       *sort_tlist;
        List       *i;
        int                     numsortkeys = 0;
@@ -1582,7 +1578,8 @@ make_sort_from_pathkeys(Query *root, List *tlist,
                /*
                 * We can sort by any one of the sort key items listed in this
                 * sublist.  For now, we take the first one that corresponds to an
-                * available Var in the sort_tlist.
+                * available Var in the sort_tlist.  If there isn't any, use the
+                * first one that is an expression in the input's vars.
                 *
                 * XXX if we have a choice, is there any way of figuring out which
                 * might be cheapest to execute?  (For example, int4lt is likely
@@ -1599,8 +1596,52 @@ make_sort_from_pathkeys(Query *root, List *tlist,
                                break;
                }
                if (!resdom)
-                       elog(ERROR, "make_sort_from_pathkeys: cannot find tlist item to sort");
-
+               {
+                       /* No matching Var; look for an expression */
+                       foreach(j, keysublist)
+                       {
+                               pathkey = lfirst(j);
+                               if (is_subseti(pull_varnos(pathkey->key), relids))
+                                       break;
+                       }
+                       if (!j)
+                               elog(ERROR, "make_sort_from_pathkeys: cannot find pathkey item to sort");
+                       /*
+                        * Do we need to insert a Result node?
+                        *
+                        * Currently, the only non-projection-capable plan type
+                        * we can see here is Append.
+                        */
+                       if (IsA(lefttree, Append))
+                       {
+                               tlist = new_unsorted_tlist(tlist);
+                               lefttree = (Plan *) make_result(tlist, NULL, lefttree);
+                       }
+                       /*
+                        * Add resjunk entry to input's tlist
+                        */
+                       resdom = makeResdom(length(tlist) + 1,
+                                                               exprType(pathkey->key),
+                                                               exprTypmod(pathkey->key),
+                                                               NULL,
+                                                               true);
+                       tlist = lappend(tlist,
+                                                       makeTargetEntry(resdom,
+                                                                                       (Expr *) pathkey->key));
+                       lefttree->targetlist = tlist; /* just in case NIL before */
+                       /*
+                        * Add one to sort node's tlist too.  This will be identical
+                        * except we are going to set the sort key info in it.
+                        */
+                       resdom = makeResdom(length(sort_tlist) + 1,
+                                                               exprType(pathkey->key),
+                                                               exprTypmod(pathkey->key),
+                                                               NULL,
+                                                               true);
+                       sort_tlist = lappend(sort_tlist,
+                                                                makeTargetEntry(resdom,
+                                                                                                (Expr *) pathkey->key));
+               }
                /*
                 * The resdom might be already marked as a sort key, if the
                 * pathkeys contain duplicate entries.  (This can happen in
index 1c1a848ea72dcc8be4c7b6ec0264f0d596a0352a..87c77e52fc37e2f063adfacbf261c89dcf42a47f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.80 2003/01/12 22:35:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.81 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,32 +60,24 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
  * add_base_rels_to_query
  *
  *       Scan the query's jointree and create baserel RelOptInfos for all
- *       the base relations (ie, table and subquery RTEs) appearing in the
- *       jointree.  Also, create otherrel RelOptInfos for join RTEs.
- *
- * The return value is a list of all the baserel indexes (but not join RTE
- * indexes) included in the scanned jointree.  This is actually just an
- * internal convenience for marking join otherrels properly; no outside
- * caller uses the result.
+ *       the base relations (ie, table, subquery, and function RTEs)
+ *       appearing in the jointree.
  *
  * At the end of this process, there should be one baserel RelOptInfo for
  * every non-join RTE that is used in the query.  Therefore, this routine
  * is the only place that should call build_base_rel.  But build_other_rel
- * will be used again later to build rels for inheritance children.
+ * will be used later to build rels for inheritance children.
  */
-List *
+void
 add_base_rels_to_query(Query *root, Node *jtnode)
 {
-       List       *result = NIL;
-
        if (jtnode == NULL)
-               return NIL;
+               return;
        if (IsA(jtnode, RangeTblRef))
        {
                int                     varno = ((RangeTblRef *) jtnode)->rtindex;
 
                build_base_rel(root, varno);
-               result = makeListi1(varno);
        }
        else if (IsA(jtnode, FromExpr))
        {
@@ -94,29 +86,15 @@ add_base_rels_to_query(Query *root, Node *jtnode)
 
                foreach(l, f->fromlist)
                {
-                       result = nconc(result,
-                                                  add_base_rels_to_query(root, lfirst(l)));
+                       add_base_rels_to_query(root, lfirst(l));
                }
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
-               RelOptInfo *jrel;
-
-               result = add_base_rels_to_query(root, j->larg);
-               result = nconc(result,
-                                          add_base_rels_to_query(root, j->rarg));
-               /* the join's own rtindex is NOT added to result */
-               jrel = build_other_rel(root, j->rtindex);
-
-               /*
-                * Mark the join's otherrel with outerjoinset = list of baserel
-                * ids included in the join.  Note we must copy here because
-                * result list is destructively modified by nconcs at higher
-                * levels.
-                */
-               jrel->outerjoinset = listCopy(result);
 
+               add_base_rels_to_query(root, j->larg);
+               add_base_rels_to_query(root, j->rarg);
                /*
                 * Safety check: join RTEs should not be SELECT FOR UPDATE targets
                 */
@@ -126,7 +104,6 @@ add_base_rels_to_query(Query *root, Node *jtnode)
        else
                elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
                         nodeTag(jtnode));
-       return result;
 }
 
 
@@ -154,11 +131,6 @@ build_base_rel_tlists(Query *root, List *tlist)
  * add_vars_to_targetlist
  *       For each variable appearing in the list, add it to the owning
  *       relation's targetlist if not already present.
- *
- * Note that join alias variables will be attached to the otherrel for
- * the join RTE.  They will later be transferred to the tlist of
- * the corresponding joinrel.  We will also cause entries to be made
- * for the Vars that the alias will eventually depend on.
  */
 static void
 add_vars_to_targetlist(Query *root, List *vars)
@@ -171,19 +143,6 @@ add_vars_to_targetlist(Query *root, List *vars)
                RelOptInfo *rel = find_base_rel(root, var->varno);
 
                add_var_to_tlist(rel, var);
-
-               if (rel->reloptkind == RELOPT_OTHER_JOIN_REL)
-               {
-                       /* Var is an alias */
-                       Node       *expansion;
-                       List       *varsused;
-
-                       expansion = flatten_join_alias_vars((Node *) var,
-                                                                                               root->rtable, true);
-                       varsused = pull_var_clause(expansion, false);
-                       add_vars_to_targetlist(root, varsused);
-                       freeList(varsused);
-               }
        }
 }
 
@@ -398,6 +357,8 @@ distribute_qual_to_rels(Query *root, Node *clause,
        restrictinfo->subclauseindices = NIL;
        restrictinfo->eval_cost.startup = -1; /* not computed until needed */
        restrictinfo->this_selec = -1;          /* not computed until needed */
+       restrictinfo->left_relids = NIL; /* set below, if join clause */
+       restrictinfo->right_relids = NIL;
        restrictinfo->mergejoinoperator = InvalidOid;
        restrictinfo->left_sortop = InvalidOid;
        restrictinfo->right_sortop = InvalidOid;
@@ -416,41 +377,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
        clause_get_relids_vars(clause, &relids, &vars);
 
        /*
-        * The clause might contain some join alias vars; if so, we want to
-        * remove the join otherrelids from relids and add the referent joins'
-        * scope lists instead (thus ensuring that the clause can be evaluated
-        * no lower than that join node).  We rely here on the marking done
-        * earlier by add_base_rels_to_query.
-        *
-        * We can combine this step with a cross-check that the clause contains
-        * no relids not within its scope.      If the first crosscheck succeeds,
-        * the clause contains no aliases and we needn't look more closely.
+        * Cross-check: clause should contain no relids not within its scope.
+        * Otherwise the parser messed up.
         */
        if (!is_subseti(relids, qualscope))
-       {
-               Relids          newrelids = NIL;
-               List       *relid;
-
-               foreach(relid, relids)
-               {
-                       RelOptInfo *rel = find_other_rel(root, lfirsti(relid));
-
-                       if (rel && rel->outerjoinset)
-                       {
-                               /* this relid is for a join RTE */
-                               newrelids = set_unioni(newrelids, rel->outerjoinset);
-                       }
-                       else
-                       {
-                               /* this relid is for a true baserel */
-                               newrelids = lappendi(newrelids, lfirsti(relid));
-                       }
-               }
-               relids = newrelids;
-               /* Now repeat the crosscheck */
-               if (!is_subseti(relids, qualscope))
-                       elog(ERROR, "JOIN qualification may not refer to other relations");
-       }
+               elog(ERROR, "JOIN qualification may not refer to other relations");
 
        /*
         * If the clause is variable-free, we force it to be evaluated at its
@@ -575,7 +506,27 @@ distribute_qual_to_rels(Query *root, Node *clause,
                /*
                 * 'clause' is a join clause, since there is more than one rel in
                 * the relid list.      Set additional RestrictInfo fields for
-                * joining.
+                * joining.  First, does it look like a normal join clause, i.e.,
+                * a binary operator relating expressions that come from distinct
+                * relations?  If so we might be able to use it in a join algorithm.
+                */
+               if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
+               {
+                       List       *left_relids;
+                       List       *right_relids;
+
+                       left_relids = pull_varnos(get_leftop((Expr *) clause));
+                       right_relids = pull_varnos(get_rightop((Expr *) clause));
+                       if (left_relids && right_relids &&
+                               nonoverlap_setsi(left_relids, right_relids))
+                       {
+                               restrictinfo->left_relids = left_relids;
+                               restrictinfo->right_relids = right_relids;
+                       }
+               }
+
+               /*
+                * Now check for hash or mergejoinable operators.
                 *
                 * We don't bother setting the hashjoin info if we're not going
                 * to need it.  We do want to know about mergejoinable ops in all
@@ -675,11 +626,6 @@ void
 process_implied_equality(Query *root, Node *item1, Node *item2,
                                                 Oid sortop1, Oid sortop2)
 {
-       Index           irel1;
-       Index           irel2;
-       RelOptInfo *rel1;
-       List       *restrictlist;
-       List       *itm;
        Oid                     ltype,
                                rtype;
        Operator        eq_operator;
@@ -687,50 +633,14 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
        Expr       *clause;
 
        /*
-        * Currently, since check_mergejoinable only accepts Var = Var
-        * clauses, we should only see Var nodes here.  Would have to work a
-        * little harder to locate the right rel(s) if more-general mergejoin
-        * clauses were accepted.
-        */
-       Assert(IsA(item1, Var));
-       irel1 = ((Var *) item1)->varno;
-       Assert(IsA(item2, Var));
-       irel2 = ((Var *) item2)->varno;
-
-       /*
-        * If both vars belong to same rel, we need to look at that rel's
-        * baserestrictinfo list.  If different rels, each will have a
-        * joininfo node for the other, and we can scan either list.
-        */
-       rel1 = find_base_rel(root, irel1);
-       if (irel1 == irel2)
-               restrictlist = rel1->baserestrictinfo;
-       else
-       {
-               JoinInfo   *joininfo = find_joininfo_node(rel1,
-                                                                                                 makeListi1(irel2));
-
-               restrictlist = joininfo->jinfo_restrictinfo;
-       }
-
-       /*
-        * Scan to see if equality is already known.
+        * Forget it if this equality is already recorded.
+        *
+        * Note: if only a single relation is involved, we may fall through
+        * here and end up rejecting the equality later on in qual_is_redundant.
+        * This is a tad slow but should be okay.
         */
-       foreach(itm, restrictlist)
-       {
-               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
-               Node       *left,
-                                  *right;
-
-               if (restrictinfo->mergejoinoperator == InvalidOid)
-                       continue;                       /* ignore non-mergejoinable clauses */
-               /* We now know the restrictinfo clause is a binary opclause */
-               left = (Node *) get_leftop(restrictinfo->clause);
-               right = (Node *) get_rightop(restrictinfo->clause);
-               if ((equal(item1, left) && equal(item2, right)) ||
-                       (equal(item2, left) && equal(item1, right)))
-                       return;                         /* found a matching clause */
-       }
+       if (exprs_known_equal(root, item1, item2))
+               return;
 
        /*
         * This equality is new information, so construct a clause
@@ -770,6 +680,8 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
        ReleaseSysCache(eq_operator);
 
        /*
+        * Push the new clause into all the appropriate restrictinfo lists.
+        *
         * Note: we mark the qual "pushed down" to ensure that it can never be
         * taken for an original JOIN/ON clause.
         */
@@ -779,44 +691,45 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
 }
 
 /*
- * vars_known_equal
- *       Detect whether two Vars are known equal due to equijoin clauses.
+ * exprs_known_equal
+ *       Detect whether two expressions are known equal due to equijoin clauses.
  *
  * This is not completely accurate since we avoid adding redundant restriction
  * clauses to individual base rels (see qual_is_redundant).  However, after
- * the implied-equality-deduction phase, it is complete for Vars of different
- * rels; that's sufficient for planned uses.
+ * the implied-equality-deduction phase, it is complete for expressions
+ * involving Vars of multiple rels; that's sufficient for planned uses.
  */
 bool
-vars_known_equal(Query *root, Var *var1, Var *var2)
+exprs_known_equal(Query *root, Node *item1, Node *item2)
 {
-       Index           irel1;
-       Index           irel2;
+       List       *relids;
        RelOptInfo *rel1;
        List       *restrictlist;
        List       *itm;
 
+       /* Get list of relids referenced in the two expressions */
+       relids = set_unioni(pull_varnos(item1), pull_varnos(item2));
+
        /*
-        * Would need more work here if we wanted to check for known equality
-        * of general clauses: there might be multiple base rels involved.
+        * If there are no Vars at all, say "true".  This prevents
+        * process_implied_equality from trying to store "const = const"
+        * deductions.
         */
-       Assert(IsA(var1, Var));
-       irel1 = var1->varno;
-       Assert(IsA(var2, Var));
-       irel2 = var2->varno;
+       if (relids == NIL)
+               return true;
 
        /*
-        * If both vars belong to same rel, we need to look at that rel's
-        * baserestrictinfo list.  If different rels, each will have a
-        * joininfo node for the other, and we can scan either list.
+        * If the exprs involve a single rel, we need to look at that rel's
+        * baserestrictinfo list.  If multiple rels, any one will have a
+        * joininfo node for the rest, and we can scan any of 'em.
         */
-       rel1 = find_base_rel(root, irel1);
-       if (irel1 == irel2)
+       rel1 = find_base_rel(root, lfirsti(relids));
+       relids = lnext(relids);
+       if (relids == NIL)
                restrictlist = rel1->baserestrictinfo;
        else
        {
-               JoinInfo   *joininfo = find_joininfo_node(rel1,
-                                                                                                 makeListi1(irel2));
+               JoinInfo   *joininfo = find_joininfo_node(rel1, relids);
 
                restrictlist = joininfo->jinfo_restrictinfo;
        }
@@ -833,10 +746,10 @@ vars_known_equal(Query *root, Var *var1, Var *var2)
                if (restrictinfo->mergejoinoperator == InvalidOid)
                        continue;                       /* ignore non-mergejoinable clauses */
                /* We now know the restrictinfo clause is a binary opclause */
-               left = (Node *) get_leftop(restrictinfo->clause);
-               right = (Node *) get_rightop(restrictinfo->clause);
-               if ((equal(var1, left) && equal(var2, right)) ||
-                       (equal(var2, left) && equal(var1, right)))
+               left = get_leftop(restrictinfo->clause);
+               right = get_rightop(restrictinfo->clause);
+               if ((equal(item1, left) && equal(item2, right)) ||
+                       (equal(item2, left) && equal(item1, right)))
                        return true;            /* found a matching clause */
        }
 
@@ -862,7 +775,7 @@ qual_is_redundant(Query *root,
        List       *olditem;
        Node       *newleft;
        Node       *newright;
-       List       *equalvars;
+       List       *equalexprs;
        bool            someadded;
 
        /*
@@ -898,15 +811,15 @@ qual_is_redundant(Query *root,
                return false;
 
        /*
-        * Now, we want to develop a list of Vars that are known equal to the
+        * Now, we want to develop a list of exprs that are known equal to the
         * left side of the new qual.  We traverse the old-quals list
-        * repeatedly to transitively expand the Vars list.  If at any point
-        * we find we can reach the right-side Var of the new qual, we are
-        * done.  We give up when we can't expand the equalvars list any more.
+        * repeatedly to transitively expand the exprs list.  If at any point
+        * we find we can reach the right-side expr of the new qual, we are
+        * done.  We give up when we can't expand the equalexprs list any more.
         */
-       newleft = (Node *) get_leftop(restrictinfo->clause);
-       newright = (Node *) get_rightop(restrictinfo->clause);
-       equalvars = makeList1(newleft);
+       newleft = get_leftop(restrictinfo->clause);
+       newright = get_rightop(restrictinfo->clause);
+       equalexprs = makeList1(newleft);
        do
        {
                someadded = false;
@@ -915,22 +828,22 @@ qual_is_redundant(Query *root,
                while (olditem)
                {
                        RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
-                       Node       *oldleft = (Node *) get_leftop(oldrinfo->clause);
-                       Node       *oldright = (Node *) get_rightop(oldrinfo->clause);
+                       Node       *oldleft = get_leftop(oldrinfo->clause);
+                       Node       *oldright = get_rightop(oldrinfo->clause);
                        Node       *newguy = NULL;
 
                        /* must advance olditem before lremove possibly pfree's it */
                        olditem = lnext(olditem);
 
-                       if (member(oldleft, equalvars))
+                       if (member(oldleft, equalexprs))
                                newguy = oldright;
-                       else if (member(oldright, equalvars))
+                       else if (member(oldright, equalexprs))
                                newguy = oldleft;
                        else
                                continue;
                        if (equal(newguy, newright))
                                return true;    /* we proved new clause is redundant */
-                       equalvars = lcons(newguy, equalvars);
+                       equalexprs = lcons(newguy, equalexprs);
                        someadded = true;
 
                        /*
@@ -956,39 +869,28 @@ qual_is_redundant(Query *root,
  *       info fields in the restrictinfo.
  *
  *       Currently, we support mergejoin for binary opclauses where
- *       both operands are simple Vars and the operator is a mergejoinable
- *       operator.
+ *       the operator is a mergejoinable operator.  The arguments can be
+ *       anything --- as long as there are no volatile functions in them.
  */
 static void
 check_mergejoinable(RestrictInfo *restrictinfo)
 {
        Expr       *clause = restrictinfo->clause;
-       Var                *left,
-                          *right;
        Oid                     opno,
                                leftOp,
                                rightOp;
 
        if (!is_opclause(clause))
                return;
-
-       left = get_leftop(clause);
-       right = get_rightop(clause);
-
-       /* caution: is_opclause accepts more than I do, so check it */
-       if (!right)
-               return;                                 /* unary opclauses need not apply */
-       if (!IsA(left, Var) ||
-               !IsA(right, Var))
+       if (length(((OpExpr *) clause)->args) != 2)
                return;
 
        opno = ((OpExpr *) clause)->opno;
 
        if (op_mergejoinable(opno,
-                                                left->vartype,
-                                                right->vartype,
                                                 &leftOp,
-                                                &rightOp))
+                                                &rightOp) &&
+               !contain_volatile_functions((Node *) clause))
        {
                restrictinfo->mergejoinoperator = opno;
                restrictinfo->left_sortop = leftOp;
@@ -1002,34 +904,23 @@ check_mergejoinable(RestrictInfo *restrictinfo)
  *       info fields in the restrictinfo.
  *
  *       Currently, we support hashjoin for binary opclauses where
- *       both operands are simple Vars and the operator is a hashjoinable
- *       operator.
+ *       the operator is a hashjoinable operator.  The arguments can be
+ *       anything --- as long as there are no volatile functions in them.
  */
 static void
 check_hashjoinable(RestrictInfo *restrictinfo)
 {
        Expr       *clause = restrictinfo->clause;
-       Var                *left,
-                          *right;
        Oid                     opno;
 
        if (!is_opclause(clause))
                return;
-
-       left = get_leftop(clause);
-       right = get_rightop(clause);
-
-       /* caution: is_opclause accepts more than I do, so check it */
-       if (!right)
-               return;                                 /* unary opclauses need not apply */
-       if (!IsA(left, Var) ||
-               !IsA(right, Var))
+       if (length(((OpExpr *) clause)->args) != 2)
                return;
 
        opno = ((OpExpr *) clause)->opno;
 
-       if (op_hashjoinable(opno,
-                                               left->vartype,
-                                               right->vartype))
+       if (op_hashjoinable(opno) &&
+               !contain_volatile_functions((Node *) clause))
                restrictinfo->hashjoinoperator = opno;
 }
index 5d2634bf82d883d55d87da2bb7c6cebdc447c4d2..6e265931eb226edf60ac26d06f93019fa32ed1aa 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.72 2002/11/21 00:42:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.73 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -117,7 +117,7 @@ query_planner(Query *root, List *tlist, double tuple_fraction,
        /*
         * Construct RelOptInfo nodes for all base relations in query.
         */
-       (void) add_base_rels_to_query(root, (Node *) root->jointree);
+       add_base_rels_to_query(root, (Node *) root->jointree);
 
        /*
         * Examine the targetlist and qualifications, adding entries to
index 76a16cd833d64cf7ac1198b4e4861c5a1f747120..4cf6a09acfecbcc1b117195d871ba66aaffaf187 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.138 2003/01/13 18:10:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.139 2003/01/15 19:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -559,20 +559,6 @@ is_simple_subquery(Query *subquery)
        if (expression_returns_set((Node *) subquery->targetList))
                return false;
 
-       /*
-        * Don't pull up a subquery that has any sublinks in its targetlist,
-        * either.  As of PG 7.3 this creates problems because the pulled-up
-        * expressions may go into join alias lists, and the sublinks would
-        * not get fixed because we do flatten_join_alias_vars() too late.
-        * Eventually we should do a complete flatten_join_alias_vars as the
-        * first step of preprocess_expression, and then we could probably
-        * support this.  (BUT: it might be a bad idea anyway, due to possibly
-        * causing multiple evaluations of an expensive sublink.)
-        */
-       if (subquery->hasSubLinks &&
-               contain_subplans((Node *) subquery->targetList))
-               return false;
-
        /*
         * Hack: don't try to pull up a subquery with an empty jointree.
         * query_planner() will correctly generate a Result plan for a
@@ -750,6 +736,14 @@ preprocess_jointree(Query *parse, Node *jtnode)
 static Node *
 preprocess_expression(Query *parse, Node *expr, int kind)
 {
+       /*
+        * If the query has any join RTEs, replace join alias variables with
+        * base-relation variables. We must do this before sublink processing,
+        * else sublinks expanded out from join aliases wouldn't get processed.
+        */
+       if (parse->hasJoinRTEs)
+               expr = flatten_join_alias_vars(expr, parse->rtable);
+
        /*
         * Simplify constant expressions.
         *
@@ -783,15 +777,6 @@ preprocess_expression(Query *parse, Node *expr, int kind)
        if (PlannerQueryLevel > 1)
                expr = SS_replace_correlation_vars(expr);
 
-       /*
-        * If the query has any join RTEs, try to replace join alias variables
-        * with base-relation variables, to allow quals to be pushed down. We
-        * must do this after sublink processing, since it does not recurse
-        * into sublinks.
-        */
-       if (parse->hasJoinRTEs)
-               expr = flatten_join_alias_vars(expr, parse->rtable, false);
-
        return expr;
 }
 
index 1b5c72e163c43e78d757944ab015a899aa514277..1c9f8a27cff9044b7eac8887ecd81f967f5626e9 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.88 2003/01/13 18:10:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.89 2003/01/15 19:35:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -278,8 +278,7 @@ fix_expr_references_walker(Node *node, void *context)
  * Note: this same transformation has already been applied to the quals
  * of the join by createplan.c.  It's a little odd to do it here for the
  * targetlist and there for the quals, but it's easier that way.  (Look
- * at switch_outer() and the handling of nestloop inner indexscans to
- * see why.)
+ * at the handling of nestloop inner indexscans to see why.)
  *
  * Because the quals are reference-adjusted sooner, we cannot do equal()
  * comparisons between qual and tlist var nodes during the time between
@@ -379,8 +378,7 @@ set_uppernode_references(Plan *plan, Index subvarno)
  *        Creates a new set of targetlist entries or join qual clauses by
  *        changing the varno/varattno values of variables in the clauses
  *        to reference target list values from the outer and inner join
- *        relation target lists.  Also, any join alias variables in the
- *        clauses are expanded into references to their component variables.
+ *        relation target lists.
  *
  * This is used in two different scenarios: a normal join clause, where
  * all the Vars in the clause *must* be replaced by OUTER or INNER references;
@@ -428,7 +426,6 @@ join_references_mutator(Node *node,
        {
                Var                *var = (Var *) node;
                Resdom     *resdom;
-               Node       *newnode;
 
                /* First look for the var in the input tlists */
                resdom = tlist_member((Node *) var, context->outer_tlist);
@@ -454,20 +451,6 @@ join_references_mutator(Node *node,
                if (var->varno == context->acceptable_rel)
                        return (Node *) copyObject(var);
 
-               /*
-                * Perhaps it's a join alias that can be resolved to input vars?
-                * We try this last since it's relatively slow.
-                */
-               newnode = flatten_join_alias_vars((Node *) var,
-                                                                                 context->rtable,
-                                                                                 true);
-               if (!equal(newnode, (Node *) var))
-               {
-                       /* Must now resolve the input vars... */
-                       newnode = join_references_mutator(newnode, context);
-                       return newnode;
-               }
-
                /* No referent found for Var */
                elog(ERROR, "join_references: variable not in subplan target lists");
        }
index 5177e210d3d66cfde830e1dd2c7f99507f81bbd8..4af395b860c2b43d0cbca0946d6f6f71f0e2d1d8 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.85 2003/01/12 22:35:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.86 2003/01/15 19:35:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -837,9 +837,7 @@ adjust_inherited_attrs_mutator(Node *node,
        }
 
        /*
-        * We have to process RestrictInfo nodes specially: we do NOT want to
-        * copy the original subclauseindices list, since the new rel may have
-        * different indices.  The list will be rebuilt during later planning.
+        * We have to process RestrictInfo nodes specially.
         */
        if (IsA(node, RestrictInfo))
        {
@@ -849,10 +847,41 @@ adjust_inherited_attrs_mutator(Node *node,
                /* Copy all flat-copiable fields */
                memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
 
+               /* Recursively fix the clause itself */
                newinfo->clause = (Expr *)
                        adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
 
+               /*
+                * We do NOT want to copy the original subclauseindices list, since
+                * the new rel will have different indices.  The list will be rebuilt
+                * when needed during later planning.
+                */
                newinfo->subclauseindices = NIL;
+
+               /*
+                * Adjust left/right relids lists too.
+                */
+               if (intMember(context->old_rt_index, oldinfo->left_relids))
+               {
+                       newinfo->left_relids = listCopy(oldinfo->left_relids);
+                       newinfo->left_relids = lremovei(context->old_rt_index,
+                                                                                       newinfo->left_relids);
+                       newinfo->left_relids = lconsi(context->new_rt_index,
+                                                                                 newinfo->left_relids);
+               }
+               else
+                       newinfo->left_relids = oldinfo->left_relids;
+               if (intMember(context->old_rt_index, oldinfo->right_relids))
+               {
+                       newinfo->right_relids = listCopy(oldinfo->right_relids);
+                       newinfo->right_relids = lremovei(context->old_rt_index,
+                                                                                        newinfo->right_relids);
+                       newinfo->right_relids = lconsi(context->new_rt_index,
+                                                                                  newinfo->right_relids);
+               }
+               else
+                       newinfo->right_relids = oldinfo->right_relids;
+
                newinfo->eval_cost.startup = -1; /* reset these too */
                newinfo->this_selec = -1;
                newinfo->left_pathkey = NIL;    /* and these */
@@ -869,6 +898,7 @@ adjust_inherited_attrs_mutator(Node *node,
         * NOTE: we do not need to recurse into sublinks, because they should
         * already have been converted to subplans before we see them.
         */
+       Assert(!IsA(node, SubLink));
 
        /*
         * BUT: although we don't need to recurse into subplans, we do need to
index e38fc46821d5eab93fdf18f5fe8eede2e77b1d75..233a8cb8947d45dd0384c756703f89d75942f72d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.121 2003/01/10 21:08:13 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.122 2003/01/15 19:35:44 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -112,17 +112,13 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
  *
  * Returns the left operand of a clause of the form (op expr expr)
  *             or (op expr)
- *
- * NB: for historical reasons, the result is declared Var *, even
- * though many callers can cope with results that are not Vars.
- * The result really ought to be declared Expr * or Node *.
  */
-Var *
+Node *
 get_leftop(Expr *clause)
 {
        OpExpr *expr = (OpExpr *) clause;
 
-       if (expr->args != NULL)
+       if (expr->args != NIL)
                return lfirst(expr->args);
        else
                return NULL;
@@ -134,12 +130,12 @@ get_leftop(Expr *clause)
  * Returns the right operand in a clause of the form (op expr expr).
  * NB: result will be NULL if applied to a unary op clause.
  */
-Var *
+Node *
 get_rightop(Expr *clause)
 {
        OpExpr *expr = (OpExpr *) clause;
 
-       if (expr->args != NULL && lnext(expr->args) != NULL)
+       if (expr->args != NIL && lnext(expr->args) != NIL)
                return lfirst(lnext(expr->args));
        else
                return NULL;
index 296d211ad12166e58dc3f8d98a13e1473c837cd7..87207f617cc508eb2a46bf3365d2dc8a36d714ae 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.42 2003/01/12 22:35:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.43 2003/01/15 19:35:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,9 +110,9 @@ build_other_rel(Query *root, int relid)
        /* No existing RelOptInfo for this other rel, so make a new one */
        rel = make_base_rel(root, relid);
 
-       /* if it's not a join rel, must be a child rel */
-       if (rel->reloptkind == RELOPT_BASEREL)
-               rel->reloptkind = RELOPT_OTHER_CHILD_REL;
+       /* presently, must be an inheritance child rel */
+       Assert(rel->reloptkind == RELOPT_BASEREL);
+       rel->reloptkind = RELOPT_OTHER_CHILD_REL;
 
        /* and add it to the list */
        root->other_rel_list = lcons(rel, root->other_rel_list);
@@ -146,8 +146,6 @@ make_base_rel(Query *root, int relid)
        rel->pages = 0;
        rel->tuples = 0;
        rel->subplan = NULL;
-       rel->joinrti = 0;
-       rel->joinrteids = NIL;
        rel->baserestrictinfo = NIL;
        rel->baserestrictcost.startup = 0;
        rel->baserestrictcost.per_tuple = 0;
@@ -174,10 +172,6 @@ make_base_rel(Query *root, int relid)
                case RTE_FUNCTION:
                        /* Subquery or function --- nothing to do here */
                        break;
-               case RTE_JOIN:
-                       /* Join --- must be an otherrel */
-                       rel->reloptkind = RELOPT_OTHER_JOIN_REL;
-                       break;
                default:
                        elog(ERROR, "make_base_rel: unsupported RTE kind %d",
                                 (int) rte->rtekind);
@@ -220,47 +214,6 @@ find_base_rel(Query *root, int relid)
        return NULL;                            /* keep compiler quiet */
 }
 
-/*
- * find_other_rel
- *       Find an otherrel entry, if one exists for the given relid.
- *       Return NULL if no entry.
- */
-RelOptInfo *
-find_other_rel(Query *root, int relid)
-{
-       List       *rels;
-
-       foreach(rels, root->other_rel_list)
-       {
-               RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
-
-               if (lfirsti(rel->relids) == relid)
-                       return rel;
-       }
-       return NULL;
-}
-
-/*
- * find_other_rel_for_join
- *       Look for an otherrel for a join RTE matching the given baserel set.
- *       Return NULL if no entry.
- */
-RelOptInfo *
-find_other_rel_for_join(Query *root, List *relids)
-{
-       List       *rels;
-
-       foreach(rels, root->other_rel_list)
-       {
-               RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
-
-               if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
-                       && sameseti(relids, rel->outerjoinset))
-                       return rel;
-       }
-       return NULL;
-}
-
 /*
  * find_join_rel
  *       Returns relation entry corresponding to 'relids' (a list of RT indexes),
@@ -310,7 +263,6 @@ build_join_rel(Query *root,
 {
        List       *joinrelids;
        RelOptInfo *joinrel;
-       RelOptInfo *joinrterel;
        List       *restrictlist;
        List       *new_outer_tlist;
        List       *new_inner_tlist;
@@ -360,9 +312,6 @@ build_join_rel(Query *root,
        joinrel->pages = 0;
        joinrel->tuples = 0;
        joinrel->subplan = NULL;
-       joinrel->joinrti = 0;
-       joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
-                                                               inner_rel->joinrteids);
        joinrel->baserestrictinfo = NIL;
        joinrel->baserestrictcost.startup = 0;
        joinrel->baserestrictcost.per_tuple = 0;
@@ -371,15 +320,6 @@ build_join_rel(Query *root,
        joinrel->index_outer_relids = NIL;
        joinrel->index_inner_paths = NIL;
 
-       /* Is there a join RTE matching this join? */
-       joinrterel = find_other_rel_for_join(root, joinrelids);
-       if (joinrterel)
-       {
-               /* Yes, remember its RT index */
-               joinrel->joinrti = lfirsti(joinrterel->relids);
-               joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids);
-       }
-
        /*
         * Create a new tlist by removing irrelevant elements from both tlists
         * of the outer and inner join relations and then merging the results
@@ -400,23 +340,6 @@ build_join_rel(Query *root,
                                                                         length(new_outer_tlist) + 1);
        joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
 
-       /*
-        * If there are any alias variables attached to the matching join RTE,
-        * attach them to the tlist too, so that they will be evaluated for
-        * use at higher plan levels.
-        */
-       if (joinrterel)
-       {
-               List       *jrtetl;
-
-               foreach(jrtetl, joinrterel->targetlist)
-               {
-                       TargetEntry *jrtete = lfirst(jrtetl);
-
-                       add_var_to_tlist(joinrel, (Var *) jrtete->expr);
-               }
-       }
-
        /*
         * Construct restrict and join clause lists for the new joinrel. (The
         * caller might or might not need the restrictlist, but I need it
index 92ff0cd5b4c003b540af9d0c16e0c9d00489be67..11267428d7ae8a9936091e6e48d2e441c04e4686 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.43 2003/01/10 21:08:13 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.44 2003/01/15 19:35:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parsetree.h"
 
 
+/* macros borrowed from expression_tree_mutator */
+
+#define FLATCOPY(newnode, node, nodetype)  \
+       ( (newnode) = makeNode(nodetype), \
+         memcpy((newnode), (node), sizeof(nodetype)) )
+
+#define MUTATE(newfield, oldfield, fieldtype, mutator, context)  \
+               ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
+
+
 typedef struct
 {
        List       *varlist;
@@ -42,7 +52,7 @@ typedef struct
 typedef struct
 {
        List       *rtable;
-       bool            force;
+       int                     sublevels_up;
 } flatten_join_alias_vars_context;
 
 static bool pull_varnos_walker(Node *node,
@@ -314,26 +324,16 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
  *       relation variables instead.  This allows quals involving such vars to be
  *       pushed down.
  *
- * If force is TRUE then we will reduce all JOIN alias Vars to non-alias Vars
- * or expressions thereof (there may be COALESCE and/or type conversions
- * involved).  If force is FALSE we will not expand a Var to a non-Var
- * expression. This is a hack to avoid confusing mergejoin planning, which
- * currently cannot cope with non-Var join items --- we leave the join vars
- * as Vars till after planning is done, then expand them during setrefs.c.
- *
- * Upper-level vars (with varlevelsup > 0) are ignored; normally there
- * should not be any by the time this routine is called.
- *
- * Does not examine subqueries, therefore must only be used after reduction
- * of sublinks to subplans!
+ * NOTE: this is used on not-yet-planned expressions.  We do not expect it
+ * to be applied directly to a Query node.
  */
 Node *
-flatten_join_alias_vars(Node *node, List *rtable, bool force)
+flatten_join_alias_vars(Node *node, List *rtable)
 {
        flatten_join_alias_vars_context context;
 
        context.rtable = rtable;
-       context.force = force;
+       context.sublevels_up = 0;
 
        return flatten_join_alias_vars_mutator(node, &context);
 }
@@ -350,21 +350,31 @@ flatten_join_alias_vars_mutator(Node *node,
                RangeTblEntry *rte;
                Node       *newvar;
 
-               if (var->varlevelsup != 0)
+               if (var->varlevelsup != context->sublevels_up)
                        return node;            /* no need to copy, really */
                rte = rt_fetch(var->varno, context->rtable);
                if (rte->rtekind != RTE_JOIN)
                        return node;
                Assert(var->varattno > 0);
                newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
-               if (IsA(newvar, Var) ||context->force)
-               {
-                       /* expand it; recurse in case join input is itself a join */
-                       return flatten_join_alias_vars_mutator(newvar, context);
-               }
-               /* we don't want to force expansion of this alias Var */
-               return node;
+               /* expand it; recurse in case join input is itself a join */
+               return flatten_join_alias_vars_mutator(newvar, context);
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+               Query      *query = (Query *) node;
+               Query      *newnode;
+
+               FLATCOPY(newnode, query, Query);
+               context->sublevels_up++;
+               query_tree_mutator(newnode, flatten_join_alias_vars_mutator,
+                                                  (void *) context, QTW_IGNORE_JOINALIASES);
+               context->sublevels_up--;
+               return (Node *) newnode;
        }
+       /* Already-planned tree not supported */
+       Assert(!is_subplan(node));
        return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
                                                                   (void *) context);
 }
index 977cbe0a5e48a7c4d43f2e401e1f3c3583c0d281..fe6f38eee8509e5f7a7cb9723dba6364570f3353 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.125 2003/01/12 22:35:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.126 2003/01/15 19:35:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1754,8 +1754,8 @@ mergejoinscansel(Query *root, Node *clause,
        if (!is_opclause(clause))
                return;                                 /* shouldn't happen */
        opno = ((OpExpr *) clause)->opno;
-       left = get_leftop((Expr *) clause);
-       right = get_rightop((Expr *) clause);
+       left = (Var *) get_leftop((Expr *) clause);
+       right = (Var *) get_rightop((Expr *) clause);
        if (!right)
                return;                                 /* shouldn't happen */
 
@@ -1766,8 +1766,6 @@ mergejoinscansel(Query *root, Node *clause,
 
        /* Verify mergejoinability and get left and right "<" operators */
        if (!op_mergejoinable(opno,
-                                                 left->vartype,
-                                                 right->vartype,
                                                  &lsortop,
                                                  &rsortop))
                return;                                 /* shouldn't happen */
@@ -1892,17 +1890,6 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows)
                List       *varshere;
 
                varshere = pull_var_clause(groupexpr, false);
-               /*
-                * Replace any JOIN alias Vars with the underlying Vars.  (This
-                * is not really right for FULL JOIN ...)
-                */
-               if (root->hasJoinRTEs)
-               {
-                       varshere = (List *) flatten_join_alias_vars((Node *) varshere,
-                                                                                                               root->rtable,
-                                                                                                               true);
-                       varshere = pull_var_clause((Node *) varshere, false);
-               }
                /*
                 * If we find any variable-free GROUP BY item, then either it is
                 * a constant (and we can ignore it) or it contains a volatile
@@ -1963,7 +1950,7 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows)
                        l2 = lnext(l2);
 
                        if (var->varno != varinfo->var->varno &&
-                               vars_known_equal(root, var, varinfo->var))
+                               exprs_known_equal(root, (Node *) var, (Node *) varinfo->var))
                        {
                                /* Found a match */
                                if (varinfo->ndistinct <= ndistinct)
index 56dabcd754284f633f72437bcbca4889d4c0a8e7..a6c838974c0d30562d5853c95f9edfdf414a24d7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.88 2002/12/05 04:04:44 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.89 2003/01/15 19:35:44 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
@@ -315,11 +315,11 @@ get_opname(Oid opno)
 /*
  * op_mergejoinable
  *
- *             Returns the left and right sort operators and types corresponding to a
- *             mergejoinable operator, or nil if the operator is not mergejoinable.
+ *             Returns the left and right sort operators corresponding to a
+ *             mergejoinable operator, or false if the operator is not mergejoinable.
  */
 bool
-op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp)
+op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
 {
        HeapTuple       tp;
        bool            result = false;
@@ -332,9 +332,7 @@ op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp)
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
 
                if (optup->oprlsortop &&
-                       optup->oprrsortop &&
-                       optup->oprleft == ltype &&
-                       optup->oprright == rtype)
+                       optup->oprrsortop)
                {
                        *leftOp = optup->oprlsortop;
                        *rightOp = optup->oprrsortop;
@@ -391,14 +389,13 @@ op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
 /*
  * op_hashjoinable
  *
- * Returns the hash operator corresponding to a hashjoinable operator,
- * or InvalidOid if the operator is not hashjoinable.
+ * Returns true if the operator is hashjoinable.
  */
-Oid
-op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
+bool
+op_hashjoinable(Oid opno)
 {
        HeapTuple       tp;
-       Oid                     result = InvalidOid;
+       bool            result = false;
 
        tp = SearchSysCache(OPEROID,
                                                ObjectIdGetDatum(opno),
@@ -407,10 +404,7 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
 
-               if (optup->oprcanhash &&
-                       optup->oprleft == ltype &&
-                       optup->oprright == rtype)
-                       result = opno;
+               result = optup->oprcanhash;
                ReleaseSysCache(tp);
        }
        return result;
index d3fb2231f1e99be2a4201207106158a6767d9555..a21debe02f90c81d1a9f9f87e50f52d16658622f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.75 2003/01/12 22:35:29 tgl Exp $
+ * $Id: relation.h,v 1.76 2003/01/15 19:35:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,7 +50,7 @@ typedef struct QualCost
  *             Per-relation information for planning/optimization
  *
  * For planning purposes, a "base rel" is either a plain relation (a table)
- * or the output of a sub-SELECT that appears in the range table.
+ * or the output of a sub-SELECT or function that appears in the range table.
  * In either case it is uniquely identified by an RT index.  A "joinrel"
  * is the joining of two or more base rels.  A joinrel is identified by
  * the set of RT indexes for its component baserels.  We create RelOptInfo
@@ -63,14 +63,10 @@ typedef struct QualCost
  *
  * We also have "other rels", which are like base rels in that they refer to
  * single RT indexes; but they are not part of the join tree, and are stored
- * in other_rel_list not base_rel_list.  An otherrel is created for each
- * join RTE as an aid in processing Vars that refer to the join's outputs,
- * but it serves no other purpose in planning. It is important not to
- * confuse this otherrel with the joinrel that represents the matching set
- * of base relations.
- *
- * A second category of otherrels are those made for child relations of an
- * inheritance scan (SELECT FROM foo*).  The parent table's RTE and
+ * in other_rel_list not base_rel_list.
+ *
+ * Currently the only kind of otherrels are those made for child relations
+ * of an inheritance scan (SELECT FROM foo*).  The parent table's RTE and
  * corresponding baserel represent the whole result of the inheritance scan.
  * The planner creates separate RTEs and associated RelOptInfos for each child
  * table (including the parent table, in its capacity as a member of the
@@ -80,6 +76,10 @@ typedef struct QualCost
  * the inheritance set; then the parent baserel is given an Append plan
  * comprising the best plans for the individual child tables.
  *
+ * At one time we also made otherrels to represent join RTEs, for use in
+ * handling join alias Vars.  Currently this is not needed because all join
+ * alias Vars are expanded to non-aliased form during preprocess_expression.
+ *
  * Parts of this data structure are specific to various scan and join
  * mechanisms. It didn't seem worth creating new node types for them.
  *
@@ -114,15 +114,7 @@ typedef struct QualCost
  *             set_base_rel_pathlist processes the object.
  *
  *             For otherrels that are inheritance children, these fields are filled
- *             in just as for a baserel.  In otherrels for join RTEs, these fields
- *             are empty --- the only useful field of a join otherrel is its
- *             outerjoinset.
- *
- * If the relation is a join relation it will have these fields set:
- *
- *             joinrti - RT index of corresponding JOIN RTE, if any; 0 if none
- *             joinrteids - List of RT indexes of JOIN RTEs included in this join
- *                                      (including joinrti)
+ *             in just as for a baserel.
  *
  * The presence of the remaining fields depends on the restrictions
  * and joins that the relation participates in:
@@ -135,8 +127,7 @@ typedef struct QualCost
  *             outerjoinset - For a base rel: if the rel appears within the nullable
  *                                     side of an outer join, the list of all relids
  *                                     participating in the highest such outer join; else NIL.
- *                                     For a join otherrel: the list of all baserel relids
- *                                     syntactically within the join.  Otherwise, unused.
+ *                                     Otherwise, unused.
  *             joininfo  - List of JoinInfo nodes, containing info about each join
  *                                     clause in which this relation participates
  *             index_outer_relids - only used for base rels; list of outer relids
@@ -170,7 +161,6 @@ typedef enum RelOptKind
 {
        RELOPT_BASEREL,
        RELOPT_JOINREL,
-       RELOPT_OTHER_JOIN_REL,
        RELOPT_OTHER_CHILD_REL
 } RelOptKind;
 
@@ -202,10 +192,6 @@ typedef struct RelOptInfo
        double          tuples;
        struct Plan *subplan;           /* if subquery */
 
-       /* information about a join rel (not set for base rels!) */
-       Index           joinrti;
-       List       *joinrteids;
-
        /* used by various scans and joins: */
        List       *baserestrictinfo;           /* RestrictInfo structures (if
                                                                                 * base rel) */
@@ -275,15 +261,6 @@ typedef struct IndexOptInfo
 } IndexOptInfo;
 
 
-/*
- * A Var is considered to belong to a relation if it's either from one
- * of the actual base rels making up the relation, or it's a join alias
- * var that is included in the relation.
- */
-#define VARISRELMEMBER(varno,rel) (intMember((varno), (rel)->relids) || \
-                                                                  intMember((varno), (rel)->joinrteids))
-
-
 /*
  * PathKeys
  *
@@ -583,6 +560,15 @@ typedef struct RestrictInfo
        QualCost        eval_cost;              /* eval cost of clause; -1 if not yet set */
        Selectivity this_selec;         /* selectivity; -1 if not yet set */
 
+       /*
+        * If the clause looks useful for joining --- that is, it is a binary
+        * opclause with nonoverlapping sets of relids referenced in the left
+        * and right sides --- then these two fields are set to lists of the
+        * referenced relids.  Otherwise they are both NIL.
+        */
+       List       *left_relids;        /* relids in left side of join clause */
+       List       *right_relids;       /* relids in right side of join clause */
+
        /* valid if clause is mergejoinable, else InvalidOid: */
        Oid                     mergejoinoperator;              /* copy of clause operator */
        Oid                     left_sortop;    /* leftside sortop needed for mergejoin */
index f280e420d9b4c85a44123aad48df1398b503df9b..4ed022b15e766c4de1006cd32b7f43b639bbb47a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.58 2002/12/14 00:17:59 tgl Exp $
+ * $Id: clauses.h,v 1.59 2003/01/15 19:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,8 +25,8 @@
 
 extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
                                                   Expr *leftop, Expr *rightop);
-extern Var *get_leftop(Expr *clause);
-extern Var *get_rightop(Expr *clause);
+extern Node *get_leftop(Expr *clause);
+extern Node *get_rightop(Expr *clause);
 
 extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
                                                         CoercionForm funcformat, List *funcargs);
index 65abeb0cf61694abacf6fbbd778341b455b86b79..77ed27e7e55b23de9a2acc3c87567af330b03edd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.46 2002/11/30 05:21:03 tgl Exp $
+ * $Id: pathnode.h,v 1.47 2003/01/15 19:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -74,8 +74,6 @@ extern HashPath *create_hashjoin_path(Query *root,
 extern void build_base_rel(Query *root, int relid);
 extern RelOptInfo *build_other_rel(Query *root, int relid);
 extern RelOptInfo *find_base_rel(Query *root, int relid);
-extern RelOptInfo *find_other_rel(Query *root, int relid);
-extern RelOptInfo *find_other_rel_for_join(Query *root, List *relids);
 extern RelOptInfo *build_join_rel(Query *root,
                           RelOptInfo *outer_rel,
                           RelOptInfo *inner_rel,
index 134adad9b604c99f35225bf6042a7cebd9036f78..f2604527001e039ffb1048e293b2fde2d3bfa876 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.64 2002/12/12 15:49:41 tgl Exp $
+ * $Id: planmain.h,v 1.65 2003/01/15 19:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,8 +32,6 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
 extern Sort *make_sort(Query *root, List *tlist,
                  Plan *lefttree, int keycount);
-extern Sort *make_sort_from_pathkeys(Query *root, List *tlist,
-                                               Plan *lefttree, List *pathkeys);
 extern Agg *make_agg(Query *root, List *tlist, List *qual,
                                         AggStrategy aggstrategy,
                                         int numGroupCols, AttrNumber *grpColIdx,
@@ -54,12 +52,12 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 /*
  * prototypes for plan/initsplan.c
  */
-extern List *add_base_rels_to_query(Query *root, Node *jtnode);
+extern void add_base_rels_to_query(Query *root, Node *jtnode);
 extern void build_base_rel_tlists(Query *root, List *tlist);
 extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
 extern void process_implied_equality(Query *root, Node *item1, Node *item2,
                                                 Oid sortop1, Oid sortop2);
-extern bool vars_known_equal(Query *root, Var *var1, Var *var2);
+extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
 
 /*
  * prototypes for plan/setrefs.c
index 102e928a544b87e99595f78d010d9efeaebb4c62..07b8b311d076ae4f43d79cec6ea28bd2d1d44d92 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: var.h,v 1.23 2002/12/12 20:35:16 tgl Exp $
+ * $Id: var.h,v 1.24 2003/01/15 19:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,6 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
 extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
 extern bool contain_var_clause(Node *node);
 extern List *pull_var_clause(Node *node, bool includeUpperVars);
-extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);
+extern Node *flatten_join_alias_vars(Node *node, List *rtable);
 
 #endif   /* VAR_H */
index a0d3574b246e61266efa4bfab102671859f4fe61..8200730f6a25676031c670d7710f2a546022bf5d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.65 2002/12/01 21:05:14 tgl Exp $
+ * $Id: lsyscache.h,v 1.66 2003/01/15 19:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,11 +26,10 @@ extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
 extern bool opclass_is_btree(Oid opclass);
 extern RegProcedure get_opcode(Oid opno);
 extern char *get_opname(Oid opno);
-extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,
-                                Oid *leftOp, Oid *rightOp);
+extern bool op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp);
 extern void op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
                                          RegProcedure *ltproc, RegProcedure *gtproc);
-extern Oid     op_hashjoinable(Oid opno, Oid ltype, Oid rtype);
+extern bool op_hashjoinable(Oid opno);
 extern bool op_strict(Oid opno);
 extern char op_volatile(Oid opno);
 extern Oid     get_commutator(Oid opno);
index 3decbdb7991dfa01557f019eb5cbb413d80adc46..7ef807a95db1375103f19a0f1d55ad210dc8c48c 100644 (file)
@@ -498,6 +498,17 @@ WHERE p1.oprcode = p2.oid AND
 -----+---------+-----+---------
 (0 rows)
 
+-- If the operator is mergejoinable or hashjoinable, its underlying function
+-- should not be volatile.
+SELECT p1.oid, p1.oprname, p2.oid, p2.proname
+FROM pg_operator AS p1, pg_proc AS p2
+WHERE p1.oprcode = p2.oid AND
+    (p1.oprlsortop != 0 OR p1.oprcanhash) AND
+    p2.provolatile = 'v';
+ oid | oprname | oid | proname 
+-----+---------+-----+---------
+(0 rows)
+
 -- If oprrest is set, the operator must return boolean,
 -- and it must link to a proc with the right signature
 -- to be a restriction selectivity estimator.
@@ -583,7 +594,8 @@ WHERE a.aggfnoid = p.oid AND
      a.aggtranstype != p2.prorettype OR
      a.aggtranstype != p2.proargtypes[0] OR
      NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)));
+          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
+ORDER BY 1;
  aggfnoid | proname | oid |   proname   
 ----------+---------+-----+-------------
      2121 | max     | 768 | int4larger
index 46c153e5d14042ed1722548f7f57c68939815521..8f943188d24e87cc3e3eab4e9228e66e5beaddc0 100644 (file)
@@ -107,7 +107,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
     (p1.typelem != 0 AND p1.typlen < 0) AND NOT
-    (p2.prorettype = p1.oid AND NOT p2.proretset);
+    (p2.prorettype = p1.oid AND NOT p2.proretset)
+ORDER BY 1;
  oid  |  typname  | oid |  proname  
 ------+-----------+-----+-----------
    32 | SET       | 109 | unknownin
@@ -132,7 +133,8 @@ FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
     ((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR
      (p2.oid = 'array_out'::regproc AND
-      p1.typelem != 0 AND p1.typlen = -1));
+      p1.typelem != 0 AND p1.typlen = -1))
+ORDER BY 1;
  oid  |  typname  | oid |  proname   
 ------+-----------+-----+------------
    32 | SET       | 110 | unknownout
index faacc83850e8c7f673e054387d3ea811c2abab28..650073cccc1dd128cd6930dd50d4b51261ad9a2d 100644 (file)
@@ -416,6 +416,15 @@ WHERE p1.oprcode = p2.oid AND
      (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
      p1.oprright != 0);
 
+-- If the operator is mergejoinable or hashjoinable, its underlying function
+-- should not be volatile.
+
+SELECT p1.oid, p1.oprname, p2.oid, p2.proname
+FROM pg_operator AS p1, pg_proc AS p2
+WHERE p1.oprcode = p2.oid AND
+    (p1.oprlsortop != 0 OR p1.oprcanhash) AND
+    p2.provolatile = 'v';
+
 -- If oprrest is set, the operator must return boolean,
 -- and it must link to a proc with the right signature
 -- to be a restriction selectivity estimator.
@@ -490,7 +499,8 @@ WHERE a.aggfnoid = p.oid AND
      a.aggtranstype != p2.prorettype OR
      a.aggtranstype != p2.proargtypes[0] OR
      NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)));
+          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
+ORDER BY 1;
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
 -- FIXME: what about binary-compatible types?
index 0cc4748fa89b95b6363a493e00880529ee80019d..f0ffa5984dc374abb0e851bc0e2146950888b353 100644 (file)
@@ -90,7 +90,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
     (p1.typelem != 0 AND p1.typlen < 0) AND NOT
-    (p2.prorettype = p1.oid AND NOT p2.proretset);
+    (p2.prorettype = p1.oid AND NOT p2.proretset)
+ORDER BY 1;
 
 -- Varlena array types will point to array_in
 SELECT p1.oid, p1.typname, p2.oid, p2.proname
@@ -108,7 +109,8 @@ FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
     ((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR
      (p2.oid = 'array_out'::regproc AND
-      p1.typelem != 0 AND p1.typlen = -1));
+      p1.typelem != 0 AND p1.typlen = -1))
+ORDER BY 1;
 
 SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2